install-pear-nozlib.phar   [plain text]


<?php
error_reporting(1803);
if (function_exists('mb_internal_encoding')) {
    mb_internal_encoding('ASCII');
}
if (!class_exists('PHP_Archive')) {/**
 * PHP_Archive Class (implements .phar)
 *
 * @package PHP_Archive
 * @category PHP
 */
/**
 * PHP_Archive Class (implements .phar)
 *
 * PHAR files a singular archive from which an entire application can run.
 * To use it, simply package it using {@see PHP_Archive_Creator} and use phar://
 * URIs to your includes. i.e. require_once 'phar://config.php' will include config.php
 * from the root of the PHAR file.
 *
 * Gz code borrowed from the excellent File_Archive package by Vincent Lascaux.
 *
 * @copyright Copyright David Shafik and Synaptic Media 2004. All rights reserved.
 * @author Davey Shafik <davey@synapticmedia.net>
 * @author Greg Beaver <cellog@php.net>
 * @link http://www.synapticmedia.net Synaptic Media
 * @version Id: Archive.php,v 1.52 2007/09/01 20:28:14 cellog Exp $
 * @package PHP_Archive
 * @category PHP
 */
 
class PHP_Archive
{
    const GZ = 0x00001000;
    const BZ2 = 0x00002000;
    const SIG = 0x00010000;
    const SHA1 = 0x0002;
    const MD5 = 0x0001;
    /**
     * Whether this archive is compressed with zlib
     *
     * @var bool
     */
    private $_compressed;
    /**
     * @var string Real path to the .phar archive
     */
    private $_archiveName = null;
    /**
     * Current file name in the phar
     * @var string
     */
    protected $currentFilename = null;
    /**
     * Length of current file in the phar
     * @var string
     */
    protected $internalFileLength = null;
    /**
     * Current file statistics (size, creation date, etc.)
     * @var string
     */
    protected $currentStat = null;
    /**
     * @var resource|null Pointer to open .phar
     */
    protected $fp = null;
    /**
     * @var int Current Position of the pointer
     */
    protected $position = 0;

    /**
     * Map actual realpath of phars to meta-data about the phar
     *
     * Data is indexed by the alias that is used by internal files.  In other
     * words, if a file is included via:
     * <code>
     * require_once 'phar://PEAR.phar/PEAR/Installer.php';
     * </code>
     * then the alias is "PEAR.phar"
     * 
     * Information stored is a boolean indicating whether this .phar is compressed
     * with zlib, another for bzip2, phar-specific meta-data, and
     * the precise offset of internal files
     * within the .phar, used with the {@link $_manifest} to load actual file contents
     * @var array
     */
    private static $_pharMapping = array();
    /**
     * Map real file paths to alias used
     *
     * @var array
     */
    private static $_pharFiles = array();
    /**
     * File listing for the .phar
     * 
     * The manifest is indexed per phar.
     * 
     * Files within the .phar are indexed by their relative path within the
     * .phar.  Each file has this information in its internal array
     *
     * - 0 = uncompressed file size
     * - 1 = timestamp of when file was added to phar
     * - 2 = offset of file within phar relative to internal file's start
     * - 3 = compressed file size (actual size in the phar)
     * @var array
     */
    private static $_manifest = array();
    /**
     * Absolute offset of internal files within the .phar, indexed by absolute
     * path to the .phar
     *
     * @var array
     */
    private static $_fileStart = array();
    /**
     * file name of the phar
     *
     * @var string
     */
    private $_basename;


    /**
     * Default MIME types used for the web front controller
     *
     * @var array
     */
    public static $defaultmimes = array(
            'aif' => 'audio/x-aiff',
            'aiff' => 'audio/x-aiff',
            'arc' => 'application/octet-stream',
            'arj' => 'application/octet-stream',
            'art' => 'image/x-jg',
            'asf' => 'video/x-ms-asf',
            'asx' => 'video/x-ms-asf',
            'avi' => 'video/avi',
            'bin' => 'application/octet-stream',
            'bm' => 'image/bmp',
            'bmp' => 'image/bmp',
            'bz2' => 'application/x-bzip2',
            'css' => 'text/css',
            'doc' => 'application/msword',
            'dot' => 'application/msword',
            'dv' => 'video/x-dv',
            'dvi' => 'application/x-dvi',
            'eps' => 'application/postscript',
            'exe' => 'application/octet-stream',
            'gif' => 'image/gif',
            'gz' => 'application/x-gzip',
            'gzip' => 'application/x-gzip',
            'htm' => 'text/html',
            'html' => 'text/html',
            'ico' => 'image/x-icon',
            'jpe' => 'image/jpeg',
            'jpg' => 'image/jpeg',
            'jpeg' => 'image/jpeg',
            'js' => 'application/x-javascript',
            'log' => 'text/plain',
            'mid' => 'audio/x-midi',
            'mov' => 'video/quicktime',
            'mp2' => 'audio/mpeg',
            'mp3' => 'audio/mpeg3',
            'mpg' => 'audio/mpeg',
            'pdf' => 'aplication/pdf',
            'png' => 'image/png',
            'rtf' => 'application/rtf',
            'tif' => 'image/tiff',
            'tiff' => 'image/tiff',
            'txt' => 'text/plain',
            'xml' => 'text/xml',
        );

    public static $defaultphp = array(
        'php' => true
        );

    public static $defaultphps = array(
        'phps' => true
        );

    public static $deny = array('/.+\.inc$/');

    public static function viewSource($archive, $file)
    {
        // security, idea borrowed from PHK
        if (!file_exists($archive . '.introspect')) {
            header("HTTP/1.0 404 Not Found");
            return false;
        }
        if (self::_fileExists($archive, $_GET['viewsource'])) {
            $source = highlight_file('phar://install-pear-nozlib.phar/' .
                $_GET['viewsource'], true);
            header('Content-Type: text/html');
            header('Content-Length: ' . strlen($source));
            echo '<html><head><title>Source of ',
                htmlspecialchars($_GET['viewsource']), '</title></head>';
            echo '<body><h1>Source of ',
                htmlspecialchars($_GET['viewsource']), '</h1>';
            if (isset($_GET['introspect'])) {
                echo '<a href="', htmlspecialchars($_SERVER['PHP_SELF']),
                    '?introspect=', urlencode(htmlspecialchars($_GET['introspect'])),
                    '">Return to ', htmlspecialchars($_GET['introspect']), '</a><br />';
            }
            echo $source;
            return false;
        } else {
            header("HTTP/1.0 404 Not Found");
            return false;
        }
        
    }

    public static function introspect($archive, $dir)
    {
        // security, idea borrowed from PHK
        if (!file_exists($archive . '.introspect')) {
            header("HTTP/1.0 404 Not Found");
            return false;
        }
        if (!$dir) {
            $dir = '/';
        }
        $dir = self::processFile($dir);
        if ($dir[0] != '/') {
            $dir = '/' . $dir;
        }
        try {
            $self = htmlspecialchars($_SERVER['PHP_SELF']);
            $iterate = new DirectoryIterator('phar://install-pear-nozlib.phar' . $dir);
            echo '<html><head><title>Introspect ', htmlspecialchars($dir),
                '</title></head><body><h1>Introspect ', htmlspecialchars($dir),
                '</h1><ul>';
            if ($dir != '/') {
                echo '<li><a href="', $self, '?introspect=',
                    htmlspecialchars(dirname($dir)), '">..</a></li>';
            }
            foreach ($iterate as $entry) {
                if ($entry->isDot()) continue;
                $name = self::processFile($entry->getPathname());
                $name = str_replace('phar://install-pear-nozlib.phar/', '', $name);
                if ($entry->isDir()) {
                    echo '<li><a href="', $self, '?introspect=',
                        urlencode(htmlspecialchars($name)),
                        '">',
                        htmlspecialchars($entry->getFilename()), '/</a> [directory]</li>';
                } else {
                    echo '<li><a href="', $self, '?introspect=',
                        urlencode(htmlspecialchars($dir)), '&viewsource=',
                        urlencode(htmlspecialchars($name)),
                        '">',
                        htmlspecialchars($entry->getFilename()), '</a></li>';
                }
            }
            return false;
        } catch (Exception $e) {
            echo '<html><head><title>Directory not found: ',
                htmlspecialchars($dir), '</title></head>',
                '<body><h1>Directory not found: ', htmlspecialchars($dir), '</h1>',
                '<p>Try <a href="', htmlspecialchars($_SERVER['PHP_SELF']), '?introspect=/">',
                'This link</a></p></body></html>';
            return false;
        }
    }

    public static function webFrontController($initfile)
    {
        if (isset($_SERVER) && isset($_SERVER['REQUEST_URI'])) {
            $uri = parse_url($_SERVER['REQUEST_URI']);
            $archive = realpath($_SERVER['SCRIPT_FILENAME']);
            $subpath = str_replace('/' . basename($archive), '', $uri['path']);
            if (!$subpath || $subpath == '/') {
                if (isset($_GET['viewsource'])) {
                    return self::viewSource($archive, $_GET['viewsource']);
                }
                if (isset($_GET['introspect'])) {
                    return self::introspect($archive, $_GET['introspect']);
                }
                $subpath = '/' . $initfile;
            }
            if (!self::_fileExists($archive, substr($subpath, 1))) {
                header("HTTP/1.0 404 Not Found");
                return false;
            }
            foreach (self::$deny as $pattern) {
                if (preg_match($pattern, $subpath)) {
                    header("HTTP/1.0 404 Not Found");
                    return false;
                }
            }
            $inf = pathinfo(basename($subpath));
            if (!isset($inf['extension'])) {
                header('Content-Type: text/plain');
                header('Content-Length: ' .
                    self::_filesize($archive, substr($subpath, 1)));
                readfile('phar://install-pear-nozlib.phar' . $subpath);
                return false;
            }
            if (isset(self::$defaultphp[$inf['extension']])) {
                include 'phar://install-pear-nozlib.phar' . $subpath;
                return false;
            }
            if (isset(self::$defaultmimes[$inf['extension']])) {
                header('Content-Type: ' . self::$defaultmimes[$inf['extension']]);
                header('Content-Length: ' .
                    self::_filesize($archive, substr($subpath, 1)));
                readfile('phar://install-pear-nozlib.phar' . $subpath);
                return false;
            }
            if (isset(self::$defaultphps[$inf['extension']])) {
                header('Content-Type: text/html');
                $c = highlight_file('phar://install-pear-nozlib.phar' . $subpath, true);
                header('Content-Length: ' . strlen($c));
                echo $c;
                return false;
            }
            header('Content-Type: text/plain');
            header('Content-Length: ' .
                    self::_filesize($archive, substr($subpath, 1)));
            readfile('phar://install-pear-nozlib.phar' . $subpath);
        }
    }

    /**
     * Detect end of stub
     *
     * @param string $buffer stub past '__HALT_'.'COMPILER();'
     * @return end of stub, prior to length of manifest.
     */
    private static final function _endOfStubLength($buffer)
    {
        $pos = 0;
        if (!strlen($buffer)) {
            return $pos;
        }
        if (($buffer[0] == ' ' || $buffer[0] == "\n") && @substr($buffer, 1, 2) == '')
        {
            $pos += 3;
            if ($buffer[$pos] == "\r" && $buffer[$pos+1] == "\n") {
                $pos += 2;
            }
            else if ($buffer[$pos] == "\n") {
                $pos += 1;
            }
        }
        return $pos;
    }

    /**
     * Allows loading an external Phar archive without include()ing it
     *
     * @param string $file  phar package to load
     * @param string $alias alias to use
     * @throws Exception
     */
    public static final function loadPhar($file, $alias = NULL)
    {
        $file = realpath($file);
        if ($file) {
            $fp = fopen($file, 'rb');
            $buffer = '';
            while (!feof($fp)) {
                $buffer .= fread($fp, 8192);
                // don't break phars
                if ($pos = strpos($buffer, '__HALT_COMPI' . 'LER();')) {
                    $buffer .= fread($fp, 5);
                    fclose($fp);
                    $pos += 18;
                    $pos += self::_endOfStubLength(substr($buffer, $pos));
                    return self::_mapPhar($file, $pos, $alias);
                }
            }
            fclose($fp);
        }
    }

    /**
     * Map a full real file path to an alias used to refer to the .phar
     *
     * This function can only be called from the initialization of the .phar itself.
     * Any attempt to call from outside the .phar or to re-alias the .phar will fail
     * as a security measure.
     * @param string $alias
     * @param int $dataoffset the value of 42421                   
     */
    public static final function mapPhar($alias = NULL, $dataoffset = NULL)
    {
        try {
            $trace = debug_backtrace();
            $file = $trace[0]['file'];
            // this ensures that this is safe
            if (!in_array($file, get_included_files())) {
                die('SECURITY ERROR: PHP_Archive::mapPhar can only be called from within ' .
                    'the phar that initiates it');
            }
            $file = realpath($file);
            if (!isset($dataoffset)) {
                $dataoffset = constant('__COMPILER_HALT_OFFSET'.'__');
                $fp = fopen($file, 'rb');
                fseek($fp, $dataoffset, SEEK_SET);
                $dataoffset = $dataoffset + self::_endOfStubLength(fread($fp, 5));
                fclose($fp);
            }

            self::_mapPhar($file, $dataoffset);
        } catch (Exception $e) {
            die($e->getMessage());
        }
    }

    /**
     * Sub-function, allows recovery from errors
     *
     * @param unknown_type $file
     * @param unknown_type $dataoffset
     */
    private static function _mapPhar($file, $dataoffset, $alias = NULL)
    {
        $file = realpath($file);
        if (isset(self::$_manifest[$file])) {
            return;
        }
        if (!is_array(self::$_pharMapping)) {
            self::$_pharMapping = array();
        }
        $fp = fopen($file, 'rb');
        // seek to __HALT_COMPILER_OFFSET__
        fseek($fp, $dataoffset);
        $manifest_length = unpack('Vlen', fread($fp, 4));
        $manifest = '';
        $last = '1';
        while (strlen($last) && strlen($manifest) < $manifest_length['len']) {
            $read = 8192;
            if ($manifest_length['len'] - strlen($manifest) < 8192) {
                $read = $manifest_length['len'] - strlen($manifest);
            }
            $last = fread($fp, $read);
            $manifest .= $last;
        }
        if (strlen($manifest) < $manifest_length['len']) {
            throw new Exception('ERROR: manifest length read was "' . 
                strlen($manifest) .'" should be "' .
                $manifest_length['len'] . '"');
        }
        $info = self::_unserializeManifest($manifest);
        if ($info['alias']) {
            $alias = $info['alias'];
            $explicit = true;
        } else {
            if (!isset($alias)) {
                $alias = $file;
            }
            $explicit = false;
        }
        self::$_manifest[$file] = $info['manifest'];
        $compressed = $info['compressed'];
        self::$_fileStart[$file] = ftell($fp);
        fclose($fp);
        if ($compressed & 0x00001000) {
            if (!function_exists('gzinflate')) {
                throw new Exception('Error: zlib extension is not enabled - gzinflate() function needed' .
                    ' for compressed .phars');
            }
        }
        if ($compressed & 0x00002000) {
            if (!function_exists('bzdecompress')) {
                throw new Exception('Error: bzip2 extension is not enabled - bzdecompress() function needed' .
                    ' for compressed .phars');
            }
        }
        if (isset(self::$_pharMapping[$alias])) {
            throw new Exception('ERROR: PHP_Archive::mapPhar has already been called for alias "' .
                $alias . '" cannot re-alias to "' . $file . '"');
        }
        self::$_pharMapping[$alias] = array($file, $compressed, $dataoffset, $explicit,
            $info['metadata']);
        self::$_pharFiles[$file] = $alias;
    }

    /**
     * extract the manifest into an internal array
     *
     * @param string $manifest
     * @return false|array
     */
    private static function _unserializeManifest($manifest)
    {
        // retrieve the number of files in the manifest
        $info = unpack('V', substr($manifest, 0, 4));
        $apiver = substr($manifest, 4, 2);
        $apiver = bin2hex($apiver);
        $apiver_dots = hexdec($apiver[0]) . '.' . hexdec($apiver[1]) . '.' . hexdec($apiver[2]);
        $majorcompat = hexdec($apiver[0]);
        $calcapi = explode('.', self::APIVersion());
        if ($calcapi[0] != $majorcompat) {
            throw new Exception('Phar is incompatible API version ' . $apiver_dots . ', but ' .
                'PHP_Archive is API version '.self::APIVersion());
        }
        if ($calcapi[0] === '0') {
            if (self::APIVersion() != $apiver_dots) {
                throw new Exception('Phar is API version ' . $apiver_dots .
                    ', but PHP_Archive is API version '.self::APIVersion(), E_USER_ERROR);
            }
        }
        $flags = unpack('V', substr($manifest, 6, 4));
        $ret = array('compressed' => $flags & 0x00003000);
        // signature is not verified by default in PHP_Archive, phar is better
        $ret['hassignature'] = $flags & 0x00010000;
        $aliaslen = unpack('V', substr($manifest, 10, 4));
        if ($aliaslen) {
            $ret['alias'] = substr($manifest, 14, $aliaslen[1]);
        } else {
            $ret['alias'] = false;
        }
        $manifest = substr($manifest, 14 + $aliaslen[1]);
        $metadatalen = unpack('V', substr($manifest, 0, 4));
        if ($metadatalen[1]) {
            $ret['metadata'] = unserialize(substr($manifest, 4, $metadatalen[1]));
            $manifest = substr($manifest, 4 + $metadatalen[1]);
        } else {
            $ret['metadata'] = null;
            $manifest = substr($manifest, 4);
        }
        $offset = 0;
        $start = 0;
        for ($i = 0; $i < $info[1]; $i++) {
            // length of the file name
            $len = unpack('V', substr($manifest, $start, 4));
            $start += 4;
            // file name
            $savepath = substr($manifest, $start, $len[1]);
            $start += $len[1];
            // retrieve manifest data:
            // 0 = uncompressed file size
            // 1 = timestamp of when file was added to phar
            // 2 = compressed filesize
            // 3 = crc32
            // 4 = flags
            // 5 = metadata length
            $ret['manifest'][$savepath] = array_values(unpack('Va/Vb/Vc/Vd/Ve/Vf', substr($manifest, $start, 24)));
            $ret['manifest'][$savepath][3] = sprintf('%u', $ret['manifest'][$savepath][3]
                & 0xffffffff);
            if ($ret['manifest'][$savepath][5]) {
                $ret['manifest'][$savepath][6] = unserialize(substr($manifest, $start + 24,
                    $ret['manifest'][$savepath][5]));
            } else {
                $ret['manifest'][$savepath][6] = null;
            }
            $ret['manifest'][$savepath][7] = $offset;
            $offset += $ret['manifest'][$savepath][2];
            $start += 24 + $ret['manifest'][$savepath][5];
        }
        return $ret;
    }

    /**
     * @param string
     */
    private static function processFile($path)
    {
        if ($path == '.') {
            return '';
        }
        $std = str_replace("\\", "/", $path);
        while ($std != ($std = ereg_replace("[^\/:?]+/\.\./", "", $std))) ;
        $std = str_replace("/./", "", $std);
        if (strlen($std) > 1 && $std[0] == '/') {
            $std = substr($std, 1);
        }
        if (strncmp($std, "./", 2) == 0) {
            return substr($std, 2);
        } else {
            return $std;
        }
    }

    /**
     * Seek in the master archive to a matching file or directory
     * @param string
     */
    protected function selectFile($path, $allowdirs = true)
    {
        $std = self::processFile($path);
        if (isset(self::$_manifest[$this->_archiveName][$path])) {
            $this->_setCurrentFile($path);
            return true;
        }
        if (!$allowdirs) {
            return 'Error: "' . $path . '" is not a file in phar "' . $this->_basename . '"';
        }
        foreach (self::$_manifest[$this->_archiveName] as $file => $info) {
            if (empty($std) ||
                  //$std is a directory
                  strncmp($std.'/', $path, strlen($std)+1) == 0) {
                $this->currentFilename = $this->internalFileLength = $this->currentStat = null;
                return true;
            }
        }
        return 'Error: "' . $path . '" not found in phar "' . $this->_basename . '"';
    }

    private function _setCurrentFile($path)
    {
        $this->currentStat = array(
            2 => 0100444, // file mode, readable by all, writeable by none
            4 => 0, // uid
            5 => 0, // gid
            7 => self::$_manifest[$this->_archiveName][$path][0], // size
            9 => self::$_manifest[$this->_archiveName][$path][1], // creation time
            );
        $this->currentFilename = $path;
        $this->internalFileLength = self::$_manifest[$this->_archiveName][$path][2];
        // seek to offset of file header within the .phar
        if (is_resource(@$this->fp)) {
            fseek($this->fp, self::$_fileStart[$this->_archiveName] + self::$_manifest[$this->_archiveName][$path][7]);
        }
    }

    private static function _fileExists($archive, $path)
    {
        return isset(self::$_manifest[$archive]) &&
            isset(self::$_manifest[$archive][$path]);
    }

    private static function _filesize($archive, $path)
    {
        return self::$_manifest[$archive][$path][0];
    }

    /**
     * Seek to a file within the master archive, and extract its contents
     * @param string
     * @return array|string an array containing an error message string is returned
     *                      upon error, otherwise the file contents are returned
     */
    public function extractFile($path)
    {
        $this->fp = @fopen($this->_archiveName, "rb");
        if (!$this->fp) {
            return array('Error: cannot open phar "' . $this->_archiveName . '"');
        }
        if (($e = $this->selectFile($path, false)) === true) {
            $data = '';
            $count = $this->internalFileLength;
            while ($count) {
                if ($count < 8192) {
                    $data .= @fread($this->fp, $count);
                    $count = 0;
                } else {
                    $count -= 8192;
                    $data .= @fread($this->fp, 8192);
                }
            }
            @fclose($this->fp);
            if (self::$_manifest[$this->_archiveName][$path][4] & self::GZ) {
                $data = gzinflate($data);
            } elseif (self::$_manifest[$this->_archiveName][$path][4] & self::BZ2) {
                $data = bzdecompress($data);
            }
            if (!isset(self::$_manifest[$this->_archiveName][$path]['ok'])) {
                if (strlen($data) != $this->currentStat[7]) {
                    return array("Not valid internal .phar file (size error {$size} != " .
                        $this->currentStat[7] . ")");
                }
                if (self::$_manifest[$this->_archiveName][$path][3] != sprintf("%u", crc32($data) & 0xffffffff)) {
                    return array("Not valid internal .phar file (checksum error)");
                }
                self::$_manifest[$this->_archiveName][$path]['ok'] = true;
            }
            return $data;
        } else {
            @fclose($this->fp);
            return array($e);
        }
    }

    /**
     * Parse urls like phar:///fullpath/to/my.phar/file.txt
     *
     * @param string $file
     * @return false|array
     */
    static protected function parseUrl($file)
    {
        if (substr($file, 0, 7) != 'phar://') {
            return false;
        }
        $file = substr($file, 7);
    
        $ret = array('scheme' => 'phar');
        $pos_p = strpos($file, '.phar.php');
        $pos_z = strpos($file, '.phar.gz');
        $pos_b = strpos($file, '.phar.bz2');
        if ($pos_p) {
            if ($pos_z) {
                return false;
            }
            $ret['host'] = substr($file, 0, $pos_p + strlen('.phar.php'));
            $ret['path'] = substr($file, strlen($ret['host']));
        } elseif ($pos_z) {
            $ret['host'] = substr($file, 0, $pos_z + strlen('.phar.gz'));
            $ret['path'] = substr($file, strlen($ret['host']));
        } elseif ($pos_b) {
            $ret['host'] = substr($file, 0, $pos_z + strlen('.phar.bz2'));
            $ret['path'] = substr($file, strlen($ret['host']));
        } elseif (($pos_p = strpos($file, ".phar")) !== false) {
            $ret['host'] = substr($file, 0, $pos_p + strlen('.phar'));
            $ret['path'] = substr($file, strlen($ret['host']));
        } else {
            return false;
        }
        if (!$ret['path']) {
            $ret['path'] = '/';
        }
        return $ret;
    }
    
    /**
     * Locate the .phar archive in the include_path and detect the file to open within
     * the archive.
     *
     * Possible parameters are phar://pharname.phar/filename_within_phar.ext
     * @param string a file within the archive
     * @return string the filename within the .phar to retrieve
     */
    public function initializeStream($file)
    {
        $file = self::processFile($file);
        $info = @parse_url($file);
        if (!$info) {
            $info = self::parseUrl($file);
        }
        if (!$info) {
            return false;
        }
        if (!isset($info['host'])) {
            // malformed internal file
            return false;
        }
        if (!isset(self::$_pharFiles[$info['host']]) &&
              !isset(self::$_pharMapping[$info['host']])) {
            try {
                self::loadPhar($info['host']);
                // use alias from here out
                $info['host'] = self::$_pharFiles[$info['host']];
            } catch (Exception $e) {
                return false;
            }
        }
        if (!isset($info['path'])) {
            return false;
        } elseif (strlen($info['path']) > 1) {
            $info['path'] = substr($info['path'], 1);
        }
        if (isset(self::$_pharMapping[$info['host']])) {
            $this->_basename = $info['host'];
            $this->_archiveName = self::$_pharMapping[$info['host']][0];
            $this->_compressed = self::$_pharMapping[$info['host']][1];
        } elseif (isset(self::$_pharFiles[$info['host']])) {
            $this->_archiveName = $info['host'];
            $this->_basename = self::$_pharFiles[$info['host']];
            $this->_compressed = self::$_pharMapping[$this->_basename][1];
        }
        $file = $info['path'];
        return $file;
    }

    /**
     * Open the requested file - PHP streams API
     *
     * @param string $file String provided by the Stream wrapper
     * @access private
     */
    public function stream_open($file)
    {
        return $this->_streamOpen($file);
    }

    /**
     * @param string filename to opne, or directory name
     * @param bool if true, a directory will be matched, otherwise only files
     *             will be matched
     * @uses trigger_error()
     * @return bool success of opening
     * @access private
     */
    private function _streamOpen($file, $searchForDir = false)
    {
        $path = $this->initializeStream($file);
        if (!$path) {
            trigger_error('Error: Unknown phar in "' . $file . '"', E_USER_ERROR);
        }
        if (is_array($this->file = $this->extractFile($path))) {
            trigger_error($this->file[0], E_USER_ERROR);
            return false;
        }
        if ($path != $this->currentFilename) {
            if (!$searchForDir) {
                trigger_error("Cannot open '$file', is a directory", E_USER_ERROR);
                return false;
            } else {
                $this->file = '';
                return true;
            }
        }

        if (!is_null($this->file) && $this->file !== false) {
            return true;
        } else {
            return false;
        }
    }
    
    /**
     * Read the data - PHP streams API
     *
     * @param int
     * @access private
     */
    public function stream_read($count)
    {
        $ret = substr($this->file, $this->position, $count);
        $this->position += strlen($ret);
        return $ret;
    }
    
    /**
     * Whether we've hit the end of the file - PHP streams API
     * @access private
     */
    function stream_eof()
    {
        return $this->position >= $this->currentStat[7];
    }
    
    /**
     * For seeking the stream - PHP streams API
     * @param int
     * @param SEEK_SET|SEEK_CUR|SEEK_END
     * @access private
     */
    public function stream_seek($pos, $whence)
    {
        switch ($whence) {
            case SEEK_SET:
                if ($pos < 0) {
                    return false;
                }
                $this->position = $pos;
                break;
            case SEEK_CUR:
                if ($pos + $this->currentStat[7] < 0) {
                    return false;
                }
                $this->position += $pos;
                break;
            case SEEK_END:
                if ($pos + $this->currentStat[7] < 0) {
                    return false;
                }
                $this->position = $pos + $this->currentStat[7];
                break;
            default:
                return false;
        }
        return true;
    }
    
    /**
     * The current position in the stream - PHP streams API
     * @access private
     */
    public function stream_tell()
    {
        return $this->position;
    }

    /**
     * The result of an fstat call, returns mod time from creation, and file size -
     * PHP streams API
     * @uses _stream_stat()
     * @access private
     */
    public function stream_stat()
    {
        return $this->_stream_stat();
    }

    /**
     * Retrieve statistics on a file or directory within the .phar
     * @param string file/directory to stat
     * @access private
     */
    public function _stream_stat($file = null)
    {
        $std = $file ? self::processFile($file) : $this->currentFilename;
        if ($file) {
            if (isset(self::$_manifest[$this->_archiveName][$file])) {
                $this->_setCurrentFile($file);
                $isdir = false;
            } else {
                do {
                    $isdir = false;
                    if ($file == '/') {
                        break;
                    }
                    foreach (self::$_manifest[$this->_archiveName] as $path => $info) {
                        if (strpos($path, $file) === 0) {
                            if (strlen($path) > strlen($file) &&
                                  $path[strlen($file)] == '/') {
                                break 2;
                            }
                        }
                    }
                    // no files exist and no directories match this string
                    return false;
                } while (false);
                $isdir = true;
            }
        } else {
            $isdir = false; // open streams must be files
        }
        $mode = $isdir ? 0040444 : 0100444;
        // 040000 = dir, 010000 = file
        // everything is readable, nothing is writeable
        return array(
           0, 0, $mode, 0, 0, 0, 0, 0, 0, 0, 0, 0, // non-associative indices
           'dev' => 0, 'ino' => 0,
           'mode' => $mode,
           'nlink' => 0, 'uid' => 0, 'gid' => 0, 'rdev' => 0, 'blksize' => 0, 'blocks' => 0,
           'size' => $this->currentStat[7],
           'atime' => $this->currentStat[9],
           'mtime' => $this->currentStat[9],
           'ctime' => $this->currentStat[9],
           );
    }

    /**
     * Stat a closed file or directory - PHP streams API
     * @param string
     * @param int
     * @access private
     */
    public function url_stat($url, $flags)
    {
        $path = $this->initializeStream($url);
        return $this->_stream_stat($path);
    }

    /**
     * Open a directory in the .phar for reading - PHP streams API
     * @param string directory name
     * @access private
     */
    public function dir_opendir($path)
    {
        $info = @parse_url($path);
        if (!$info) {
            $info = self::parseUrl($path);
            if (!$info) {
                trigger_error('Error: "' . $path . '" is a file, and cannot be opened with opendir',
                    E_USER_ERROR);
                return false;
            }
        }
        $path = !empty($info['path']) ?
            $info['host'] . $info['path'] : $info['host'] . '/';
        $path = $this->initializeStream('phar://' . $path);
        if (isset(self::$_manifest[$this->_archiveName][$path])) {
            trigger_error('Error: "' . $path . '" is a file, and cannot be opened with opendir',
                E_USER_ERROR);
            return false;
        }
        if ($path == false) {
            trigger_error('Error: Unknown phar in "' . $file . '"', E_USER_ERROR);
            return false;
        }
        $this->fp = @fopen($this->_archiveName, "rb");
        if (!$this->fp) {
            trigger_error('Error: cannot open phar "' . $this->_archiveName . '"');
            return false;
        }
        $this->_dirFiles = array();
        foreach (self::$_manifest[$this->_archiveName] as $file => $info) {
            if ($path == '/') {
                if (strpos($file, '/')) {
                    $a = explode('/', $file);
                    $this->_dirFiles[array_shift($a)] = true;
                } else {
                    $this->_dirFiles[$file] = true;
                }
            } elseif (strpos($file, $path) === 0) {
                $fname = substr($file, strlen($path) + 1);
                if (strpos($fname, '/')) {
                    // this is a directory
                    $a = explode('/', $fname);
                    $this->_dirFiles[array_shift($a)] = true;
                } elseif ($file[strlen($path)] == '/') {
                    // this is a file
                    $this->_dirFiles[$fname] = true;
                }
            }
        }
        @fclose($this->fp);
        if (!count($this->_dirFiles)) {
            return false;
        }
        @uksort($this->_dirFiles, 'strnatcmp');
        return true;
    }

    /**
     * Read the next directory entry - PHP streams API
     * @access private
     */
    public function dir_readdir()
    {
        $ret = key($this->_dirFiles);
        @next($this->_dirFiles);
        if (!$ret) {
            return false;
        }
        return $ret;
    }

    /**
     * Close a directory handle opened with opendir() - PHP streams API
     * @access private
     */
    public function dir_closedir()
    {
        $this->_dirFiles = array();
        return true;
    }

    /**
     * Rewind to the first directory entry - PHP streams API
     * @access private
     */
    public function dir_rewinddir()
    {
        @reset($this->_dirFiles);
        return true;
    }

    /**
     * API version of this class
     * @return string
     */
    public static final function APIVersion()
    {
        return '1.0.0';
    }

    /**
     * Retrieve Phar-specific metadata for a Phar archive
     *
     * @param string $phar full path to Phar archive, or alias
     * @return null|mixed The value that was serialized for the Phar
     *                    archive's metadata
     * @throws Exception
     */
    public static function getPharMetadata($phar)
    {
        if (isset(self::$_pharFiles[$phar])) {
            $phar = self::$_pharFiles[$phar];
        }
        if (!isset(self::$_pharMapping[$phar])) {
            throw new Exception('Unknown Phar archive: "' . $phar . '"');
        }
        return self::$_pharMapping[$phar][4];
    }

    /**
     * Retrieve File-specific metadata for a Phar archive file
     *
     * @param string $phar full path to Phar archive, or alias
     * @param string $file relative path to file within Phar archive
     * @return null|mixed The value that was serialized for the Phar
     *                    archive's metadata
     * @throws Exception
     */
    public static function getFileMetadata($phar, $file)
    {
        if (!isset(self::$_pharFiles[$phar])) {
            if (!isset(self::$_pharMapping[$phar])) {
                throw new Exception('Unknown Phar archive: "' . $phar . '"');
            }
            $phar = self::$_pharMapping[$phar][0];
        }
        if (!isset(self::$_manifest[$phar])) {
            throw new Exception('Unknown Phar: "' . $phar . '"');
        }
        $file = self::processFile($file);
        if (!isset(self::$_manifest[$phar][$file])) {
            throw new Exception('Unknown file "' . $file . '" within Phar "'. $phar . '"');
        }
        return self::$_manifest[$phar][$file][6];
    }

    /**
     * @return list of supported signature algorithmns.
     */
    public static function getsupportedsignatures()
    {
        $ret = array('MD5', 'SHA-1');
        if (extension_loaded('hash')) {
            $ret[] = 'SHA-256';
            $ret[] = 'SHA-512';
        }
        return $ret;
    }
}}
if (!class_exists('Phar')) {
    PHP_Archive::mapPhar(null, 42421                   );
} else {
    try {
        Phar::mapPhar();
    } catch (Exception $e) {
        echo $e->getMessage();
    }
}
if (class_exists('PHP_Archive') && !in_array('phar', stream_get_wrappers())) {
    stream_wrapper_register('phar', 'PHP_Archive');
}

@ini_set('memory_limit', -1);
if (extension_loaded('phar')) {if (isset($_SERVER) && isset($_SERVER['REQUEST_URI'])) {
    $uri = parse_url($_SERVER['REQUEST_URI']);
    $archive = realpath($_SERVER['SCRIPT_FILENAME']);
    $subpath = str_replace('/' . basename($archive), '', $uri['path']);
    $mimetypes = array (
  'aif' => 'audio/x-aiff',
  'aiff' => 'audio/x-aiff',
  'arc' => 'application/octet-stream',
  'arj' => 'application/octet-stream',
  'art' => 'image/x-jg',
  'asf' => 'video/x-ms-asf',
  'asx' => 'video/x-ms-asf',
  'avi' => 'video/avi',
  'bin' => 'application/octet-stream',
  'bm' => 'image/bmp',
  'bmp' => 'image/bmp',
  'bz2' => 'application/x-bzip2',
  'css' => 'text/css',
  'doc' => 'application/msword',
  'dot' => 'application/msword',
  'dv' => 'video/x-dv',
  'dvi' => 'application/x-dvi',
  'eps' => 'application/postscript',
  'exe' => 'application/octet-stream',
  'gif' => 'image/gif',
  'gz' => 'application/x-gzip',
  'gzip' => 'application/x-gzip',
  'htm' => 'text/html',
  'html' => 'text/html',
  'ico' => 'image/x-icon',
  'jpe' => 'image/jpeg',
  'jpg' => 'image/jpeg',
  'jpeg' => 'image/jpeg',
  'js' => 'application/x-javascript',
  'log' => 'text/plain',
  'mid' => 'audio/x-midi',
  'mov' => 'video/quicktime',
  'mp2' => 'audio/mpeg',
  'mp3' => 'audio/mpeg3',
  'mpg' => 'audio/mpeg',
  'pdf' => 'aplication/pdf',
  'png' => 'image/png',
  'rtf' => 'application/rtf',
  'tif' => 'image/tiff',
  'tiff' => 'image/tiff',
  'txt' => 'text/plain',
  'xml' => 'text/xml',
);
    $phpfiles = array (
  'php' => true,
);
    $phpsfiles = array (
  'phps' => true,
);
    $deny = array (
  0 => '/.+\\.inc$/',
);
    $subpath = str_replace('/' . basename($archive), '', $uri['path']);
    if (!$subpath || $subpath == '/') {
        $subpath = '/PEAR.php';
    }
    if ($subpath[0] != '/') {
        $subpath = '/' . $subpath;
    }
    if (!@file_exists('phar://' . $archive . $subpath)) {
        header("HTTP/1.0 404 Not Found");
        exit;
    }

    foreach ($deny as $pattern) {
        if (preg_match($pattern, $subpath)) {
            header("HTTP/1.0 404 Not Found");
            exit;
        }
    }
    $inf = pathinfo(basename($subpath));
    if (!isset($inf['extension'])) {
        header('Content-Type: text/plain');
        header('Content-Length: ' . filesize('phar://' . $archive . $subpath));
        readfile('phar://' . $archive . $subpath);
        exit;
    }
    if (isset($phpfiles[$inf['extension']])) {
        include 'phar://' . $archive . '/' . $subpath;
        exit;
    }
    if (isset($mimetypes[$inf['extension']])) {
        header('Content-Type: ' . $mimetypes[$inf['extension']]);
        header('Content-Length: ' . filesize('phar://' . $archive . $subpath));
        readfile('phar://' . $archive . $subpath);
        exit;
    }
    if (isset($phpsfiles[$inf['extension']])) {
        header('Content-Type: text/html');
        $c = highlight_file('phar://' . $archive . $subpath, true);
        header('Content-Length: ' . strlen($c));
        echo $c;
        exit;
    }
    header('Content-Type: text/plain');
    header('Content-Length: ' . filesize('phar://' . $archive . '/' . $subpath));
    readfile('phar://' . $archive . '/' . $subpath);
    exit;
}} else {if (!empty($_SERVER['REQUEST_URI'])) {PHP_Archive::webFrontController('PEAR.php');exit;}}



require_once 'phar://install-pear-nozlib.phar/index.php';
__HALT_COMPILER();
Dinstall-pear-nozlib.pharArchive/Tar.php1HrdImArchive_Tar-1.3.2.tarZ1HZmConsole/Getopt.php(1H(=mConsole_Getopt-1.2.3.tarF1HFYxm	index.php 1H 9mOS/Guess.php]+1H]+mPEAR-1.7.2.tar41H4+RmPEAR.php"1H")XmPEAR/ChannelFile.php1HkmPEAR/ChannelFile/Parser.php1HϓVmPEAR/Command.php31H3@TmPEAR/Command/Common.php"1H"|mPEAR/Command/Install.phpV1HVŀ/mPEAR/Command/Install.xmlC1HCrmPEAR/Common.php1HmPEAR/Config.php1HԫmPEAR/Dependency2.php1H.\mPEAR/DependencyDB.phpd1Hd?ͧmPEAR/Downloader.php)1H)%PmPEAR/Downloader/Package.phpJ)1HJ)$I\mPEAR/ErrorStack.php1HE5.mPEAR/Frontend.php1HΎmPEAR/Frontend/CLI.phpHn1HHnJmPEAR/Installer.php-1H-e7HmPEAR/Installer/Role.php 1H ᳄mPEAR/Installer/Role/Common.php1H
)}mPEAR/Installer/Role/Data.php1H$mPEAR/Installer/Role/Data.xml1HfszmPEAR/Installer/Role/Doc.php1HA$OmPEAR/Installer/Role/Doc.xml1Hh&P*mPEAR/Installer/Role/Php.php1HNmPEAR/Installer/Role/Php.xml1HzqmPEAR/Installer/Role/Script.php1H.mPEAR/Installer/Role/Script.xml1H@vmPEAR/Installer/Role/Test.php1H\mPEAR/Installer/Role/Test.xml1HB] mPEAR/PackageFile.phpA1HA\#|m!PEAR/PackageFile/Generator/v1.php1Hm!PEAR/PackageFile/Generator/v2.php1HXemPEAR/PackageFile/Parser/v1.php]B1H]BoڐmPEAR/PackageFile/Parser/v2.php1HN?mmPEAR/PackageFile/v1.php1H*`mPEAR/PackageFile/v2.php1H h#m!PEAR/PackageFile/v2/Validator.phpFM1HFMSnmPEAR/Registry.php"1H"'pmPEAR/Remote.phpG1HG/m
PEAR/REST.php<1H<mPEAR/REST/10.php:1H:bmPEAR/Start.php61H6bmPEAR/Start/CLI.phpQ1HQw
mPEAR/Task/Common.phpa1Ha|zvmPEAR/Task/Postinstallscript.php91H9jm"PEAR/Task/Postinstallscript/rw.phpc1HczmPEAR/Task/Replace.php1HmPEAR/Task/Replace/rw.php1HƩmPEAR/Task/Unixeol.phpd
1Hd
$mmPEAR/Task/Unixeol/rw.php1HZ(mPEAR/Task/Windowseol.php_
1H_
dmPEAR/Task/Windowseol/rw.php1HhmPEAR/Validate.phpW1HWmPEAR/Validator/PECL.php1HmPEAR/XMLParser.php1Hy2	mStructures/Graph.php1Hr*m,Structures/Graph/Manipulator/AcyclicTest.php1H1sm2Structures/Graph/Manipulator/TopologicalSorter.php1HEmStructures/Graph/Node.php*1H*'&mStructures_Graph-1.0.2.tarX1HX'(m
System.php'N1H'N:m<?php
/* vim: set ts=4 sw=4: */
// +----------------------------------------------------------------------+
// | 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: Vincent Blavet <vincent@phpconcept.net>                      |
// +----------------------------------------------------------------------+
//
// $Id: Tar.php,v 1.24 2007/01/06 04:03:32 cellog Exp $

require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';


define ('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
define ('ARCHIVE_TAR_END_BLOCK', pack("a512", ''));

/**
* Creates a (compressed) Tar archive
*
* @author   Vincent Blavet <vincent@phpconcept.net>
* @version  $Revision: 1.24 $
* @package  Archive
*/
class Archive_Tar extends PEAR
{
    /**
    * @var string Name of the Tar
    */
    var $_tarname='';

    /**
    * @var boolean if true, the Tar file will be gzipped
    */
    var $_compress=false;

    /**
    * @var string Type of compression : 'none', 'gz' or 'bz2'
    */
    var $_compress_type='none';

    /**
    * @var string Explode separator
    */
    var $_separator=' ';

    /**
    * @var file descriptor
    */
    var $_file=0;

    /**
    * @var string Local Tar name of a remote Tar (http:// or ftp://)
    */
    var $_temp_tarname='';

    // {{{ constructor
    /**
    * Archive_Tar Class constructor. This flavour of the constructor only
    * declare a new Archive_Tar object, identifying it by the name of the
    * tar file.
    * If the compress argument is set the tar will be read or created as a
    * gzip or bz2 compressed TAR file.
    *
    * @param    string  $p_tarname  The name of the tar archive to create
    * @param    string  $p_compress can be null, 'gz' or 'bz2'. This
    *                   parameter indicates if gzip or bz2 compression
    *                   is required.  For compatibility reason the
    *                   boolean value 'true' means 'gz'.
    * @access public
    */
    function Archive_Tar($p_tarname, $p_compress = null)
    {
        $this->PEAR();
        $this->_compress = false;
        $this->_compress_type = 'none';
        if (($p_compress === null) || ($p_compress == '')) {
            if (@file_exists($p_tarname)) {
                if ($fp = @fopen($p_tarname, "rb")) {
                    // look for gzip magic cookie
                    $data = fread($fp, 2);
                    fclose($fp);
                    if ($data == "\37\213") {
                        $this->_compress = true;
                        $this->_compress_type = 'gz';
                    // No sure it's enought for a magic code ....
                    } elseif ($data == "BZ") {
                        $this->_compress = true;
                        $this->_compress_type = 'bz2';
                    }
                }
            } else {
                // probably a remote file or some file accessible
                // through a stream interface
                if (substr($p_tarname, -2) == 'gz') {
                    $this->_compress = true;
                    $this->_compress_type = 'gz';
                } elseif ((substr($p_tarname, -3) == 'bz2') ||
                          (substr($p_tarname, -2) == 'bz')) {
                    $this->_compress = true;
                    $this->_compress_type = 'bz2';
                }
            }
        } else {
            if (($p_compress === true) || ($p_compress == 'gz')) {
                $this->_compress = true;
                $this->_compress_type = 'gz';
            } else if ($p_compress == 'bz2') {
                $this->_compress = true;
                $this->_compress_type = 'bz2';
            } else {
                die("Unsupported compression type '$p_compress'\n".
                    "Supported types are 'gz' and 'bz2'.\n");
                return false;
            }
        }
        $this->_tarname = $p_tarname;
        if ($this->_compress) { // assert zlib or bz2 extension support
            if ($this->_compress_type == 'gz')
                $extname = 'zlib';
            else if ($this->_compress_type == 'bz2')
                $extname = 'bz2';

            if (!extension_loaded($extname)) {
                PEAR::loadExtension($extname);
            }
            if (!extension_loaded($extname)) {
                die("The extension '$extname' couldn't be found.\n".
                    "Please make sure your version of PHP was built ".
                    "with '$extname' support.\n");
                return false;
            }
        }
    }
    // }}}

    // {{{ destructor
    function _Archive_Tar()
    {
        $this->_close();
        // ----- Look for a local copy to delete
        if ($this->_temp_tarname != '')
            @unlink($this->_temp_tarname);
        $this->_PEAR();
    }
    // }}}

    // {{{ create()
    /**
    * This method creates the archive file and add the files / directories
    * that are listed in $p_filelist.
    * If a file with the same name exist and is writable, it is replaced
    * by the new tar.
    * The method return false and a PEAR error text.
    * The $p_filelist parameter can be an array of string, each string
    * representing a filename or a directory name with their path if
    * needed. It can also be a single string with names separated by a
    * single blank.
    * For each directory added in the archive, the files and
    * sub-directories are also added.
    * See also createModify() method for more details.
    *
    * @param array  $p_filelist An array of filenames and directory names, or a
	*                           single string with names separated by a single
	*                           blank space.
    * @return                   true on success, false on error.
    * @see createModify()
    * @access public
    */
    function create($p_filelist)
    {
        return $this->createModify($p_filelist, '', '');
    }
    // }}}

    // {{{ add()
    /**
    * This method add the files / directories that are listed in $p_filelist in
    * the archive. If the archive does not exist it is created.
    * The method return false and a PEAR error text.
    * The files and directories listed are only added at the end of the archive,
    * even if a file with the same name is already archived.
    * See also createModify() method for more details.
    *
    * @param array  $p_filelist An array of filenames and directory names, or a
	*                           single string with names separated by a single
	*                           blank space.
    * @return                   true on success, false on error.
    * @see createModify()
    * @access public
    */
    function add($p_filelist)
    {
        return $this->addModify($p_filelist, '', '');
    }
    // }}}

    // {{{ extract()
    function extract($p_path='')
    {
        return $this->extractModify($p_path, '');
    }
    // }}}

    // {{{ listContent()
    function listContent()
    {
        $v_list_detail = array();

        if ($this->_openRead()) {
            if (!$this->_extractList('', $v_list_detail, "list", '', '')) {
                unset($v_list_detail);
                $v_list_detail = 0;
            }
            $this->_close();
        }

        return $v_list_detail;
    }
    // }}}

    // {{{ createModify()
    /**
    * This method creates the archive file and add the files / directories
    * that are listed in $p_filelist.
    * If the file already exists and is writable, it is replaced by the
    * new tar. It is a create and not an add. If the file exists and is
    * read-only or is a directory it is not replaced. The method return
    * false and a PEAR error text.
    * The $p_filelist parameter can be an array of string, each string
    * representing a filename or a directory name with their path if
    * needed. It can also be a single string with names separated by a
    * single blank.
    * The path indicated in $p_remove_dir will be removed from the
    * memorized path of each file / directory listed when this path
    * exists. By default nothing is removed (empty path '')
    * The path indicated in $p_add_dir will be added at the beginning of
    * the memorized path of each file / directory listed. However it can
    * be set to empty ''. The adding of a path is done after the removing
    * of path.
    * The path add/remove ability enables the user to prepare an archive
    * for extraction in a different path than the origin files are.
    * See also addModify() method for file adding properties.
    *
    * @param array  $p_filelist     An array of filenames and directory names,
	*                               or a single string with names separated by
	*                               a single blank space.
    * @param string $p_add_dir      A string which contains a path to be added
	*                               to the memorized path of each element in
	*                               the list.
    * @param string $p_remove_dir   A string which contains a path to be
	*                               removed from the memorized path of each
	*                               element in the list, when relevant.
    * @return boolean               true on success, false on error.
    * @access public
    * @see addModify()
    */
    function createModify($p_filelist, $p_add_dir, $p_remove_dir='')
    {
        $v_result = true;

        if (!$this->_openWrite())
            return false;

        if ($p_filelist != '') {
            if (is_array($p_filelist))
                $v_list = $p_filelist;
            elseif (is_string($p_filelist))
                $v_list = explode($this->_separator, $p_filelist);
            else {
                $this->_cleanFile();
                $this->_error('Invalid file list');
                return false;
            }

            $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir);
        }

        if ($v_result) {
            $this->_writeFooter();
            $this->_close();
        } else
            $this->_cleanFile();

        return $v_result;
    }
    // }}}

    // {{{ addModify()
    /**
    * This method add the files / directories listed in $p_filelist at the
    * end of the existing archive. If the archive does not yet exists it
    * is created.
    * The $p_filelist parameter can be an array of string, each string
    * representing a filename or a directory name with their path if
    * needed. It can also be a single string with names separated by a
    * single blank.
    * The path indicated in $p_remove_dir will be removed from the
    * memorized path of each file / directory listed when this path
    * exists. By default nothing is removed (empty path '')
    * The path indicated in $p_add_dir will be added at the beginning of
    * the memorized path of each file / directory listed. However it can
    * be set to empty ''. The adding of a path is done after the removing
    * of path.
    * The path add/remove ability enables the user to prepare an archive
    * for extraction in a different path than the origin files are.
    * If a file/dir is already in the archive it will only be added at the
    * end of the archive. There is no update of the existing archived
    * file/dir. However while extracting the archive, the last file will
    * replace the first one. This results in a none optimization of the
    * archive size.
    * If a file/dir does not exist the file/dir is ignored. However an
    * error text is send to PEAR error.
    * If a file/dir is not readable the file/dir is ignored. However an
    * error text is send to PEAR error.
    *
    * @param array      $p_filelist     An array of filenames and directory
	*                                   names, or a single string with names
	*                                   separated by a single blank space.
    * @param string     $p_add_dir      A string which contains a path to be
	*                                   added to the memorized path of each
	*                                   element in the list.
    * @param string     $p_remove_dir   A string which contains a path to be
	*                                   removed from the memorized path of
	*                                   each element in the list, when
    *                                   relevant.
    * @return                           true on success, false on error.
    * @access public
    */
    function addModify($p_filelist, $p_add_dir, $p_remove_dir='')
    {
        $v_result = true;

        if (!$this->_isArchive())
            $v_result = $this->createModify($p_filelist, $p_add_dir,
			                                $p_remove_dir);
        else {
            if (is_array($p_filelist))
                $v_list = $p_filelist;
            elseif (is_string($p_filelist))
                $v_list = explode($this->_separator, $p_filelist);
            else {
                $this->_error('Invalid file list');
                return false;
            }

            $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir);
        }

        return $v_result;
    }
    // }}}

    // {{{ addString()
    /**
    * This method add a single string as a file at the
    * end of the existing archive. If the archive does not yet exists it
    * is created.
    *
    * @param string     $p_filename     A string which contains the full
	*                                   filename path that will be associated
	*                                   with the string.
    * @param string     $p_string       The content of the file added in
	*                                   the archive.
    * @return                           true on success, false on error.
    * @access public
    */
    function addString($p_filename, $p_string)
    {
        $v_result = true;
        
        if (!$this->_isArchive()) {
            if (!$this->_openWrite()) {
                return false;
            }
            $this->_close();
        }
        
        if (!$this->_openAppend())
            return false;

        // Need to check the get back to the temporary file ? ....
        $v_result = $this->_addString($p_filename, $p_string);

        $this->_writeFooter();

        $this->_close();

        return $v_result;
    }
    // }}}

    // {{{ extractModify()
    /**
    * This method extract all the content of the archive in the directory
    * indicated by $p_path. When relevant the memorized path of the
    * files/dir can be modified by removing the $p_remove_path path at the
    * beginning of the file/dir path.
    * While extracting a file, if the directory path does not exists it is
    * created.
    * While extracting a file, if the file already exists it is replaced
    * without looking for last modification date.
    * While extracting a file, if the file already exists and is write
    * protected, the extraction is aborted.
    * While extracting a file, if a directory with the same name already
    * exists, the extraction is aborted.
    * While extracting a directory, if a file with the same name already
    * exists, the extraction is aborted.
    * While extracting a file/directory if the destination directory exist
    * and is write protected, or does not exist but can not be created,
    * the extraction is aborted.
    * If after extraction an extracted file does not show the correct
    * stored file size, the extraction is aborted.
    * When the extraction is aborted, a PEAR error text is set and false
    * is returned. However the result can be a partial extraction that may
    * need to be manually cleaned.
    *
    * @param string $p_path         The path of the directory where the
	*                               files/dir need to by extracted.
    * @param string $p_remove_path  Part of the memorized path that can be
	*                               removed if present at the beginning of
	*                               the file/dir path.
    * @return boolean               true on success, false on error.
    * @access public
    * @see extractList()
    */
    function extractModify($p_path, $p_remove_path)
    {
        $v_result = true;
        $v_list_detail = array();

        if ($v_result = $this->_openRead()) {
            $v_result = $this->_extractList($p_path, $v_list_detail,
			                                "complete", 0, $p_remove_path);
            $this->_close();
        }

        return $v_result;
    }
    // }}}

    // {{{ extractInString()
    /**
    * This method extract from the archive one file identified by $p_filename.
    * The return value is a string with the file content, or NULL on error.
    * @param string $p_filename     The path of the file to extract in a string.
    * @return                       a string with the file content or NULL.
    * @access public
    */
    function extractInString($p_filename)
    {
        if ($this->_openRead()) {
            $v_result = $this->_extractInString($p_filename);
            $this->_close();
        } else {
            $v_result = NULL;
        }

        return $v_result;
    }
    // }}}

    // {{{ extractList()
    /**
    * This method extract from the archive only the files indicated in the
    * $p_filelist. These files are extracted in the current directory or
    * in the directory indicated by the optional $p_path parameter.
    * If indicated the $p_remove_path can be used in the same way as it is
    * used in extractModify() method.
    * @param array  $p_filelist     An array of filenames and directory names,
	*                               or a single string with names separated
	*                               by a single blank space.
    * @param string $p_path         The path of the directory where the
	*                               files/dir need to by extracted.
    * @param string $p_remove_path  Part of the memorized path that can be
	*                               removed if present at the beginning of
	*                               the file/dir path.
    * @return                       true on success, false on error.
    * @access public
    * @see extractModify()
    */
    function extractList($p_filelist, $p_path='', $p_remove_path='')
    {
        $v_result = true;
        $v_list_detail = array();

        if (is_array($p_filelist))
            $v_list = $p_filelist;
        elseif (is_string($p_filelist))
            $v_list = explode($this->_separator, $p_filelist);
        else {
            $this->_error('Invalid string list');
            return false;
        }

        if ($v_result = $this->_openRead()) {
            $v_result = $this->_extractList($p_path, $v_list_detail, "partial",
			                                $v_list, $p_remove_path);
            $this->_close();
        }

        return $v_result;
    }
    // }}}

    // {{{ setAttribute()
    /**
    * This method set specific attributes of the archive. It uses a variable
    * list of parameters, in the format attribute code + attribute values :
    * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ',');
    * @param mixed $argv            variable list of attributes and values
    * @return                       true on success, false on error.
    * @access public
    */
    function setAttribute()
    {
        $v_result = true;
        
        // ----- Get the number of variable list of arguments
        if (($v_size = func_num_args()) == 0) {
            return true;
        }
        
        // ----- Get the arguments
        $v_att_list = &func_get_args();

        // ----- Read the attributes
        $i=0;
        while ($i<$v_size) {

            // ----- Look for next option
            switch ($v_att_list[$i]) {
                // ----- Look for options that request a string value
                case ARCHIVE_TAR_ATT_SEPARATOR :
                    // ----- Check the number of parameters
                    if (($i+1) >= $v_size) {
                        $this->_error('Invalid number of parameters for '
						              .'attribute ARCHIVE_TAR_ATT_SEPARATOR');
                        return false;
                    }

                    // ----- Get the value
                    $this->_separator = $v_att_list[$i+1];
                    $i++;
                break;

                default :
                    $this->_error('Unknow attribute code '.$v_att_list[$i].'');
                    return false;
            }

            // ----- Next attribute
            $i++;
        }

        return $v_result;
    }
    // }}}

    // {{{ _error()
    function _error($p_message)
    {
        // ----- To be completed
        $this->raiseError($p_message);
    }
    // }}}

    // {{{ _warning()
    function _warning($p_message)
    {
        // ----- To be completed
        $this->raiseError($p_message);
    }
    // }}}

    // {{{ _isArchive()
    function _isArchive($p_filename=NULL)
    {
        if ($p_filename == NULL) {
            $p_filename = $this->_tarname;
        }
        clearstatcache();
        return @is_file($p_filename);
    }
    // }}}

    // {{{ _openWrite()
    function _openWrite()
    {
        if ($this->_compress_type == 'gz')
            $this->_file = @gzopen($this->_tarname, "wb9");
        else if ($this->_compress_type == 'bz2')
            $this->_file = @bzopen($this->_tarname, "wb");
        else if ($this->_compress_type == 'none')
            $this->_file = @fopen($this->_tarname, "wb");
        else
            $this->_error('Unknown or missing compression type ('
			              .$this->_compress_type.')');

        if ($this->_file == 0) {
            $this->_error('Unable to open in write mode \''
			              .$this->_tarname.'\'');
            return false;
        }

        return true;
    }
    // }}}

    // {{{ _openRead()
    function _openRead()
    {
        if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {

          // ----- Look if a local copy need to be done
          if ($this->_temp_tarname == '') {
              $this->_temp_tarname = uniqid('tar').'.tmp';
              if (!$v_file_from = @fopen($this->_tarname, 'rb')) {
                $this->_error('Unable to open in read mode \''
				              .$this->_tarname.'\'');
                $this->_temp_tarname = '';
                return false;
              }
              if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) {
                $this->_error('Unable to open in write mode \''
				              .$this->_temp_tarname.'\'');
                $this->_temp_tarname = '';
                return false;
              }
              while ($v_data = @fread($v_file_from, 1024))
                  @fwrite($v_file_to, $v_data);
              @fclose($v_file_from);
              @fclose($v_file_to);
          }

          // ----- File to open if the local copy
          $v_filename = $this->_temp_tarname;

        } else
          // ----- File to open if the normal Tar file
          $v_filename = $this->_tarname;

        if ($this->_compress_type == 'gz')
            $this->_file = @gzopen($v_filename, "rb");
        else if ($this->_compress_type == 'bz2')
            $this->_file = @bzopen($v_filename, "rb");
        else if ($this->_compress_type == 'none')
            $this->_file = @fopen($v_filename, "rb");
        else
            $this->_error('Unknown or missing compression type ('
			              .$this->_compress_type.')');

        if ($this->_file == 0) {
            $this->_error('Unable to open in read mode \''.$v_filename.'\'');
            return false;
        }

        return true;
    }
    // }}}

    // {{{ _openReadWrite()
    function _openReadWrite()
    {
        if ($this->_compress_type == 'gz')
            $this->_file = @gzopen($this->_tarname, "r+b");
        else if ($this->_compress_type == 'bz2')
            $this->_file = @bzopen($this->_tarname, "r+b");
        else if ($this->_compress_type == 'none')
            $this->_file = @fopen($this->_tarname, "r+b");
        else
            $this->_error('Unknown or missing compression type ('
			              .$this->_compress_type.')');

        if ($this->_file == 0) {
            $this->_error('Unable to open in read/write mode \''
			              .$this->_tarname.'\'');
            return false;
        }

        return true;
    }
    // }}}

    // {{{ _close()
    function _close()
    {
        //if (isset($this->_file)) {
        if (is_resource($this->_file)) {
            if ($this->_compress_type == 'gz')
                @gzclose($this->_file);
            else if ($this->_compress_type == 'bz2')
                @bzclose($this->_file);
            else if ($this->_compress_type == 'none')
                @fclose($this->_file);
            else
                $this->_error('Unknown or missing compression type ('
				              .$this->_compress_type.')');

            $this->_file = 0;
        }

        // ----- Look if a local copy need to be erase
        // Note that it might be interesting to keep the url for a time : ToDo
        if ($this->_temp_tarname != '') {
            @unlink($this->_temp_tarname);
            $this->_temp_tarname = '';
        }

        return true;
    }
    // }}}

    // {{{ _cleanFile()
    function _cleanFile()
    {
        $this->_close();

        // ----- Look for a local copy
        if ($this->_temp_tarname != '') {
            // ----- Remove the local copy but not the remote tarname
            @unlink($this->_temp_tarname);
            $this->_temp_tarname = '';
        } else {
            // ----- Remove the local tarname file
            @unlink($this->_tarname);
        }
        $this->_tarname = '';

        return true;
    }
    // }}}

    // {{{ _writeBlock()
    function _writeBlock($p_binary_data, $p_len=null)
    {
      if (is_resource($this->_file)) {
          if ($p_len === null) {
              if ($this->_compress_type == 'gz')
                  @gzputs($this->_file, $p_binary_data);
              else if ($this->_compress_type == 'bz2')
                  @bzwrite($this->_file, $p_binary_data);
              else if ($this->_compress_type == 'none')
                  @fputs($this->_file, $p_binary_data);
              else
                  $this->_error('Unknown or missing compression type ('
				                .$this->_compress_type.')');
          } else {
              if ($this->_compress_type == 'gz')
                  @gzputs($this->_file, $p_binary_data, $p_len);
              else if ($this->_compress_type == 'bz2')
                  @bzwrite($this->_file, $p_binary_data, $p_len);
              else if ($this->_compress_type == 'none')
                  @fputs($this->_file, $p_binary_data, $p_len);
              else
                  $this->_error('Unknown or missing compression type ('
				                .$this->_compress_type.')');

          }
      }
      return true;
    }
    // }}}

    // {{{ _readBlock()
    function _readBlock()
    {
      $v_block = null;
      if (is_resource($this->_file)) {
          if ($this->_compress_type == 'gz')
              $v_block = @gzread($this->_file, 512);
          else if ($this->_compress_type == 'bz2')
              $v_block = @bzread($this->_file, 512);
          else if ($this->_compress_type == 'none')
              $v_block = @fread($this->_file, 512);
          else
              $this->_error('Unknown or missing compression type ('
			                .$this->_compress_type.')');
      }
      return $v_block;
    }
    // }}}

    // {{{ _jumpBlock()
    function _jumpBlock($p_len=null)
    {
      if (is_resource($this->_file)) {
          if ($p_len === null)
              $p_len = 1;

          if ($this->_compress_type == 'gz') {
              @gzseek($this->_file, gztell($this->_file)+($p_len*512));
          }
          else if ($this->_compress_type == 'bz2') {
              // ----- Replace missing bztell() and bzseek()
              for ($i=0; $i<$p_len; $i++)
                  $this->_readBlock();
          } else if ($this->_compress_type == 'none')
              @fseek($this->_file, ftell($this->_file)+($p_len*512));
          else
              $this->_error('Unknown or missing compression type ('
			                .$this->_compress_type.')');

      }
      return true;
    }
    // }}}

    // {{{ _writeFooter()
    function _writeFooter()
    {
      if (is_resource($this->_file)) {
          // ----- Write the last 0 filled block for end of archive
          $v_binary_data = pack('a1024', '');
          $this->_writeBlock($v_binary_data);
      }
      return true;
    }
    // }}}

    // {{{ _addList()
    function _addList($p_list, $p_add_dir, $p_remove_dir)
    {
      $v_result=true;
      $v_header = array();

      // ----- Remove potential windows directory separator
      $p_add_dir = $this->_translateWinPath($p_add_dir);
      $p_remove_dir = $this->_translateWinPath($p_remove_dir, false);

      if (!$this->_file) {
          $this->_error('Invalid file descriptor');
          return false;
      }

      if (sizeof($p_list) == 0)
          return true;

      foreach ($p_list as $v_filename) {
          if (!$v_result) {
              break;
          }

        // ----- Skip the current tar name
        if ($v_filename == $this->_tarname)
            continue;

        if ($v_filename == '')
            continue;

        if (!file_exists($v_filename)) {
            $this->_warning("File '$v_filename' does not exist");
            continue;
        }

        // ----- Add the file or directory header
        if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir))
            return false;

        if (@is_dir($v_filename)) {
            if (!($p_hdir = opendir($v_filename))) {
                $this->_warning("Directory '$v_filename' can not be read");
                continue;
            }
            while (false !== ($p_hitem = readdir($p_hdir))) {
                if (($p_hitem != '.') && ($p_hitem != '..')) {
                    if ($v_filename != ".")
                        $p_temp_list[0] = $v_filename.'/'.$p_hitem;
                    else
                        $p_temp_list[0] = $p_hitem;

                    $v_result = $this->_addList($p_temp_list,
					                            $p_add_dir,
												$p_remove_dir);
                }
            }

            unset($p_temp_list);
            unset($p_hdir);
            unset($p_hitem);
        }
      }

      return $v_result;
    }
    // }}}

    // {{{ _addFile()
    function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir)
    {
      if (!$this->_file) {
          $this->_error('Invalid file descriptor');
          return false;
      }

      if ($p_filename == '') {
          $this->_error('Invalid file name');
          return false;
      }

      // ----- Calculate the stored filename
      $p_filename = $this->_translateWinPath($p_filename, false);;
      $v_stored_filename = $p_filename;
      if (strcmp($p_filename, $p_remove_dir) == 0) {
          return true;
      }
      if ($p_remove_dir != '') {
          if (substr($p_remove_dir, -1) != '/')
              $p_remove_dir .= '/';

          if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir)
              $v_stored_filename = substr($p_filename, strlen($p_remove_dir));
      }
      $v_stored_filename = $this->_translateWinPath($v_stored_filename);
      if ($p_add_dir != '') {
          if (substr($p_add_dir, -1) == '/')
              $v_stored_filename = $p_add_dir.$v_stored_filename;
          else
              $v_stored_filename = $p_add_dir.'/'.$v_stored_filename;
      }

      $v_stored_filename = $this->_pathReduction($v_stored_filename);

      if ($this->_isArchive($p_filename)) {
          if (($v_file = @fopen($p_filename, "rb")) == 0) {
              $this->_warning("Unable to open file '".$p_filename
			                  ."' in binary read mode");
              return true;
          }

          if (!$this->_writeHeader($p_filename, $v_stored_filename))
              return false;

          while (($v_buffer = fread($v_file, 512)) != '') {
              $v_binary_data = pack("a512", "$v_buffer");
              $this->_writeBlock($v_binary_data);
          }

          fclose($v_file);

      } else {
          // ----- Only header for dir
          if (!$this->_writeHeader($p_filename, $v_stored_filename))
              return false;
      }

      return true;
    }
    // }}}

    // {{{ _addString()
    function _addString($p_filename, $p_string)
    {
      if (!$this->_file) {
          $this->_error('Invalid file descriptor');
          return false;
      }

      if ($p_filename == '') {
          $this->_error('Invalid file name');
          return false;
      }

      // ----- Calculate the stored filename
      $p_filename = $this->_translateWinPath($p_filename, false);;

      if (!$this->_writeHeaderBlock($p_filename, strlen($p_string),
	                                  time(), 384, "", 0, 0))
          return false;

      $i=0;
      while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') {
          $v_binary_data = pack("a512", $v_buffer);
          $this->_writeBlock($v_binary_data);
      }

      return true;
    }
    // }}}

    // {{{ _writeHeader()
    function _writeHeader($p_filename, $p_stored_filename)
    {
        if ($p_stored_filename == '')
            $p_stored_filename = $p_filename;
        $v_reduce_filename = $this->_pathReduction($p_stored_filename);

        if (strlen($v_reduce_filename) > 99) {
          if (!$this->_writeLongHeader($v_reduce_filename))
            return false;
        }

        $v_info = stat($p_filename);
        $v_uid = sprintf("%6s ", DecOct($v_info[4]));
        $v_gid = sprintf("%6s ", DecOct($v_info[5]));
        $v_perms = sprintf("%6s ", DecOct(fileperms($p_filename)));

        $v_mtime = sprintf("%11s", DecOct(filemtime($p_filename)));

        if (@is_dir($p_filename)) {
          $v_typeflag = "5";
          $v_size = sprintf("%11s ", DecOct(0));
        } else {
          $v_typeflag = '';
          clearstatcache();
          $v_size = sprintf("%11s ", DecOct(filesize($p_filename)));
        }

        $v_linkname = '';

        $v_magic = '';

        $v_version = '';

        $v_uname = '';

        $v_gname = '';

        $v_devmajor = '';

        $v_devminor = '';

        $v_prefix = '';

        $v_binary_data_first = pack("a100a8a8a8a12A12",
		                            $v_reduce_filename, $v_perms, $v_uid,
									$v_gid, $v_size, $v_mtime);
        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
		                           $v_typeflag, $v_linkname, $v_magic,
								   $v_version, $v_uname, $v_gname,
								   $v_devmajor, $v_devminor, $v_prefix, '');

        // ----- Calculate the checksum
        $v_checksum = 0;
        // ..... First part of the header
        for ($i=0; $i<148; $i++)
            $v_checksum += ord(substr($v_binary_data_first,$i,1));
        // ..... Ignore the checksum value and replace it by ' ' (space)
        for ($i=148; $i<156; $i++)
            $v_checksum += ord(' ');
        // ..... Last part of the header
        for ($i=156, $j=0; $i<512; $i++, $j++)
            $v_checksum += ord(substr($v_binary_data_last,$j,1));

        // ----- Write the first 148 bytes of the header in the archive
        $this->_writeBlock($v_binary_data_first, 148);

        // ----- Write the calculated checksum
        $v_checksum = sprintf("%6s ", DecOct($v_checksum));
        $v_binary_data = pack("a8", $v_checksum);
        $this->_writeBlock($v_binary_data, 8);

        // ----- Write the last 356 bytes of the header in the archive
        $this->_writeBlock($v_binary_data_last, 356);

        return true;
    }
    // }}}

    // {{{ _writeHeaderBlock()
    function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0,
	                           $p_type='', $p_uid=0, $p_gid=0)
    {
        $p_filename = $this->_pathReduction($p_filename);

        if (strlen($p_filename) > 99) {
          if (!$this->_writeLongHeader($p_filename))
            return false;
        }

        if ($p_type == "5") {
          $v_size = sprintf("%11s ", DecOct(0));
        } else {
          $v_size = sprintf("%11s ", DecOct($p_size));
        }

        $v_uid = sprintf("%6s ", DecOct($p_uid));
        $v_gid = sprintf("%6s ", DecOct($p_gid));
        $v_perms = sprintf("%6s ", DecOct($p_perms));

        $v_mtime = sprintf("%11s", DecOct($p_mtime));

        $v_linkname = '';

        $v_magic = '';

        $v_version = '';

        $v_uname = '';

        $v_gname = '';

        $v_devmajor = '';

        $v_devminor = '';

        $v_prefix = '';

        $v_binary_data_first = pack("a100a8a8a8a12A12",
		                            $p_filename, $v_perms, $v_uid, $v_gid,
									$v_size, $v_mtime);
        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
		                           $p_type, $v_linkname, $v_magic,
								   $v_version, $v_uname, $v_gname,
								   $v_devmajor, $v_devminor, $v_prefix, '');

        // ----- Calculate the checksum
        $v_checksum = 0;
        // ..... First part of the header
        for ($i=0; $i<148; $i++)
            $v_checksum += ord(substr($v_binary_data_first,$i,1));
        // ..... Ignore the checksum value and replace it by ' ' (space)
        for ($i=148; $i<156; $i++)
            $v_checksum += ord(' ');
        // ..... Last part of the header
        for ($i=156, $j=0; $i<512; $i++, $j++)
            $v_checksum += ord(substr($v_binary_data_last,$j,1));

        // ----- Write the first 148 bytes of the header in the archive
        $this->_writeBlock($v_binary_data_first, 148);

        // ----- Write the calculated checksum
        $v_checksum = sprintf("%6s ", DecOct($v_checksum));
        $v_binary_data = pack("a8", $v_checksum);
        $this->_writeBlock($v_binary_data, 8);

        // ----- Write the last 356 bytes of the header in the archive
        $this->_writeBlock($v_binary_data_last, 356);

        return true;
    }
    // }}}

    // {{{ _writeLongHeader()
    function _writeLongHeader($p_filename)
    {
        $v_size = sprintf("%11s ", DecOct(strlen($p_filename)));

        $v_typeflag = 'L';

        $v_linkname = '';

        $v_magic = '';

        $v_version = '';

        $v_uname = '';

        $v_gname = '';

        $v_devmajor = '';

        $v_devminor = '';

        $v_prefix = '';

        $v_binary_data_first = pack("a100a8a8a8a12A12",
		                            '././@LongLink', 0, 0, 0, $v_size, 0);
        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
		                           $v_typeflag, $v_linkname, $v_magic,
								   $v_version, $v_uname, $v_gname,
								   $v_devmajor, $v_devminor, $v_prefix, '');

        // ----- Calculate the checksum
        $v_checksum = 0;
        // ..... First part of the header
        for ($i=0; $i<148; $i++)
            $v_checksum += ord(substr($v_binary_data_first,$i,1));
        // ..... Ignore the checksum value and replace it by ' ' (space)
        for ($i=148; $i<156; $i++)
            $v_checksum += ord(' ');
        // ..... Last part of the header
        for ($i=156, $j=0; $i<512; $i++, $j++)
            $v_checksum += ord(substr($v_binary_data_last,$j,1));

        // ----- Write the first 148 bytes of the header in the archive
        $this->_writeBlock($v_binary_data_first, 148);

        // ----- Write the calculated checksum
        $v_checksum = sprintf("%6s ", DecOct($v_checksum));
        $v_binary_data = pack("a8", $v_checksum);
        $this->_writeBlock($v_binary_data, 8);

        // ----- Write the last 356 bytes of the header in the archive
        $this->_writeBlock($v_binary_data_last, 356);

        // ----- Write the filename as content of the block
        $i=0;
        while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') {
            $v_binary_data = pack("a512", "$v_buffer");
            $this->_writeBlock($v_binary_data);
        }

        return true;
    }
    // }}}

    // {{{ _readHeader()
    function _readHeader($v_binary_data, &$v_header)
    {
        if (strlen($v_binary_data)==0) {
            $v_header['filename'] = '';
            return true;
        }

        if (strlen($v_binary_data) != 512) {
            $v_header['filename'] = '';
            $this->_error('Invalid block size : '.strlen($v_binary_data));
            return false;
        }

        if (!is_array($v_header)) {
            $v_header = array();
        }
        // ----- Calculate the checksum
        $v_checksum = 0;
        // ..... First part of the header
        for ($i=0; $i<148; $i++)
            $v_checksum+=ord(substr($v_binary_data,$i,1));
        // ..... Ignore the checksum value and replace it by ' ' (space)
        for ($i=148; $i<156; $i++)
            $v_checksum += ord(' ');
        // ..... Last part of the header
        for ($i=156; $i<512; $i++)
           $v_checksum+=ord(substr($v_binary_data,$i,1));

        $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/"
		                 ."a8checksum/a1typeflag/a100link/a6magic/a2version/"
						 ."a32uname/a32gname/a8devmajor/a8devminor",
						 $v_binary_data);

        // ----- Extract the checksum
        $v_header['checksum'] = OctDec(trim($v_data['checksum']));
        if ($v_header['checksum'] != $v_checksum) {
            $v_header['filename'] = '';

            // ----- Look for last block (empty block)
            if (($v_checksum == 256) && ($v_header['checksum'] == 0))
                return true;

            $this->_error('Invalid checksum for file "'.$v_data['filename']
			              .'" : '.$v_checksum.' calculated, '
						  .$v_header['checksum'].' expected');
            return false;
        }

        // ----- Extract the properties
        $v_header['filename'] = trim($v_data['filename']);
        if ($this->_maliciousFilename($v_header['filename'])) {
            $this->_error('Malicious .tar detected, file "' . $v_header['filename'] .
                '" will not install in desired directory tree');
            return false;
        }
        $v_header['mode'] = OctDec(trim($v_data['mode']));
        $v_header['uid'] = OctDec(trim($v_data['uid']));
        $v_header['gid'] = OctDec(trim($v_data['gid']));
        $v_header['size'] = OctDec(trim($v_data['size']));
        $v_header['mtime'] = OctDec(trim($v_data['mtime']));
        if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
          $v_header['size'] = 0;
        }
        $v_header['link'] = trim($v_data['link']);
        /* ----- All these fields are removed form the header because
		they do not carry interesting info
        $v_header[magic] = trim($v_data[magic]);
        $v_header[version] = trim($v_data[version]);
        $v_header[uname] = trim($v_data[uname]);
        $v_header[gname] = trim($v_data[gname]);
        $v_header[devmajor] = trim($v_data[devmajor]);
        $v_header[devminor] = trim($v_data[devminor]);
        */

        return true;
    }
    // }}}

    // {{{ _maliciousFilename()
    /**
     * Detect and report a malicious file name
     *
     * @param string $file
     * @return bool
     * @access private
     */
    function _maliciousFilename($file)
    {
        if (strpos($file, '/../') !== false) {
            return true;
        }
        if (strpos($file, '../') === 0) {
            return true;
        }
        return false;
    }
    // }}}

    // {{{ _readLongHeader()
    function _readLongHeader(&$v_header)
    {
      $v_filename = '';
      $n = floor($v_header['size']/512);
      for ($i=0; $i<$n; $i++) {
        $v_content = $this->_readBlock();
        $v_filename .= $v_content;
      }
      if (($v_header['size'] % 512) != 0) {
        $v_content = $this->_readBlock();
        $v_filename .= $v_content;
      }

      // ----- Read the next header
      $v_binary_data = $this->_readBlock();

      if (!$this->_readHeader($v_binary_data, $v_header))
        return false;

      $v_header['filename'] = $v_filename;
        if ($this->_maliciousFilename($v_filename)) {
            $this->_error('Malicious .tar detected, file "' . $v_filename .
                '" will not install in desired directory tree');
            return false;
      }

      return true;
    }
    // }}}

    // {{{ _extractInString()
    /**
    * This method extract from the archive one file identified by $p_filename.
    * The return value is a string with the file content, or NULL on error.
    * @param string $p_filename     The path of the file to extract in a string.
    * @return                       a string with the file content or NULL.
    * @access private
    */
    function _extractInString($p_filename)
    {
        $v_result_str = "";

        While (strlen($v_binary_data = $this->_readBlock()) != 0)
        {
          if (!$this->_readHeader($v_binary_data, $v_header))
            return NULL;

          if ($v_header['filename'] == '')
            continue;

          // ----- Look for long filename
          if ($v_header['typeflag'] == 'L') {
            if (!$this->_readLongHeader($v_header))
              return NULL;
          }

          if ($v_header['filename'] == $p_filename) {
              if ($v_header['typeflag'] == "5") {
                  $this->_error('Unable to extract in string a directory '
				                .'entry {'.$v_header['filename'].'}');
                  return NULL;
              } else {
                  $n = floor($v_header['size']/512);
                  for ($i=0; $i<$n; $i++) {
                      $v_result_str .= $this->_readBlock();
                  }
                  if (($v_header['size'] % 512) != 0) {
                      $v_content = $this->_readBlock();
                      $v_result_str .= substr($v_content, 0,
					                          ($v_header['size'] % 512));
                  }
                  return $v_result_str;
              }
          } else {
              $this->_jumpBlock(ceil(($v_header['size']/512)));
          }
        }

        return NULL;
    }
    // }}}

    // {{{ _extractList()
    function _extractList($p_path, &$p_list_detail, $p_mode,
	                      $p_file_list, $p_remove_path)
    {
    $v_result=true;
    $v_nb = 0;
    $v_extract_all = true;
    $v_listing = false;

    $p_path = $this->_translateWinPath($p_path, false);
    if ($p_path == '' || (substr($p_path, 0, 1) != '/'
	    && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) {
      $p_path = "./".$p_path;
    }
    $p_remove_path = $this->_translateWinPath($p_remove_path);

    // ----- Look for path to remove format (should end by /)
    if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/'))
      $p_remove_path .= '/';
    $p_remove_path_size = strlen($p_remove_path);

    switch ($p_mode) {
      case "complete" :
        $v_extract_all = TRUE;
        $v_listing = FALSE;
      break;
      case "partial" :
          $v_extract_all = FALSE;
          $v_listing = FALSE;
      break;
      case "list" :
          $v_extract_all = FALSE;
          $v_listing = TRUE;
      break;
      default :
        $this->_error('Invalid extract mode ('.$p_mode.')');
        return false;
    }

    clearstatcache();

    while (strlen($v_binary_data = $this->_readBlock()) != 0)
    {
      $v_extract_file = FALSE;
      $v_extraction_stopped = 0;

      if (!$this->_readHeader($v_binary_data, $v_header))
        return false;

      if ($v_header['filename'] == '') {
        continue;
      }

      // ----- Look for long filename
      if ($v_header['typeflag'] == 'L') {
        if (!$this->_readLongHeader($v_header))
          return false;
      }

      if ((!$v_extract_all) && (is_array($p_file_list))) {
        // ----- By default no unzip if the file is not found
        $v_extract_file = false;

        for ($i=0; $i<sizeof($p_file_list); $i++) {
          // ----- Look if it is a directory
          if (substr($p_file_list[$i], -1) == '/') {
            // ----- Look if the directory is in the filename path
            if ((strlen($v_header['filename']) > strlen($p_file_list[$i]))
			    && (substr($v_header['filename'], 0, strlen($p_file_list[$i]))
				    == $p_file_list[$i])) {
              $v_extract_file = TRUE;
              break;
            }
          }

          // ----- It is a file, so compare the file names
          elseif ($p_file_list[$i] == $v_header['filename']) {
            $v_extract_file = TRUE;
            break;
          }
        }
      } else {
        $v_extract_file = TRUE;
      }

      // ----- Look if this file need to be extracted
      if (($v_extract_file) && (!$v_listing))
      {
        if (($p_remove_path != '')
            && (substr($v_header['filename'], 0, $p_remove_path_size)
			    == $p_remove_path))
          $v_header['filename'] = substr($v_header['filename'],
		                                 $p_remove_path_size);
        if (($p_path != './') && ($p_path != '/')) {
          while (substr($p_path, -1) == '/')
            $p_path = substr($p_path, 0, strlen($p_path)-1);

          if (substr($v_header['filename'], 0, 1) == '/')
              $v_header['filename'] = $p_path.$v_header['filename'];
          else
            $v_header['filename'] = $p_path.'/'.$v_header['filename'];
        }
        if (file_exists($v_header['filename'])) {
          if (   (@is_dir($v_header['filename']))
		      && ($v_header['typeflag'] == '')) {
            $this->_error('File '.$v_header['filename']
			              .' already exists as a directory');
            return false;
          }
          if (   ($this->_isArchive($v_header['filename']))
		      && ($v_header['typeflag'] == "5")) {
            $this->_error('Directory '.$v_header['filename']
			              .' already exists as a file');
            return false;
          }
          if (!is_writeable($v_header['filename'])) {
            $this->_error('File '.$v_header['filename']
			              .' already exists and is write protected');
            return false;
          }
          if (filemtime($v_header['filename']) > $v_header['mtime']) {
            // To be completed : An error or silent no replace ?
          }
        }

        // ----- Check the directory availability and create it if necessary
        elseif (($v_result
		         = $this->_dirCheck(($v_header['typeflag'] == "5"
				                    ?$v_header['filename']
									:dirname($v_header['filename'])))) != 1) {
            $this->_error('Unable to create path for '.$v_header['filename']);
            return false;
        }

        if ($v_extract_file) {
          if ($v_header['typeflag'] == "5") {
            if (!@file_exists($v_header['filename'])) {
                if (!@mkdir($v_header['filename'], 0777)) {
                    $this->_error('Unable to create directory {'
					              .$v_header['filename'].'}');
                    return false;
                }
            }
          } elseif ($v_header['typeflag'] == "2") {
              if (!@symlink($v_header['link'], $v_header['filename'])) {
                  $this->_error('Unable to extract symbolic link {'
                                .$v_header['filename'].'}');
                  return false;
              }
          } else {
              if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) {
                  $this->_error('Error while opening {'.$v_header['filename']
				                .'} in write binary mode');
                  return false;
              } else {
                  $n = floor($v_header['size']/512);
                  for ($i=0; $i<$n; $i++) {
                      $v_content = $this->_readBlock();
                      fwrite($v_dest_file, $v_content, 512);
                  }
            if (($v_header['size'] % 512) != 0) {
              $v_content = $this->_readBlock();
              fwrite($v_dest_file, $v_content, ($v_header['size'] % 512));
            }

            @fclose($v_dest_file);

            // ----- Change the file mode, mtime
            @touch($v_header['filename'], $v_header['mtime']);
            if ($v_header['mode'] & 0111) {
                // make file executable, obey umask
                $mode = fileperms($v_header['filename']) | (~umask() & 0111);
                @chmod($v_header['filename'], $mode);
            }
          }

          // ----- Check the file size
          clearstatcache();
          if (filesize($v_header['filename']) != $v_header['size']) {
              $this->_error('Extracted file '.$v_header['filename']
			                .' does not have the correct file size \''
							.filesize($v_header['filename'])
							.'\' ('.$v_header['size']
							.' expected). Archive may be corrupted.');
              return false;
          }
          }
        } else {
          $this->_jumpBlock(ceil(($v_header['size']/512)));
        }
      } else {
          $this->_jumpBlock(ceil(($v_header['size']/512)));
      }

      /* TBC : Seems to be unused ...
      if ($this->_compress)
        $v_end_of_file = @gzeof($this->_file);
      else
        $v_end_of_file = @feof($this->_file);
        */

      if ($v_listing || $v_extract_file || $v_extraction_stopped) {
        // ----- Log extracted files
        if (($v_file_dir = dirname($v_header['filename']))
		    == $v_header['filename'])
          $v_file_dir = '';
        if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == ''))
          $v_file_dir = '/';

        $p_list_detail[$v_nb++] = $v_header;
        if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) {
            return true;
        }
      }
    }

        return true;
    }
    // }}}

    // {{{ _openAppend()
    function _openAppend()
    {
        if (filesize($this->_tarname) == 0)
          return $this->_openWrite();
          
        if ($this->_compress) {
            $this->_close();

            if (!@rename($this->_tarname, $this->_tarname.".tmp")) {
                $this->_error('Error while renaming \''.$this->_tarname
				              .'\' to temporary file \''.$this->_tarname
							  .'.tmp\'');
                return false;
            }

            if ($this->_compress_type == 'gz')
                $v_temp_tar = @gzopen($this->_tarname.".tmp", "rb");
            elseif ($this->_compress_type == 'bz2')
                $v_temp_tar = @bzopen($this->_tarname.".tmp", "rb");
                
            if ($v_temp_tar == 0) {
                $this->_error('Unable to open file \''.$this->_tarname
				              .'.tmp\' in binary read mode');
                @rename($this->_tarname.".tmp", $this->_tarname);
                return false;
            }

            if (!$this->_openWrite()) {
                @rename($this->_tarname.".tmp", $this->_tarname);
                return false;
            }

            if ($this->_compress_type == 'gz') {
                while (!@gzeof($v_temp_tar)) {
                    $v_buffer = @gzread($v_temp_tar, 512);
                    if ($v_buffer == ARCHIVE_TAR_END_BLOCK) {
                        // do not copy end blocks, we will re-make them
                        // after appending
                        continue;
                    }
                    $v_binary_data = pack("a512", $v_buffer);
                    $this->_writeBlock($v_binary_data);
                }

                @gzclose($v_temp_tar);
            }
            elseif ($this->_compress_type == 'bz2') {
                while (strlen($v_buffer = @bzread($v_temp_tar, 512)) > 0) {
                    if ($v_buffer == ARCHIVE_TAR_END_BLOCK) {
                        continue;
                    }
                    $v_binary_data = pack("a512", $v_buffer);
                    $this->_writeBlock($v_binary_data);
                }

                @bzclose($v_temp_tar);
            }

            if (!@unlink($this->_tarname.".tmp")) {
                $this->_error('Error while deleting temporary file \''
				              .$this->_tarname.'.tmp\'');
            }

        } else {
            // ----- For not compressed tar, just add files before the last
			//       one or two 512 bytes block
            if (!$this->_openReadWrite())
               return false;

            clearstatcache();
            $v_size = filesize($this->_tarname);

            // We might have zero, one or two end blocks.
            // The standard is two, but we should try to handle 
            // other cases.
            fseek($this->_file, $v_size - 1024);
            if (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
                fseek($this->_file, $v_size - 1024);
            }
            elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
                fseek($this->_file, $v_size - 512);    
            }
        }

        return true;
    }
    // }}}

    // {{{ _append()
    function _append($p_filelist, $p_add_dir='', $p_remove_dir='')
    {
        if (!$this->_openAppend())
            return false;
            
        if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir))
           $this->_writeFooter();

        $this->_close();

        return true;
    }
    // }}}

    // {{{ _dirCheck()

    /**
     * Check if a directory exists and create it (including parent
     * dirs) if not.
     *
     * @param string $p_dir directory to check
     *
     * @return bool TRUE if the directory exists or was created
     */
    function _dirCheck($p_dir)
    {
        clearstatcache();
        if ((@is_dir($p_dir)) || ($p_dir == ''))
            return true;

        $p_parent_dir = dirname($p_dir);

        if (($p_parent_dir != $p_dir) &&
            ($p_parent_dir != '') &&
            (!$this->_dirCheck($p_parent_dir)))
             return false;

        if (!@mkdir($p_dir, 0777)) {
            $this->_error("Unable to create directory '$p_dir'");
            return false;
        }

        return true;
    }

    // }}}

    // {{{ _pathReduction()

    /**
     * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar", 
     * rand emove double slashes.
     *
     * @param string $p_dir path to reduce
     *
     * @return string reduced path
     *
     * @access private
     *
     */
    function _pathReduction($p_dir)
    {
        $v_result = '';

        // ----- Look for not empty path
        if ($p_dir != '') {
            // ----- Explode path by directory names
            $v_list = explode('/', $p_dir);

            // ----- Study directories from last to first
            for ($i=sizeof($v_list)-1; $i>=0; $i--) {
                // ----- Look for current path
                if ($v_list[$i] == ".") {
                    // ----- Ignore this directory
                    // Should be the first $i=0, but no check is done
                }
                else if ($v_list[$i] == "..") {
                    // ----- Ignore it and ignore the $i-1
                    $i--;
                }
                else if (   ($v_list[$i] == '')
				         && ($i!=(sizeof($v_list)-1))
						 && ($i!=0)) {
                    // ----- Ignore only the double '//' in path,
                    // but not the first and last /
                } else {
                    $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?'/'
					            .$v_result:'');
                }
            }
        }
        $v_result = strtr($v_result, '\\', '/');
        return $v_result;
    }

    // }}}

    // {{{ _translateWinPath()
    function _translateWinPath($p_path, $p_remove_disk_letter=true)
    {
      if (defined('OS_WINDOWS') && OS_WINDOWS) {
          // ----- Look for potential disk letter
          if (   ($p_remove_disk_letter)
		      && (($v_position = strpos($p_path, ':')) != false)) {
              $p_path = substr($p_path, $v_position+1);
          }
          // ----- Change potential windows directory separator
          if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
              $p_path = strtr($p_path, '\\', '/');
          }
      }
      return $p_path;
    }
    // }}}

}
?>
package.xml100644   1750   1750       13604 10547623416   6451 <?xml version="1.0" encoding="UTF-8"?>
<package packagerversion="1.5.0RC2" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
 <name>Archive_Tar</name>
 <channel>pear.php.net</channel>
 <summary>Tar file management class</summary>
 <description>This class provides handling of tar files in PHP.
It supports creating, listing, extracting and adding to tar files.
Gzip support is available if PHP has the zlib extension built-in or
loaded. Bz2 compression is also supported with the bz2 extension loaded.</description>
 <lead>
  <name>Gregory Beaver</name>
  <user>cellog</user>
  <email>cellog@php.net</email>
  <active>yes</active>
 </lead>
 <lead>
  <name>Vincent Blavet</name>
  <user>vblavet</user>
  <email>vincent@phpconcept.net</email>
  <active>no</active>
 </lead>
 <helper>
  <name>Stig Bakken</name>
  <user>ssb</user>
  <email>stig@php.net</email>
  <active>no</active>
 </helper>
 <date>2007-01-05</date>
 <time>22:35:26</time>
 <version>
  <release>1.3.2</release>
  <api>1.3.2</api>
 </version>
 <stability>
  <release>stable</release>
  <api>stable</api>
 </stability>
 <license uri="http://www.php.net/license">PHP License</license>
 <notes>Correct Bug #4016
Remove duplicate remove error display with &apos;@&apos;
Correct Bug #3909 : Check existence of OS_WINDOWS constant
Correct Bug #5452 fix for &quot;lone zero block&quot; when untarring packages
Change filemode (from pear-core/Archive/Tar.php v.1.21)
Correct Bug #6486 Can not extract symlinks
Correct Bug #6933 Archive_Tar (Tar file management class) Directory traversal	
Correct Bug #8114 Files added on-the-fly not storing date
Correct Bug #9352 Bug on _dirCheck function over nfs path</notes>
 <contents>
  <dir name="/">
   <file baseinstalldir="/" md5sum="06409d39f4268a9aa9e2924c7f397a38" name="Archive/Tar.php" role="php" />
   <file baseinstalldir="/" md5sum="29b03715377b18b1fafcff98a99cc9a7" name="docs/Archive_Tar.txt" role="doc" />
  </dir>
 </contents>
 <compatible>
  <name>PEAR</name>
  <channel>pear.php.net</channel>
  <min>1.4.0</min>
  <max>1.5.0RC2</max>
 </compatible>
 <dependencies>
  <required>
   <php>
    <min>4.0.0</min>
   </php>
   <pearinstaller>
    <min>1.4.0b1</min>
   </pearinstaller>
  </required>
 </dependencies>
 <phprelease />
 <changelog>
  <release>
   <version>
    <release>1.3.1</release>
    <api>1.3.1</api>
   </version>
   <stability>
    <release>stable</release>
    <api>stable</api>
   </stability>
   <date>2005-03-17</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>Correct Bug #3855</notes>
  </release>
  <release>
   <version>
    <release>1.3.0</release>
    <api>1.3.0</api>
   </version>
   <stability>
    <release>stable</release>
    <api>stable</api>
   </stability>
   <date>2005-03-06</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>Bugs correction (2475, 2488, 2135, 2176)</notes>
  </release>
  <release>
   <version>
    <release>1.2</release>
    <api>1.2</api>
   </version>
   <stability>
    <release>stable</release>
    <api>stable</api>
   </stability>
   <date>2004-05-08</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>Add support for other separator than the space char and bug
	correction</notes>
  </release>
  <release>
   <version>
    <release>1.1</release>
    <api>1.1</api>
   </version>
   <stability>
    <release>stable</release>
    <api>stable</api>
   </stability>
   <date>2003-05-28</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>* Add support for BZ2 compression
* Add support for add and extract without using temporary files : methods addString() and extractInString()</notes>
  </release>
  <release>
   <version>
    <release>1.0</release>
    <api>1.0</api>
   </version>
   <stability>
    <release>stable</release>
    <api>stable</api>
   </stability>
   <date>2003-01-24</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>Change status to stable</notes>
  </release>
  <release>
   <version>
    <release>0.10-b1</release>
    <api>0.10-b1</api>
   </version>
   <stability>
    <release>beta</release>
    <api>beta</api>
   </stability>
   <date>2003-01-08</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>Add support for long filenames (greater than 99 characters)</notes>
  </release>
  <release>
   <version>
    <release>0.9</release>
    <api>0.9</api>
   </version>
   <stability>
    <release>stable</release>
    <api>stable</api>
   </stability>
   <date>2002-05-27</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>Auto-detect gzip&apos;ed files</notes>
  </release>
  <release>
   <version>
    <release>0.4</release>
    <api>0.4</api>
   </version>
   <stability>
    <release>stable</release>
    <api>stable</api>
   </stability>
   <date>2002-05-20</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>Windows bugfix: use forward slashes inside archives</notes>
  </release>
  <release>
   <version>
    <release>0.2</release>
    <api>0.2</api>
   </version>
   <stability>
    <release>stable</release>
    <api>stable</api>
   </stability>
   <date>2002-02-18</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>From initial commit to stable</notes>
  </release>
  <release>
   <version>
    <release>0.3</release>
    <api>0.3</api>
   </version>
   <stability>
    <release>stable</release>
    <api>stable</api>
   </stability>
   <date>2002-04-13</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>Windows bugfix: used wrong directory separators</notes>
  </release>
 </changelog>
</package>
Archive_Tar-1.3.2/Archive/Tar.php100644   1750   1750      167330 10547623416  12030 <?php
/* vim: set ts=4 sw=4: */
// +----------------------------------------------------------------------+
// | 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: Vincent Blavet <vincent@phpconcept.net>                      |
// +----------------------------------------------------------------------+
//
// $Id: Tar.php,v 1.39 2006/12/22 19:20:08 cellog Exp $

require_once 'PEAR.php';


define ('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
define ('ARCHIVE_TAR_END_BLOCK', pack("a512", ''));

/**
* Creates a (compressed) Tar archive
*
* @author   Vincent Blavet <vincent@phpconcept.net>
* @version  $Revision: 1.39 $
* @package  Archive
*/
class Archive_Tar extends PEAR
{
    /**
    * @var string Name of the Tar
    */
    var $_tarname='';

    /**
    * @var boolean if true, the Tar file will be gzipped
    */
    var $_compress=false;

    /**
    * @var string Type of compression : 'none', 'gz' or 'bz2'
    */
    var $_compress_type='none';

    /**
    * @var string Explode separator
    */
    var $_separator=' ';

    /**
    * @var file descriptor
    */
    var $_file=0;

    /**
    * @var string Local Tar name of a remote Tar (http:// or ftp://)
    */
    var $_temp_tarname='';

    // {{{ constructor
    /**
    * Archive_Tar Class constructor. This flavour of the constructor only
    * declare a new Archive_Tar object, identifying it by the name of the
    * tar file.
    * If the compress argument is set the tar will be read or created as a
    * gzip or bz2 compressed TAR file.
    *
    * @param    string  $p_tarname  The name of the tar archive to create
    * @param    string  $p_compress can be null, 'gz' or 'bz2'. This
    *                   parameter indicates if gzip or bz2 compression
    *                   is required.  For compatibility reason the
    *                   boolean value 'true' means 'gz'.
    * @access public
    */
    function Archive_Tar($p_tarname, $p_compress = null)
    {
        $this->PEAR();
        $this->_compress = false;
        $this->_compress_type = 'none';
        if (($p_compress === null) || ($p_compress == '')) {
            if (@file_exists($p_tarname)) {
                if ($fp = @fopen($p_tarname, "rb")) {
                    // look for gzip magic cookie
                    $data = fread($fp, 2);
                    fclose($fp);
                    if ($data == "\37\213") {
                        $this->_compress = true;
                        $this->_compress_type = 'gz';
                    // No sure it's enought for a magic code ....
                    } elseif ($data == "BZ") {
                        $this->_compress = true;
                        $this->_compress_type = 'bz2';
                    }
                }
            } else {
                // probably a remote file or some file accessible
                // through a stream interface
                if (substr($p_tarname, -2) == 'gz') {
                    $this->_compress = true;
                    $this->_compress_type = 'gz';
                } elseif ((substr($p_tarname, -3) == 'bz2') ||
                          (substr($p_tarname, -2) == 'bz')) {
                    $this->_compress = true;
                    $this->_compress_type = 'bz2';
                }
            }
        } else {
            if (($p_compress === true) || ($p_compress == 'gz')) {
                $this->_compress = true;
                $this->_compress_type = 'gz';
            } else if ($p_compress == 'bz2') {
                $this->_compress = true;
                $this->_compress_type = 'bz2';
            } else {
                die("Unsupported compression type '$p_compress'\n".
                    "Supported types are 'gz' and 'bz2'.\n");
                return false;
            }
        }
        $this->_tarname = $p_tarname;
        if ($this->_compress) { // assert zlib or bz2 extension support
            if ($this->_compress_type == 'gz')
                $extname = 'zlib';
            else if ($this->_compress_type == 'bz2')
                $extname = 'bz2';

            if (!extension_loaded($extname)) {
                PEAR::loadExtension($extname);
            }
            if (!extension_loaded($extname)) {
                die("The extension '$extname' couldn't be found.\n".
                    "Please make sure your version of PHP was built ".
                    "with '$extname' support.\n");
                return false;
            }
        }
    }
    // }}}

    // {{{ destructor
    function _Archive_Tar()
    {
        $this->_close();
        // ----- Look for a local copy to delete
        if ($this->_temp_tarname != '')
            @unlink($this->_temp_tarname);
        $this->_PEAR();
    }
    // }}}

    // {{{ create()
    /**
    * This method creates the archive file and add the files / directories
    * that are listed in $p_filelist.
    * If a file with the same name exist and is writable, it is replaced
    * by the new tar.
    * The method return false and a PEAR error text.
    * The $p_filelist parameter can be an array of string, each string
    * representing a filename or a directory name with their path if
    * needed. It can also be a single string with names separated by a
    * single blank.
    * For each directory added in the archive, the files and
    * sub-directories are also added.
    * See also createModify() method for more details.
    *
    * @param array  $p_filelist An array of filenames and directory names, or a
	*                           single string with names separated by a single
	*                           blank space.
    * @return                   true on success, false on error.
    * @see createModify()
    * @access public
    */
    function create($p_filelist)
    {
        return $this->createModify($p_filelist, '', '');
    }
    // }}}

    // {{{ add()
    /**
    * This method add the files / directories that are listed in $p_filelist in
    * the archive. If the archive does not exist it is created.
    * The method return false and a PEAR error text.
    * The files and directories listed are only added at the end of the archive,
    * even if a file with the same name is already archived.
    * See also createModify() method for more details.
    *
    * @param array  $p_filelist An array of filenames and directory names, or a
	*                           single string with names separated by a single
	*                           blank space.
    * @return                   true on success, false on error.
    * @see createModify()
    * @access public
    */
    function add($p_filelist)
    {
        return $this->addModify($p_filelist, '', '');
    }
    // }}}

    // {{{ extract()
    function extract($p_path='')
    {
        return $this->extractModify($p_path, '');
    }
    // }}}

    // {{{ listContent()
    function listContent()
    {
        $v_list_detail = array();

        if ($this->_openRead()) {
            if (!$this->_extractList('', $v_list_detail, "list", '', '')) {
                unset($v_list_detail);
                $v_list_detail = 0;
            }
            $this->_close();
        }

        return $v_list_detail;
    }
    // }}}

    // {{{ createModify()
    /**
    * This method creates the archive file and add the files / directories
    * that are listed in $p_filelist.
    * If the file already exists and is writable, it is replaced by the
    * new tar. It is a create and not an add. If the file exists and is
    * read-only or is a directory it is not replaced. The method return
    * false and a PEAR error text.
    * The $p_filelist parameter can be an array of string, each string
    * representing a filename or a directory name with their path if
    * needed. It can also be a single string with names separated by a
    * single blank.
    * The path indicated in $p_remove_dir will be removed from the
    * memorized path of each file / directory listed when this path
    * exists. By default nothing is removed (empty path '')
    * The path indicated in $p_add_dir will be added at the beginning of
    * the memorized path of each file / directory listed. However it can
    * be set to empty ''. The adding of a path is done after the removing
    * of path.
    * The path add/remove ability enables the user to prepare an archive
    * for extraction in a different path than the origin files are.
    * See also addModify() method for file adding properties.
    *
    * @param array  $p_filelist     An array of filenames and directory names,
	*                               or a single string with names separated by
	*                               a single blank space.
    * @param string $p_add_dir      A string which contains a path to be added
	*                               to the memorized path of each element in
	*                               the list.
    * @param string $p_remove_dir   A string which contains a path to be
	*                               removed from the memorized path of each
	*                               element in the list, when relevant.
    * @return boolean               true on success, false on error.
    * @access public
    * @see addModify()
    */
    function createModify($p_filelist, $p_add_dir, $p_remove_dir='')
    {
        $v_result = true;

        if (!$this->_openWrite())
            return false;

        if ($p_filelist != '') {
            if (is_array($p_filelist))
                $v_list = $p_filelist;
            elseif (is_string($p_filelist))
                $v_list = explode($this->_separator, $p_filelist);
            else {
                $this->_cleanFile();
                $this->_error('Invalid file list');
                return false;
            }

            $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir);
        }

        if ($v_result) {
            $this->_writeFooter();
            $this->_close();
        } else
            $this->_cleanFile();

        return $v_result;
    }
    // }}}

    // {{{ addModify()
    /**
    * This method add the files / directories listed in $p_filelist at the
    * end of the existing archive. If the archive does not yet exists it
    * is created.
    * The $p_filelist parameter can be an array of string, each string
    * representing a filename or a directory name with their path if
    * needed. It can also be a single string with names separated by a
    * single blank.
    * The path indicated in $p_remove_dir will be removed from the
    * memorized path of each file / directory listed when this path
    * exists. By default nothing is removed (empty path '')
    * The path indicated in $p_add_dir will be added at the beginning of
    * the memorized path of each file / directory listed. However it can
    * be set to empty ''. The adding of a path is done after the removing
    * of path.
    * The path add/remove ability enables the user to prepare an archive
    * for extraction in a different path than the origin files are.
    * If a file/dir is already in the archive it will only be added at the
    * end of the archive. There is no update of the existing archived
    * file/dir. However while extracting the archive, the last file will
    * replace the first one. This results in a none optimization of the
    * archive size.
    * If a file/dir does not exist the file/dir is ignored. However an
    * error text is send to PEAR error.
    * If a file/dir is not readable the file/dir is ignored. However an
    * error text is send to PEAR error.
    *
    * @param array      $p_filelist     An array of filenames and directory
	*                                   names, or a single string with names
	*                                   separated by a single blank space.
    * @param string     $p_add_dir      A string which contains a path to be
	*                                   added to the memorized path of each
	*                                   element in the list.
    * @param string     $p_remove_dir   A string which contains a path to be
	*                                   removed from the memorized path of
	*                                   each element in the list, when
    *                                   relevant.
    * @return                           true on success, false on error.
    * @access public
    */
    function addModify($p_filelist, $p_add_dir, $p_remove_dir='')
    {
        $v_result = true;

        if (!$this->_isArchive())
            $v_result = $this->createModify($p_filelist, $p_add_dir,
			                                $p_remove_dir);
        else {
            if (is_array($p_filelist))
                $v_list = $p_filelist;
            elseif (is_string($p_filelist))
                $v_list = explode($this->_separator, $p_filelist);
            else {
                $this->_error('Invalid file list');
                return false;
            }

            $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir);
        }

        return $v_result;
    }
    // }}}

    // {{{ addString()
    /**
    * This method add a single string as a file at the
    * end of the existing archive. If the archive does not yet exists it
    * is created.
    *
    * @param string     $p_filename     A string which contains the full
	*                                   filename path that will be associated
	*                                   with the string.
    * @param string     $p_string       The content of the file added in
	*                                   the archive.
    * @return                           true on success, false on error.
    * @access public
    */
    function addString($p_filename, $p_string)
    {
        $v_result = true;
        
        if (!$this->_isArchive()) {
            if (!$this->_openWrite()) {
                return false;
            }
            $this->_close();
        }
        
        if (!$this->_openAppend())
            return false;

        // Need to check the get back to the temporary file ? ....
        $v_result = $this->_addString($p_filename, $p_string);

        $this->_writeFooter();

        $this->_close();

        return $v_result;
    }
    // }}}

    // {{{ extractModify()
    /**
    * This method extract all the content of the archive in the directory
    * indicated by $p_path. When relevant the memorized path of the
    * files/dir can be modified by removing the $p_remove_path path at the
    * beginning of the file/dir path.
    * While extracting a file, if the directory path does not exists it is
    * created.
    * While extracting a file, if the file already exists it is replaced
    * without looking for last modification date.
    * While extracting a file, if the file already exists and is write
    * protected, the extraction is aborted.
    * While extracting a file, if a directory with the same name already
    * exists, the extraction is aborted.
    * While extracting a directory, if a file with the same name already
    * exists, the extraction is aborted.
    * While extracting a file/directory if the destination directory exist
    * and is write protected, or does not exist but can not be created,
    * the extraction is aborted.
    * If after extraction an extracted file does not show the correct
    * stored file size, the extraction is aborted.
    * When the extraction is aborted, a PEAR error text is set and false
    * is returned. However the result can be a partial extraction that may
    * need to be manually cleaned.
    *
    * @param string $p_path         The path of the directory where the
	*                               files/dir need to by extracted.
    * @param string $p_remove_path  Part of the memorized path that can be
	*                               removed if present at the beginning of
	*                               the file/dir path.
    * @return boolean               true on success, false on error.
    * @access public
    * @see extractList()
    */
    function extractModify($p_path, $p_remove_path)
    {
        $v_result = true;
        $v_list_detail = array();

        if ($v_result = $this->_openRead()) {
            $v_result = $this->_extractList($p_path, $v_list_detail,
			                                "complete", 0, $p_remove_path);
            $this->_close();
        }

        return $v_result;
    }
    // }}}

    // {{{ extractInString()
    /**
    * This method extract from the archive one file identified by $p_filename.
    * The return value is a string with the file content, or NULL on error.
    * @param string $p_filename     The path of the file to extract in a string.
    * @return                       a string with the file content or NULL.
    * @access public
    */
    function extractInString($p_filename)
    {
        if ($this->_openRead()) {
            $v_result = $this->_extractInString($p_filename);
            $this->_close();
        } else {
            $v_result = NULL;
        }

        return $v_result;
    }
    // }}}

    // {{{ extractList()
    /**
    * This method extract from the archive only the files indicated in the
    * $p_filelist. These files are extracted in the current directory or
    * in the directory indicated by the optional $p_path parameter.
    * If indicated the $p_remove_path can be used in the same way as it is
    * used in extractModify() method.
    * @param array  $p_filelist     An array of filenames and directory names,
	*                               or a single string with names separated
	*                               by a single blank space.
    * @param string $p_path         The path of the directory where the
	*                               files/dir need to by extracted.
    * @param string $p_remove_path  Part of the memorized path that can be
	*                               removed if present at the beginning of
	*                               the file/dir path.
    * @return                       true on success, false on error.
    * @access public
    * @see extractModify()
    */
    function extractList($p_filelist, $p_path='', $p_remove_path='')
    {
        $v_result = true;
        $v_list_detail = array();

        if (is_array($p_filelist))
            $v_list = $p_filelist;
        elseif (is_string($p_filelist))
            $v_list = explode($this->_separator, $p_filelist);
        else {
            $this->_error('Invalid string list');
            return false;
        }

        if ($v_result = $this->_openRead()) {
            $v_result = $this->_extractList($p_path, $v_list_detail, "partial",
			                                $v_list, $p_remove_path);
            $this->_close();
        }

        return $v_result;
    }
    // }}}

    // {{{ setAttribute()
    /**
    * This method set specific attributes of the archive. It uses a variable
    * list of parameters, in the format attribute code + attribute values :
    * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ',');
    * @param mixed $argv            variable list of attributes and values
    * @return                       true on success, false on error.
    * @access public
    */
    function setAttribute()
    {
        $v_result = true;
        
        // ----- Get the number of variable list of arguments
        if (($v_size = func_num_args()) == 0) {
            return true;
        }
        
        // ----- Get the arguments
        $v_att_list = &func_get_args();

        // ----- Read the attributes
        $i=0;
        while ($i<$v_size) {

            // ----- Look for next option
            switch ($v_att_list[$i]) {
                // ----- Look for options that request a string value
                case ARCHIVE_TAR_ATT_SEPARATOR :
                    // ----- Check the number of parameters
                    if (($i+1) >= $v_size) {
                        $this->_error('Invalid number of parameters for '
						              .'attribute ARCHIVE_TAR_ATT_SEPARATOR');
                        return false;
                    }

                    // ----- Get the value
                    $this->_separator = $v_att_list[$i+1];
                    $i++;
                break;

                default :
                    $this->_error('Unknow attribute code '.$v_att_list[$i].'');
                    return false;
            }

            // ----- Next attribute
            $i++;
        }

        return $v_result;
    }
    // }}}

    // {{{ _error()
    function _error($p_message)
    {
        // ----- To be completed
        $this->raiseError($p_message);
    }
    // }}}

    // {{{ _warning()
    function _warning($p_message)
    {
        // ----- To be completed
        $this->raiseError($p_message);
    }
    // }}}

    // {{{ _isArchive()
    function _isArchive($p_filename=NULL)
    {
        if ($p_filename == NULL) {
            $p_filename = $this->_tarname;
        }
        clearstatcache();
        return @is_file($p_filename);
    }
    // }}}

    // {{{ _openWrite()
    function _openWrite()
    {
        if ($this->_compress_type == 'gz')
            $this->_file = @gzopen($this->_tarname, "wb9");
        else if ($this->_compress_type == 'bz2')
            $this->_file = @bzopen($this->_tarname, "wb");
        else if ($this->_compress_type == 'none')
            $this->_file = @fopen($this->_tarname, "wb");
        else
            $this->_error('Unknown or missing compression type ('
			              .$this->_compress_type.')');

        if ($this->_file == 0) {
            $this->_error('Unable to open in write mode \''
			              .$this->_tarname.'\'');
            return false;
        }

        return true;
    }
    // }}}

    // {{{ _openRead()
    function _openRead()
    {
        if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {

          // ----- Look if a local copy need to be done
          if ($this->_temp_tarname == '') {
              $this->_temp_tarname = uniqid('tar').'.tmp';
              if (!$v_file_from = @fopen($this->_tarname, 'rb')) {
                $this->_error('Unable to open in read mode \''
				              .$this->_tarname.'\'');
                $this->_temp_tarname = '';
                return false;
              }
              if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) {
                $this->_error('Unable to open in write mode \''
				              .$this->_temp_tarname.'\'');
                $this->_temp_tarname = '';
                return false;
              }
              while ($v_data = @fread($v_file_from, 1024))
                  @fwrite($v_file_to, $v_data);
              @fclose($v_file_from);
              @fclose($v_file_to);
          }

          // ----- File to open if the local copy
          $v_filename = $this->_temp_tarname;

        } else
          // ----- File to open if the normal Tar file
          $v_filename = $this->_tarname;

        if ($this->_compress_type == 'gz')
            $this->_file = @gzopen($v_filename, "rb");
        else if ($this->_compress_type == 'bz2')
            $this->_file = @bzopen($v_filename, "rb");
        else if ($this->_compress_type == 'none')
            $this->_file = @fopen($v_filename, "rb");
        else
            $this->_error('Unknown or missing compression type ('
			              .$this->_compress_type.')');

        if ($this->_file == 0) {
            $this->_error('Unable to open in read mode \''.$v_filename.'\'');
            return false;
        }

        return true;
    }
    // }}}

    // {{{ _openReadWrite()
    function _openReadWrite()
    {
        if ($this->_compress_type == 'gz')
            $this->_file = @gzopen($this->_tarname, "r+b");
        else if ($this->_compress_type == 'bz2')
            $this->_file = @bzopen($this->_tarname, "r+b");
        else if ($this->_compress_type == 'none')
            $this->_file = @fopen($this->_tarname, "r+b");
        else
            $this->_error('Unknown or missing compression type ('
			              .$this->_compress_type.')');

        if ($this->_file == 0) {
            $this->_error('Unable to open in read/write mode \''
			              .$this->_tarname.'\'');
            return false;
        }

        return true;
    }
    // }}}

    // {{{ _close()
    function _close()
    {
        //if (isset($this->_file)) {
        if (is_resource($this->_file)) {
            if ($this->_compress_type == 'gz')
                @gzclose($this->_file);
            else if ($this->_compress_type == 'bz2')
                @bzclose($this->_file);
            else if ($this->_compress_type == 'none')
                @fclose($this->_file);
            else
                $this->_error('Unknown or missing compression type ('
				              .$this->_compress_type.')');

            $this->_file = 0;
        }

        // ----- Look if a local copy need to be erase
        // Note that it might be interesting to keep the url for a time : ToDo
        if ($this->_temp_tarname != '') {
            @unlink($this->_temp_tarname);
            $this->_temp_tarname = '';
        }

        return true;
    }
    // }}}

    // {{{ _cleanFile()
    function _cleanFile()
    {
        $this->_close();

        // ----- Look for a local copy
        if ($this->_temp_tarname != '') {
            // ----- Remove the local copy but not the remote tarname
            @unlink($this->_temp_tarname);
            $this->_temp_tarname = '';
        } else {
            // ----- Remove the local tarname file
            @unlink($this->_tarname);
        }
        $this->_tarname = '';

        return true;
    }
    // }}}

    // {{{ _writeBlock()
    function _writeBlock($p_binary_data, $p_len=null)
    {
      if (is_resource($this->_file)) {
          if ($p_len === null) {
              if ($this->_compress_type == 'gz')
                  @gzputs($this->_file, $p_binary_data);
              else if ($this->_compress_type == 'bz2')
                  @bzwrite($this->_file, $p_binary_data);
              else if ($this->_compress_type == 'none')
                  @fputs($this->_file, $p_binary_data);
              else
                  $this->_error('Unknown or missing compression type ('
				                .$this->_compress_type.')');
          } else {
              if ($this->_compress_type == 'gz')
                  @gzputs($this->_file, $p_binary_data, $p_len);
              else if ($this->_compress_type == 'bz2')
                  @bzwrite($this->_file, $p_binary_data, $p_len);
              else if ($this->_compress_type == 'none')
                  @fputs($this->_file, $p_binary_data, $p_len);
              else
                  $this->_error('Unknown or missing compression type ('
				                .$this->_compress_type.')');

          }
      }
      return true;
    }
    // }}}

    // {{{ _readBlock()
    function _readBlock()
    {
      $v_block = null;
      if (is_resource($this->_file)) {
          if ($this->_compress_type == 'gz')
              $v_block = @gzread($this->_file, 512);
          else if ($this->_compress_type == 'bz2')
              $v_block = @bzread($this->_file, 512);
          else if ($this->_compress_type == 'none')
              $v_block = @fread($this->_file, 512);
          else
              $this->_error('Unknown or missing compression type ('
			                .$this->_compress_type.')');
      }
      return $v_block;
    }
    // }}}

    // {{{ _jumpBlock()
    function _jumpBlock($p_len=null)
    {
      if (is_resource($this->_file)) {
          if ($p_len === null)
              $p_len = 1;

          if ($this->_compress_type == 'gz') {
              @gzseek($this->_file, gztell($this->_file)+($p_len*512));
          }
          else if ($this->_compress_type == 'bz2') {
              // ----- Replace missing bztell() and bzseek()
              for ($i=0; $i<$p_len; $i++)
                  $this->_readBlock();
          } else if ($this->_compress_type == 'none')
              @fseek($this->_file, ftell($this->_file)+($p_len*512));
          else
              $this->_error('Unknown or missing compression type ('
			                .$this->_compress_type.')');

      }
      return true;
    }
    // }}}

    // {{{ _writeFooter()
    function _writeFooter()
    {
      if (is_resource($this->_file)) {
          // ----- Write the last 0 filled block for end of archive
          $v_binary_data = pack('a1024', '');
          $this->_writeBlock($v_binary_data);
      }
      return true;
    }
    // }}}

    // {{{ _addList()
    function _addList($p_list, $p_add_dir, $p_remove_dir)
    {
      $v_result=true;
      $v_header = array();

      // ----- Remove potential windows directory separator
      $p_add_dir = $this->_translateWinPath($p_add_dir);
      $p_remove_dir = $this->_translateWinPath($p_remove_dir, false);

      if (!$this->_file) {
          $this->_error('Invalid file descriptor');
          return false;
      }

      if (sizeof($p_list) == 0)
          return true;

      foreach ($p_list as $v_filename) {
          if (!$v_result) {
              break;
          }

        // ----- Skip the current tar name
        if ($v_filename == $this->_tarname)
            continue;

        if ($v_filename == '')
            continue;

        if (!file_exists($v_filename)) {
            $this->_warning("File '$v_filename' does not exist");
            continue;
        }

        // ----- Add the file or directory header
        if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir))
            return false;

        if (@is_dir($v_filename)) {
            if (!($p_hdir = opendir($v_filename))) {
                $this->_warning("Directory '$v_filename' can not be read");
                continue;
            }
            while (false !== ($p_hitem = readdir($p_hdir))) {
                if (($p_hitem != '.') && ($p_hitem != '..')) {
                    if ($v_filename != ".")
                        $p_temp_list[0] = $v_filename.'/'.$p_hitem;
                    else
                        $p_temp_list[0] = $p_hitem;

                    $v_result = $this->_addList($p_temp_list,
					                            $p_add_dir,
												$p_remove_dir);
                }
            }

            unset($p_temp_list);
            unset($p_hdir);
            unset($p_hitem);
        }
      }

      return $v_result;
    }
    // }}}

    // {{{ _addFile()
    function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir)
    {
      if (!$this->_file) {
          $this->_error('Invalid file descriptor');
          return false;
      }

      if ($p_filename == '') {
          $this->_error('Invalid file name');
          return false;
      }

      // ----- Calculate the stored filename
      $p_filename = $this->_translateWinPath($p_filename, false);;
      $v_stored_filename = $p_filename;
      if (strcmp($p_filename, $p_remove_dir) == 0) {
          return true;
      }
      if ($p_remove_dir != '') {
          if (substr($p_remove_dir, -1) != '/')
              $p_remove_dir .= '/';

          if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir)
              $v_stored_filename = substr($p_filename, strlen($p_remove_dir));
      }
      $v_stored_filename = $this->_translateWinPath($v_stored_filename);
      if ($p_add_dir != '') {
          if (substr($p_add_dir, -1) == '/')
              $v_stored_filename = $p_add_dir.$v_stored_filename;
          else
              $v_stored_filename = $p_add_dir.'/'.$v_stored_filename;
      }

      $v_stored_filename = $this->_pathReduction($v_stored_filename);

      if ($this->_isArchive($p_filename)) {
          if (($v_file = @fopen($p_filename, "rb")) == 0) {
              $this->_warning("Unable to open file '".$p_filename
			                  ."' in binary read mode");
              return true;
          }

          if (!$this->_writeHeader($p_filename, $v_stored_filename))
              return false;

          while (($v_buffer = fread($v_file, 512)) != '') {
              $v_binary_data = pack("a512", "$v_buffer");
              $this->_writeBlock($v_binary_data);
          }

          fclose($v_file);

      } else {
          // ----- Only header for dir
          if (!$this->_writeHeader($p_filename, $v_stored_filename))
              return false;
      }

      return true;
    }
    // }}}

    // {{{ _addString()
    function _addString($p_filename, $p_string)
    {
      if (!$this->_file) {
          $this->_error('Invalid file descriptor');
          return false;
      }

      if ($p_filename == '') {
          $this->_error('Invalid file name');
          return false;
      }

      // ----- Calculate the stored filename
      $p_filename = $this->_translateWinPath($p_filename, false);;

      if (!$this->_writeHeaderBlock($p_filename, strlen($p_string),
	                                  time(), 384, "", 0, 0))
          return false;

      $i=0;
      while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') {
          $v_binary_data = pack("a512", $v_buffer);
          $this->_writeBlock($v_binary_data);
      }

      return true;
    }
    // }}}

    // {{{ _writeHeader()
    function _writeHeader($p_filename, $p_stored_filename)
    {
        if ($p_stored_filename == '')
            $p_stored_filename = $p_filename;
        $v_reduce_filename = $this->_pathReduction($p_stored_filename);

        if (strlen($v_reduce_filename) > 99) {
          if (!$this->_writeLongHeader($v_reduce_filename))
            return false;
        }

        $v_info = stat($p_filename);
        $v_uid = sprintf("%6s ", DecOct($v_info[4]));
        $v_gid = sprintf("%6s ", DecOct($v_info[5]));
        $v_perms = sprintf("%6s ", DecOct(fileperms($p_filename)));

        $v_mtime = sprintf("%11s", DecOct(filemtime($p_filename)));

        if (@is_dir($p_filename)) {
          $v_typeflag = "5";
          $v_size = sprintf("%11s ", DecOct(0));
        } else {
          $v_typeflag = '';
          clearstatcache();
          $v_size = sprintf("%11s ", DecOct(filesize($p_filename)));
        }

        $v_linkname = '';

        $v_magic = '';

        $v_version = '';

        $v_uname = '';

        $v_gname = '';

        $v_devmajor = '';

        $v_devminor = '';

        $v_prefix = '';

        $v_binary_data_first = pack("a100a8a8a8a12A12",
		                            $v_reduce_filename, $v_perms, $v_uid,
									$v_gid, $v_size, $v_mtime);
        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
		                           $v_typeflag, $v_linkname, $v_magic,
								   $v_version, $v_uname, $v_gname,
								   $v_devmajor, $v_devminor, $v_prefix, '');

        // ----- Calculate the checksum
        $v_checksum = 0;
        // ..... First part of the header
        for ($i=0; $i<148; $i++)
            $v_checksum += ord(substr($v_binary_data_first,$i,1));
        // ..... Ignore the checksum value and replace it by ' ' (space)
        for ($i=148; $i<156; $i++)
            $v_checksum += ord(' ');
        // ..... Last part of the header
        for ($i=156, $j=0; $i<512; $i++, $j++)
            $v_checksum += ord(substr($v_binary_data_last,$j,1));

        // ----- Write the first 148 bytes of the header in the archive
        $this->_writeBlock($v_binary_data_first, 148);

        // ----- Write the calculated checksum
        $v_checksum = sprintf("%6s ", DecOct($v_checksum));
        $v_binary_data = pack("a8", $v_checksum);
        $this->_writeBlock($v_binary_data, 8);

        // ----- Write the last 356 bytes of the header in the archive
        $this->_writeBlock($v_binary_data_last, 356);

        return true;
    }
    // }}}

    // {{{ _writeHeaderBlock()
    function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0,
	                           $p_type='', $p_uid=0, $p_gid=0)
    {
        $p_filename = $this->_pathReduction($p_filename);

        if (strlen($p_filename) > 99) {
          if (!$this->_writeLongHeader($p_filename))
            return false;
        }

        if ($p_type == "5") {
          $v_size = sprintf("%11s ", DecOct(0));
        } else {
          $v_size = sprintf("%11s ", DecOct($p_size));
        }

        $v_uid = sprintf("%6s ", DecOct($p_uid));
        $v_gid = sprintf("%6s ", DecOct($p_gid));
        $v_perms = sprintf("%6s ", DecOct($p_perms));

        $v_mtime = sprintf("%11s", DecOct($p_mtime));

        $v_linkname = '';

        $v_magic = '';

        $v_version = '';

        $v_uname = '';

        $v_gname = '';

        $v_devmajor = '';

        $v_devminor = '';

        $v_prefix = '';

        $v_binary_data_first = pack("a100a8a8a8a12A12",
		                            $p_filename, $v_perms, $v_uid, $v_gid,
									$v_size, $v_mtime);
        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
		                           $p_type, $v_linkname, $v_magic,
								   $v_version, $v_uname, $v_gname,
								   $v_devmajor, $v_devminor, $v_prefix, '');

        // ----- Calculate the checksum
        $v_checksum = 0;
        // ..... First part of the header
        for ($i=0; $i<148; $i++)
            $v_checksum += ord(substr($v_binary_data_first,$i,1));
        // ..... Ignore the checksum value and replace it by ' ' (space)
        for ($i=148; $i<156; $i++)
            $v_checksum += ord(' ');
        // ..... Last part of the header
        for ($i=156, $j=0; $i<512; $i++, $j++)
            $v_checksum += ord(substr($v_binary_data_last,$j,1));

        // ----- Write the first 148 bytes of the header in the archive
        $this->_writeBlock($v_binary_data_first, 148);

        // ----- Write the calculated checksum
        $v_checksum = sprintf("%6s ", DecOct($v_checksum));
        $v_binary_data = pack("a8", $v_checksum);
        $this->_writeBlock($v_binary_data, 8);

        // ----- Write the last 356 bytes of the header in the archive
        $this->_writeBlock($v_binary_data_last, 356);

        return true;
    }
    // }}}

    // {{{ _writeLongHeader()
    function _writeLongHeader($p_filename)
    {
        $v_size = sprintf("%11s ", DecOct(strlen($p_filename)));

        $v_typeflag = 'L';

        $v_linkname = '';

        $v_magic = '';

        $v_version = '';

        $v_uname = '';

        $v_gname = '';

        $v_devmajor = '';

        $v_devminor = '';

        $v_prefix = '';

        $v_binary_data_first = pack("a100a8a8a8a12A12",
		                            '././@LongLink', 0, 0, 0, $v_size, 0);
        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
		                           $v_typeflag, $v_linkname, $v_magic,
								   $v_version, $v_uname, $v_gname,
								   $v_devmajor, $v_devminor, $v_prefix, '');

        // ----- Calculate the checksum
        $v_checksum = 0;
        // ..... First part of the header
        for ($i=0; $i<148; $i++)
            $v_checksum += ord(substr($v_binary_data_first,$i,1));
        // ..... Ignore the checksum value and replace it by ' ' (space)
        for ($i=148; $i<156; $i++)
            $v_checksum += ord(' ');
        // ..... Last part of the header
        for ($i=156, $j=0; $i<512; $i++, $j++)
            $v_checksum += ord(substr($v_binary_data_last,$j,1));

        // ----- Write the first 148 bytes of the header in the archive
        $this->_writeBlock($v_binary_data_first, 148);

        // ----- Write the calculated checksum
        $v_checksum = sprintf("%6s ", DecOct($v_checksum));
        $v_binary_data = pack("a8", $v_checksum);
        $this->_writeBlock($v_binary_data, 8);

        // ----- Write the last 356 bytes of the header in the archive
        $this->_writeBlock($v_binary_data_last, 356);

        // ----- Write the filename as content of the block
        $i=0;
        while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') {
            $v_binary_data = pack("a512", "$v_buffer");
            $this->_writeBlock($v_binary_data);
        }

        return true;
    }
    // }}}

    // {{{ _readHeader()
    function _readHeader($v_binary_data, &$v_header)
    {
        if (strlen($v_binary_data)==0) {
            $v_header['filename'] = '';
            return true;
        }

        if (strlen($v_binary_data) != 512) {
            $v_header['filename'] = '';
            $this->_error('Invalid block size : '.strlen($v_binary_data));
            return false;
        }

        if (!is_array($v_header)) {
            $v_header = array();
        }
        // ----- Calculate the checksum
        $v_checksum = 0;
        // ..... First part of the header
        for ($i=0; $i<148; $i++)
            $v_checksum+=ord(substr($v_binary_data,$i,1));
        // ..... Ignore the checksum value and replace it by ' ' (space)
        for ($i=148; $i<156; $i++)
            $v_checksum += ord(' ');
        // ..... Last part of the header
        for ($i=156; $i<512; $i++)
           $v_checksum+=ord(substr($v_binary_data,$i,1));

        $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/"
		                 ."a8checksum/a1typeflag/a100link/a6magic/a2version/"
						 ."a32uname/a32gname/a8devmajor/a8devminor",
						 $v_binary_data);

        // ----- Extract the checksum
        $v_header['checksum'] = OctDec(trim($v_data['checksum']));
        if ($v_header['checksum'] != $v_checksum) {
            $v_header['filename'] = '';

            // ----- Look for last block (empty block)
            if (($v_checksum == 256) && ($v_header['checksum'] == 0))
                return true;

            $this->_error('Invalid checksum for file "'.$v_data['filename']
			              .'" : '.$v_checksum.' calculated, '
						  .$v_header['checksum'].' expected');
            return false;
        }

        // ----- Extract the properties
        $v_header['filename'] = trim($v_data['filename']);
        if ($this->_maliciousFilename($v_header['filename'])) {
            $this->_error('Malicious .tar detected, file "' . $v_header['filename'] .
                '" will not install in desired directory tree');
            return false;
        }
        $v_header['mode'] = OctDec(trim($v_data['mode']));
        $v_header['uid'] = OctDec(trim($v_data['uid']));
        $v_header['gid'] = OctDec(trim($v_data['gid']));
        $v_header['size'] = OctDec(trim($v_data['size']));
        $v_header['mtime'] = OctDec(trim($v_data['mtime']));
        if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
          $v_header['size'] = 0;
        }
        $v_header['link'] = trim($v_data['link']);
        /* ----- All these fields are removed form the header because
		they do not carry interesting info
        $v_header[magic] = trim($v_data[magic]);
        $v_header[version] = trim($v_data[version]);
        $v_header[uname] = trim($v_data[uname]);
        $v_header[gname] = trim($v_data[gname]);
        $v_header[devmajor] = trim($v_data[devmajor]);
        $v_header[devminor] = trim($v_data[devminor]);
        */

        return true;
    }
    // }}}

    // {{{ _maliciousFilename()
    /**
     * Detect and report a malicious file name
     *
     * @param string $file
     * @return bool
     * @access private
     */
    function _maliciousFilename($file)
    {
        if (strpos($file, '/../') !== false) {
            return true;
        }
        if (strpos($file, '../') === 0) {
            return true;
        }
        return false;
    }
    // }}}

    // {{{ _readLongHeader()
    function _readLongHeader(&$v_header)
    {
      $v_filename = '';
      $n = floor($v_header['size']/512);
      for ($i=0; $i<$n; $i++) {
        $v_content = $this->_readBlock();
        $v_filename .= $v_content;
      }
      if (($v_header['size'] % 512) != 0) {
        $v_content = $this->_readBlock();
        $v_filename .= $v_content;
      }

      // ----- Read the next header
      $v_binary_data = $this->_readBlock();

      if (!$this->_readHeader($v_binary_data, $v_header))
        return false;

      $v_header['filename'] = $v_filename;
        if ($this->_maliciousFilename($v_filename)) {
            $this->_error('Malicious .tar detected, file "' . $v_filename .
                '" will not install in desired directory tree');
            return false;
      }

      return true;
    }
    // }}}

    // {{{ _extractInString()
    /**
    * This method extract from the archive one file identified by $p_filename.
    * The return value is a string with the file content, or NULL on error.
    * @param string $p_filename     The path of the file to extract in a string.
    * @return                       a string with the file content or NULL.
    * @access private
    */
    function _extractInString($p_filename)
    {
        $v_result_str = "";

        While (strlen($v_binary_data = $this->_readBlock()) != 0)
        {
          if (!$this->_readHeader($v_binary_data, $v_header))
            return NULL;

          if ($v_header['filename'] == '')
            continue;

          // ----- Look for long filename
          if ($v_header['typeflag'] == 'L') {
            if (!$this->_readLongHeader($v_header))
              return NULL;
          }

          if ($v_header['filename'] == $p_filename) {
              if ($v_header['typeflag'] == "5") {
                  $this->_error('Unable to extract in string a directory '
				                .'entry {'.$v_header['filename'].'}');
                  return NULL;
              } else {
                  $n = floor($v_header['size']/512);
                  for ($i=0; $i<$n; $i++) {
                      $v_result_str .= $this->_readBlock();
                  }
                  if (($v_header['size'] % 512) != 0) {
                      $v_content = $this->_readBlock();
                      $v_result_str .= substr($v_content, 0,
					                          ($v_header['size'] % 512));
                  }
                  return $v_result_str;
              }
          } else {
              $this->_jumpBlock(ceil(($v_header['size']/512)));
          }
        }

        return NULL;
    }
    // }}}

    // {{{ _extractList()
    function _extractList($p_path, &$p_list_detail, $p_mode,
	                      $p_file_list, $p_remove_path)
    {
    $v_result=true;
    $v_nb = 0;
    $v_extract_all = true;
    $v_listing = false;

    $p_path = $this->_translateWinPath($p_path, false);
    if ($p_path == '' || (substr($p_path, 0, 1) != '/'
	    && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) {
      $p_path = "./".$p_path;
    }
    $p_remove_path = $this->_translateWinPath($p_remove_path);

    // ----- Look for path to remove format (should end by /)
    if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/'))
      $p_remove_path .= '/';
    $p_remove_path_size = strlen($p_remove_path);

    switch ($p_mode) {
      case "complete" :
        $v_extract_all = TRUE;
        $v_listing = FALSE;
      break;
      case "partial" :
          $v_extract_all = FALSE;
          $v_listing = FALSE;
      break;
      case "list" :
          $v_extract_all = FALSE;
          $v_listing = TRUE;
      break;
      default :
        $this->_error('Invalid extract mode ('.$p_mode.')');
        return false;
    }

    clearstatcache();

    while (strlen($v_binary_data = $this->_readBlock()) != 0)
    {
      $v_extract_file = FALSE;
      $v_extraction_stopped = 0;

      if (!$this->_readHeader($v_binary_data, $v_header))
        return false;

      if ($v_header['filename'] == '') {
        continue;
      }

      // ----- Look for long filename
      if ($v_header['typeflag'] == 'L') {
        if (!$this->_readLongHeader($v_header))
          return false;
      }

      if ((!$v_extract_all) && (is_array($p_file_list))) {
        // ----- By default no unzip if the file is not found
        $v_extract_file = false;

        for ($i=0; $i<sizeof($p_file_list); $i++) {
          // ----- Look if it is a directory
          if (substr($p_file_list[$i], -1) == '/') {
            // ----- Look if the directory is in the filename path
            if ((strlen($v_header['filename']) > strlen($p_file_list[$i]))
			    && (substr($v_header['filename'], 0, strlen($p_file_list[$i]))
				    == $p_file_list[$i])) {
              $v_extract_file = TRUE;
              break;
            }
          }

          // ----- It is a file, so compare the file names
          elseif ($p_file_list[$i] == $v_header['filename']) {
            $v_extract_file = TRUE;
            break;
          }
        }
      } else {
        $v_extract_file = TRUE;
      }

      // ----- Look if this file need to be extracted
      if (($v_extract_file) && (!$v_listing))
      {
        if (($p_remove_path != '')
            && (substr($v_header['filename'], 0, $p_remove_path_size)
			    == $p_remove_path))
          $v_header['filename'] = substr($v_header['filename'],
		                                 $p_remove_path_size);
        if (($p_path != './') && ($p_path != '/')) {
          while (substr($p_path, -1) == '/')
            $p_path = substr($p_path, 0, strlen($p_path)-1);

          if (substr($v_header['filename'], 0, 1) == '/')
              $v_header['filename'] = $p_path.$v_header['filename'];
          else
            $v_header['filename'] = $p_path.'/'.$v_header['filename'];
        }
        if (file_exists($v_header['filename'])) {
          if (   (@is_dir($v_header['filename']))
		      && ($v_header['typeflag'] == '')) {
            $this->_error('File '.$v_header['filename']
			              .' already exists as a directory');
            return false;
          }
          if (   ($this->_isArchive($v_header['filename']))
		      && ($v_header['typeflag'] == "5")) {
            $this->_error('Directory '.$v_header['filename']
			              .' already exists as a file');
            return false;
          }
          if (!is_writeable($v_header['filename'])) {
            $this->_error('File '.$v_header['filename']
			              .' already exists and is write protected');
            return false;
          }
          if (filemtime($v_header['filename']) > $v_header['mtime']) {
            // To be completed : An error or silent no replace ?
          }
        }

        // ----- Check the directory availability and create it if necessary
        elseif (($v_result
		         = $this->_dirCheck(($v_header['typeflag'] == "5"
				                    ?$v_header['filename']
									:dirname($v_header['filename'])))) != 1) {
            $this->_error('Unable to create path for '.$v_header['filename']);
            return false;
        }

        if ($v_extract_file) {
          if ($v_header['typeflag'] == "5") {
            if (!@file_exists($v_header['filename'])) {
                if (!@mkdir($v_header['filename'], 0777)) {
                    $this->_error('Unable to create directory {'
					              .$v_header['filename'].'}');
                    return false;
                }
            }
          } elseif ($v_header['typeflag'] == "2") {
              if (!@symlink($v_header['link'], $v_header['filename'])) {
                  $this->_error('Unable to extract symbolic link {'
                                .$v_header['filename'].'}');
                  return false;
              }
          } else {
              if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) {
                  $this->_error('Error while opening {'.$v_header['filename']
				                .'} in write binary mode');
                  return false;
              } else {
                  $n = floor($v_header['size']/512);
                  for ($i=0; $i<$n; $i++) {
                      $v_content = $this->_readBlock();
                      fwrite($v_dest_file, $v_content, 512);
                  }
            if (($v_header['size'] % 512) != 0) {
              $v_content = $this->_readBlock();
              fwrite($v_dest_file, $v_content, ($v_header['size'] % 512));
            }

            @fclose($v_dest_file);

            // ----- Change the file mode, mtime
            @touch($v_header['filename'], $v_header['mtime']);
            if ($v_header['mode'] & 0111) {
                // make file executable, obey umask
                $mode = fileperms($v_header['filename']) | (~umask() & 0111);
                @chmod($v_header['filename'], $mode);
            }
          }

          // ----- Check the file size
          clearstatcache();
          if (filesize($v_header['filename']) != $v_header['size']) {
              $this->_error('Extracted file '.$v_header['filename']
			                .' does not have the correct file size \''
							.filesize($v_header['filename'])
							.'\' ('.$v_header['size']
							.' expected). Archive may be corrupted.');
              return false;
          }
          }
        } else {
          $this->_jumpBlock(ceil(($v_header['size']/512)));
        }
      } else {
          $this->_jumpBlock(ceil(($v_header['size']/512)));
      }

      /* TBC : Seems to be unused ...
      if ($this->_compress)
        $v_end_of_file = @gzeof($this->_file);
      else
        $v_end_of_file = @feof($this->_file);
        */

      if ($v_listing || $v_extract_file || $v_extraction_stopped) {
        // ----- Log extracted files
        if (($v_file_dir = dirname($v_header['filename']))
		    == $v_header['filename'])
          $v_file_dir = '';
        if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == ''))
          $v_file_dir = '/';

        $p_list_detail[$v_nb++] = $v_header;
        if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) {
            return true;
        }
      }
    }

        return true;
    }
    // }}}

    // {{{ _openAppend()
    function _openAppend()
    {
        if (filesize($this->_tarname) == 0)
          return $this->_openWrite();
          
        if ($this->_compress) {
            $this->_close();

            if (!@rename($this->_tarname, $this->_tarname.".tmp")) {
                $this->_error('Error while renaming \''.$this->_tarname
				              .'\' to temporary file \''.$this->_tarname
							  .'.tmp\'');
                return false;
            }

            if ($this->_compress_type == 'gz')
                $v_temp_tar = @gzopen($this->_tarname.".tmp", "rb");
            elseif ($this->_compress_type == 'bz2')
                $v_temp_tar = @bzopen($this->_tarname.".tmp", "rb");
                
            if ($v_temp_tar == 0) {
                $this->_error('Unable to open file \''.$this->_tarname
				              .'.tmp\' in binary read mode');
                @rename($this->_tarname.".tmp", $this->_tarname);
                return false;
            }

            if (!$this->_openWrite()) {
                @rename($this->_tarname.".tmp", $this->_tarname);
                return false;
            }

            if ($this->_compress_type == 'gz') {
                while (!@gzeof($v_temp_tar)) {
                    $v_buffer = @gzread($v_temp_tar, 512);
                    if ($v_buffer == ARCHIVE_TAR_END_BLOCK) {
                        // do not copy end blocks, we will re-make them
                        // after appending
                        continue;
                    }
                    $v_binary_data = pack("a512", $v_buffer);
                    $this->_writeBlock($v_binary_data);
                }

                @gzclose($v_temp_tar);
            }
            elseif ($this->_compress_type == 'bz2') {
                while (strlen($v_buffer = @bzread($v_temp_tar, 512)) > 0) {
                    if ($v_buffer == ARCHIVE_TAR_END_BLOCK) {
                        continue;
                    }
                    $v_binary_data = pack("a512", $v_buffer);
                    $this->_writeBlock($v_binary_data);
                }

                @bzclose($v_temp_tar);
            }

            if (!@unlink($this->_tarname.".tmp")) {
                $this->_error('Error while deleting temporary file \''
				              .$this->_tarname.'.tmp\'');
            }

        } else {
            // ----- For not compressed tar, just add files before the last
			//       one or two 512 bytes block
            if (!$this->_openReadWrite())
               return false;

            clearstatcache();
            $v_size = filesize($this->_tarname);

            // We might have zero, one or two end blocks.
            // The standard is two, but we should try to handle 
            // other cases.
            fseek($this->_file, $v_size - 1024);
            if (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
                fseek($this->_file, $v_size - 1024);
            }
            elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
                fseek($this->_file, $v_size - 512);    
            }
        }

        return true;
    }
    // }}}

    // {{{ _append()
    function _append($p_filelist, $p_add_dir='', $p_remove_dir='')
    {
        if (!$this->_openAppend())
            return false;
            
        if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir))
           $this->_writeFooter();

        $this->_close();

        return true;
    }
    // }}}

    // {{{ _dirCheck()

    /**
     * Check if a directory exists and create it (including parent
     * dirs) if not.
     *
     * @param string $p_dir directory to check
     *
     * @return bool TRUE if the directory exists or was created
     */
    function _dirCheck($p_dir)
    {
        clearstatcache();
        if ((@is_dir($p_dir)) || ($p_dir == ''))
            return true;

        $p_parent_dir = dirname($p_dir);

        if (($p_parent_dir != $p_dir) &&
            ($p_parent_dir != '') &&
            (!$this->_dirCheck($p_parent_dir)))
             return false;

        if (!@mkdir($p_dir, 0777)) {
            $this->_error("Unable to create directory '$p_dir'");
            return false;
        }

        return true;
    }

    // }}}

    // {{{ _pathReduction()

    /**
     * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar", 
     * rand emove double slashes.
     *
     * @param string $p_dir path to reduce
     *
     * @return string reduced path
     *
     * @access private
     *
     */
    function _pathReduction($p_dir)
    {
        $v_result = '';

        // ----- Look for not empty path
        if ($p_dir != '') {
            // ----- Explode path by directory names
            $v_list = explode('/', $p_dir);

            // ----- Study directories from last to first
            for ($i=sizeof($v_list)-1; $i>=0; $i--) {
                // ----- Look for current path
                if ($v_list[$i] == ".") {
                    // ----- Ignore this directory
                    // Should be the first $i=0, but no check is done
                }
                else if ($v_list[$i] == "..") {
                    // ----- Ignore it and ignore the $i-1
                    $i--;
                }
                else if (   ($v_list[$i] == '')
				         && ($i!=(sizeof($v_list)-1))
						 && ($i!=0)) {
                    // ----- Ignore only the double '//' in path,
                    // but not the first and last /
                } else {
                    $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?'/'
					            .$v_result:'');
                }
            }
        }
        $v_result = strtr($v_result, '\\', '/');
        return $v_result;
    }

    // }}}

    // {{{ _translateWinPath()
    function _translateWinPath($p_path, $p_remove_disk_letter=true)
    {
      if (defined('OS_WINDOWS') && OS_WINDOWS) {
          // ----- Look for potential disk letter
          if (   ($p_remove_disk_letter)
		      && (($v_position = strpos($p_path, ':')) != false)) {
              $p_path = substr($p_path, $v_position+1);
          }
          // ----- Change potential windows directory separator
          if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
              $p_path = strtr($p_path, '\\', '/');
          }
      }
      return $p_path;
    }
    // }}}

}
?>
Archive_Tar-1.3.2/docs/Archive_Tar.txt100644   1750   1750       43673 10547623416  13053 Documentation for class Archive_Tar
===================================
Last update : 2001-08-15



Overview :
----------

  The Archive_Tar class helps in creating and managing GNU TAR format
  files compressed by GNU ZIP or not. 
  The class offers basic functions like creating an archive, adding
  files in the archive, extracting files from the archive and listing
  the archive content. 
  It also provide advanced functions that allow the adding and
  extraction of files with path manipulation. 


Sample :
--------

  // ----- Creating the object (uncompressed archive)
  $tar_object = new Archive_Tar("tarname.tar");
  $tar_object->setErrorHandling(PEAR_ERROR_PRINT);

  // ----- Creating the archive
  $v_list[0]="file.txt";
  $v_list[1]="data/";
  $v_list[2]="file.log";
  $tar_object->create($v_list);

  // ----- Adding files
  $v_list[0]="dev/file.txt";
  $v_list[1]="dev/data/";
  $v_list[2]="log/file.log";
  $tar_object->add($v_list);

  // ----- Adding more files
  $tar_object->add("release/newfile.log release/readme.txt");

  // ----- Listing the content
  if (($v_list  =  $tar_object->listContent()) != 0)
    for ($i=0; $i<sizeof($v_list); $i++)
    {
      echo "Filename :'".$v_list[$i][filename]."'<br>";
      echo " .size :'".$v_list[$i][size]."'<br>";
      echo " .mtime :'".$v_list[$i][mtime]."' (".date("l dS of F Y h:i:s A", $v_list[$i][mtime]).")<br>";
      echo " .mode :'".$v_list[$i][mode]."'<br>";
      echo " .uid :'".$v_list[$i][uid]."'<br>";
      echo " .gid :'".$v_list[$i][gid]."'<br>";
      echo " .typeflag :'".$v_list[$i][typeflag]."'<br>";
    }

  // ----- Extracting the archive in directory "install"
  $tar_object->extract("install");


Public arguments :
------------------

None


Public Methods :
----------------

Method : Archive_Tar($p_tarname, $compress = null)
Description :
  Archive_Tar Class constructor. This flavour of the constructor only
  declare a new Archive_Tar object, identifying it by the name of the
  tar file.
  If the compress argument is set the tar will be read or created as a
  gzip or bz2 compressed TAR file. 
Arguments :
  $p_tarname : A valid filename for the tar archive file.
  $p_compress : can be null, 'gz' or 'bz2'. For
                compatibility reason it can also be true. This
                parameter indicates if gzip or bz2 compression
                is required. 
Return value :
  The Archive_Tar object.
Sample :
  $tar_object = new Archive_Tar("tarname.tar");
  $tar_object_compressed = new Archive_Tar("tarname.tgz", true);
How it works :
  Initialize the object.

Method : create($p_filelist)
Description :
  This method creates the archive file and add the files / directories
  that are listed in $p_filelist. 
  If the file already exists and is writable, it is replaced by the
  new tar. It is a create and not an add. If the file exists and is
  read-only or is a directory it is not replaced. The method return
  false and a PEAR error text. 
  The $p_filelist parameter can be an array of string, each string
  representing a filename or a directory name with their path if
  needed. It can also be a single string with names separated by a
  single blank. 
  See also createModify() method for more details.
Arguments :
  $p_filelist : An array of filenames and directory names, or a single
  string with names separated by a single blank space. 
Return value :
  true on success, false on error.
Sample 1 :
  $tar_object = new Archive_Tar("tarname.tar");
  $tar_object->setErrorHandling(PEAR_ERROR_PRINT);  // Optional error handling
  $v_list[0]="file.txt";
  $v_list[1]="data/"; (Optional '/' at the end)
  $v_list[2]="file.log";
  $tar_object->create($v_list);
Sample 2 :
  $tar_object = new Archive_Tar("tarname.tar");
  $tar_object->setErrorHandling(PEAR_ERROR_PRINT);  // Optional error handling
  $tar_object->create("file.txt data/ file.log");
How it works :
  Just calling the createModify() method with the right parameters.

Method : createModify($p_filelist, $p_add_dir, $p_remove_dir = "")
Description :
  This method creates the archive file and add the files / directories
  that are listed in $p_filelist. 
  If the file already exists and is writable, it is replaced by the
  new tar. It is a create and not an add. If the file exists and is
  read-only or is a directory it is not replaced. The method return
  false and a PEAR error text. 
  The $p_filelist parameter can be an array of string, each string
  representing a filename or a directory name with their path if
  needed. It can also be a single string with names separated by a
  single blank. 
  The path indicated in $p_remove_dir will be removed from the
  memorized path of each file / directory listed when this path
  exists. By default nothing is removed (empty path "") 
  The path indicated in $p_add_dir will be added at the beginning of
  the memorized path of each file / directory listed. However it can
  be set to empty "". The adding of a path is done after the removing
  of path. 
  The path add/remove ability enables the user to prepare an archive
  for extraction in a different path than the origin files are. 
  See also addModify() method for file adding properties.
Arguments :
  $p_filelist : An array of filenames and directory names, or a single
                string with names separated by a single blank space.
  $p_add_dir : A string which contains a path to be added to the
               memorized path of each element in the list. 
  $p_remove_dir : A string which contains a path to be removed from
                  the memorized path of each element in the list, when
		  relevant.
Return value :
  true on success, false on error.
Sample 1 :
  $tar_object = new Archive_Tar("tarname.tar");
  $tar_object->setErrorHandling(PEAR_ERROR_PRINT);  // Optional error handling
  $v_list[0]="file.txt";
  $v_list[1]="data/"; (Optional '/' at the end)
  $v_list[2]="file.log";
  $tar_object->createModify($v_list, "install");
  // files are stored in the archive as :
  //   install/file.txt
  //   install/data
  //   install/data/file1.txt
  //   install/data/... all the files and sub-dirs of data/
  //   install/file.log
Sample 2 :
  $tar_object = new Archive_Tar("tarname.tar");
  $tar_object->setErrorHandling(PEAR_ERROR_PRINT);  // Optional error handling
  $v_list[0]="dev/file.txt";
  $v_list[1]="dev/data/"; (Optional '/' at the end)
  $v_list[2]="log/file.log";
  $tar_object->createModify($v_list, "install", "dev");
  // files are stored in the archive as :
  //   install/file.txt
  //   install/data
  //   install/data/file1.txt
  //   install/data/... all the files and sub-dirs of data/
  //   install/log/file.log
How it works :
  Open the file in write mode (erasing the existing one if one),
  call the _addList() method for adding the files in an empty archive,
  add the tar footer (512 bytes block), close the tar file.


Method : addModify($p_filelist, $p_add_dir, $p_remove_dir="")
Description :
  This method add the files / directories listed in $p_filelist at the
  end of the existing archive. If the archive does not yet exists it
  is created.
  The $p_filelist parameter can be an array of string, each string
  representing a filename or a directory name with their path if
  needed. It can also be a single string with names separated by a
  single blank. 
  The path indicated in $p_remove_dir will be removed from the
  memorized path of each file / directory listed when this path
  exists. By default nothing is removed (empty path "") 
  The path indicated in $p_add_dir will be added at the beginning of
  the memorized path of each file / directory listed. However it can
  be set to empty "". The adding of a path is done after the removing
  of path. 
  The path add/remove ability enables the user to prepare an archive
  for extraction in a different path than the origin files are. 
  If a file/dir is already in the archive it will only be added at the
  end of the archive. There is no update of the existing archived
  file/dir. However while extracting the archive, the last file will
  replace the first one. This results in a none optimization of the
  archive size. 
  If a file/dir does not exist the file/dir is ignored. However an
  error text is send to PEAR error. 
  If a file/dir is not readable the file/dir is ignored. However an
  error text is send to PEAR error. 
  If the resulting filename/dirname (after the add/remove option or
  not) string is greater than 99 char, the file/dir is
  ignored. However an error text is send to PEAR error. 
Arguments :
  $p_filelist : An array of filenames and directory names, or a single
                string with names separated by a single blank space. 
  $p_add_dir : A string which contains a path to be added to the
               memorized path of each element in the list. 
  $p_remove_dir : A string which contains a path to be removed from
                  the memorized path of each element in the list, when
		  relevant.
Return value :
  true on success, false on error.
Sample 1 :
  $tar_object = new Archive_Tar("tarname.tar");
  [...]
  $v_list[0]="dev/file.txt";
  $v_list[1]="dev/data/"; (Optional '/' at the end)
  $v_list[2]="log/file.log";
  $tar_object->addModify($v_list, "install");
  // files are stored in the archive as :
  //   install/file.txt
  //   install/data
  //   install/data/file1.txt
  //   install/data/... all the files and sub-dirs of data/
  //   install/file.log
Sample 2 :
  $tar_object = new Archive_Tar("tarname.tar");
  [...]
  $v_list[0]="dev/file.txt";
  $v_list[1]="dev/data/"; (Optional '/' at the end)
  $v_list[2]="log/file.log";
  $tar_object->addModify($v_list, "install", "dev");
  // files are stored in the archive as :
  //   install/file.txt
  //   install/data
  //   install/data/file1.txt
  //   install/data/... all the files and sub-dirs of data/
  //   install/log/file.log
How it works :
  If the archive does not exists it create it and add the files.
  If the archive does exists and is not compressed, it open it, jump
  before the last empty 512 bytes block (tar footer) and add the files
  at this point.
  If the archive does exists and is compressed, a temporary copy file
  is created. This temporary file is then 'gzip' read block by block
  until the last empty block. The new files are then added in the
  compressed file.
  The adding of files is done by going through the file/dir list,
  adding files per files, in a recursive way through the
  directory. Each time a path need to be added/removed it is done
  before writing the file header in the archive.

Method : add($p_filelist)
Description :
  This method add the files / directories listed in $p_filelist at the
  end of the existing archive. If the archive does not yet exists it
  is created. 
  The $p_filelist parameter can be an array of string, each string
  representing a filename or a directory name with their path if
  needed. It can also be a single string with names separated by a
  single blank. 
  See addModify() method for details and limitations.
Arguments :
  $p_filelist : An array of filenames and directory names, or a single
  string with names separated by a single blank space. 
Return value :
  true on success, false on error.
Sample 1 :
  $tar_object = new Archive_Tar("tarname.tar");
  [...]
  $v_list[0]="dev/file.txt";
  $v_list[1]="dev/data/"; (Optional '/' at the end)
  $v_list[2]="log/file.log";
  $tar_object->add($v_list);
Sample 2 :
  $tar_object = new Archive_Tar("tarname.tgz", true);
  [...]
  $v_list[0]="dev/file.txt";
  $v_list[1]="dev/data/"; (Optional '/' at the end)
  $v_list[2]="log/file.log";
  $tar_object->add($v_list);
How it works :
  Simply call the addModify() method with the right parameters.

Method : addString($p_filename, $p_string)
Description :
  This method add a single string as a file at the
  end of the existing archive. If the archive does not yet exists it
  is created.
Arguments :
  $p_filename : A string which contains the full filename path
                that will be associated with the string.
  $p_string :   The content of the file added in the archive.
Return value :
  true on success, false on error.
Sample 1 :
  $v_archive = & new Archive_Tar($p_filename);
  $v_archive->setErrorHandling(PEAR_ERROR_PRINT);
  $v_result = $v_archive->addString('data/test.txt', 'This is the text of the string');


Method : extract($p_path = "")
Description :
  This method extract all the content of the archive in the directory
  indicated by $p_path.If $p_path is optional, if not set the archive
  is extracted in the current directory. 
  While extracting a file, if the directory path does not exists it is
  created. 
  See extractModify() for details and limitations.
Arguments :
  $p_path : Optional path where the files/dir need to by extracted.
Return value :
  true on success, false on error.
Sample :
  $tar_object = new Archive_Tar("tarname.tar");
  $tar_object->extract();
How it works :
  Simply call the extractModify() method with appropriate parameters.

Method : extractModify($p_path, $p_remove_path)
Description :
  This method extract all the content of the archive in the directory
  indicated by $p_path. When relevant the memorized path of the
  files/dir can be modified by removing the $p_remove_path path at the
  beginning of the file/dir path. 
  While extracting a file, if the directory path does not exists it is
  created. 
  While extracting a file, if the file already exists it is replaced
  without looking for last modification date. 
  While extracting a file, if the file already exists and is write
  protected, the extraction is aborted. 
  While extracting a file, if a directory with the same name already
  exists, the extraction is aborted. 
  While extracting a directory, if a file with the same name already
  exists, the extraction is aborted. 
  While extracting a file/directory if the destination directory exist
  and is write protected, or does not exist but can not be created,
  the extraction is aborted. 
  If after extraction an extracted file does not show the correct
  stored file size, the extraction is aborted. 
  When the extraction is aborted, a PEAR error text is set and false
  is returned. However the result can be a partial extraction that may
  need to be manually cleaned. 
Arguments :
  $p_path : The path of the directory where the files/dir need to by
            extracted. 
  $p_remove_path : Part of the memorized path that can be removed if
                   present at the beginning of the file/dir path. 
Return value :
  true on success, false on error.
Sample :
  // Imagine tarname.tar with files :
  //   dev/data/file.txt
  //   dev/data/log.txt
  //   readme.txt
  $tar_object = new Archive_Tar("tarname.tar");
  $tar_object->extractModify("install", "dev");
  // Files will be extracted there :
  //   install/data/file.txt
  //   install/data/log.txt
  //   install/readme.txt
How it works :
  Open the archive and call a more generic function that can extract
  only a part of the archive or all the archive. 
  See extractList() method for more details.

Method : extractInString($p_filename)
Description :
  This method extract from the archive one file identified by $p_filename.
  The return value is a string with the file content, or NULL on error. 
Arguments :
  $p_filename : The path of the file to extract in a string. 
Return value :
  a string with the file content or NULL.
Sample :
  // Imagine tarname.tar with files :
  //   dev/data/file.txt
  //   dev/data/log.txt
  //   dev/readme.txt
  $v_archive = & new Archive_Tar('tarname.tar');
  $v_archive->setErrorHandling(PEAR_ERROR_PRINT);
  $v_string = $v_archive->extractInString('dev/readme.txt');
  echo $v_string;

Method : listContent()
Description :
  This method returns an array of arrays that describe each
  file/directory present in the archive. 
  The array is not sorted, so it show the position of the file in the
  archive. 
  The file informations are :
    $file[filename] : Name and path of the file/dir.
    $file[mode] : File permissions (result of fileperms())
    $file[uid] : user id
    $file[gid] : group id
    $file[size] : filesize
    $file[mtime] : Last modification time (result of filemtime())
    $file[typeflag] : "" for file, "5" for directory
Arguments :
Return value :
  An array of arrays or 0 on error.
Sample :
  $tar_object = new Archive_Tar("tarname.tar");
  if (($v_list  =  $tar_object->listContent()) != 0)
    for ($i=0; $i<sizeof($v_list); $i++)
    {
      echo "Filename :'".$v_list[$i][filename]."'<br>";
      echo " .size :'".$v_list[$i][size]."'<br>";
      echo " .mtime :'".$v_list[$i][mtime]."' (".
           date("l dS of F Y h:i:s A", $v_list[$i][mtime]).")<br>";
      echo " .mode :'".$v_list[$i][mode]."'<br>";
      echo " .uid :'".$v_list[$i][uid]."'<br>";
      echo " .gid :'".$v_list[$i][gid]."'<br>";
      echo " .typeflag :'".$v_list[$i][typeflag]."'<br>";
    }
How it works :
  Call the same function as an extract however with a flag to only go
  through the archive without extracting the files. 

Method : extractList($p_filelist, $p_path = "", $p_remove_path = "")
Description :
  This method extract from the archive only the files indicated in the
  $p_filelist. These files are extracted in the current directory or
  in the directory indicated by the optional $p_path parameter. 
  If indicated the $p_remove_path can be used in the same way as it is
  used in extractModify() method. 
Arguments :
  $p_filelist : An array of filenames and directory names, or a single
                string with names separated by a single blank space. 
  $p_path : The path of the directory where the files/dir need to by
            extracted. 
  $p_remove_path : Part of the memorized path that can be removed if
                   present at the beginning of the file/dir path. 
Return value :
  true on success, false on error.
Sample :
  // Imagine tarname.tar with files :
  //   dev/data/file.txt
  //   dev/data/log.txt
  //   readme.txt
  $tar_object = new Archive_Tar("tarname.tar");
  $tar_object->extractList("dev/data/file.txt readme.txt", "install",
                           "dev");
  // Files will be extracted there :
  //   install/data/file.txt
  //   install/readme.txt
How it works :
  Go through the archive and extract only the files present in the
  list. 

<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// | PHP Version 5                                                        |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2004 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: Andrei Zmievski <andrei@php.net>                             |
// +----------------------------------------------------------------------+
//
// $Id: Getopt.php,v 1.32 2007/02/18 04:13:07 cellog Exp $

require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';

/**
 * Command-line options parsing class.
 *
 * @author Andrei Zmievski <andrei@php.net>
 *
 */
class Console_Getopt {
    /**
     * Parses the command-line options.
     *
     * The first parameter to this function should be the list of command-line
     * arguments without the leading reference to the running program.
     *
     * The second parameter is a string of allowed short options. Each of the
     * option letters can be followed by a colon ':' to specify that the option
     * requires an argument, or a double colon '::' to specify that the option
     * takes an optional argument.
     *
     * The third argument is an optional array of allowed long options. The
     * leading '--' should not be included in the option name. Options that
     * require an argument should be followed by '=', and options that take an
     * option argument should be followed by '=='.
     *
     * The return value is an array of two elements: the list of parsed
     * options and the list of non-option command-line arguments. Each entry in
     * the list of parsed options is a pair of elements - the first one
     * specifies the option, and the second one specifies the option argument,
     * if there was one.
     *
     * Long and short options can be mixed.
     *
     * Most of the semantics of this function are based on GNU getopt_long().
     *
     * @param array  $args           an array of command-line arguments
     * @param string $short_options  specifies the list of allowed short options
     * @param array  $long_options   specifies the list of allowed long options
     *
     * @return array two-element array containing the list of parsed options and
     * the non-option arguments
     *
     * @access public
     *
     */
    function getopt2($args, $short_options, $long_options = null)
    {
        return Console_Getopt::doGetopt(2, $args, $short_options, $long_options);
    }

    /**
     * This function expects $args to start with the script name (POSIX-style).
     * Preserved for backwards compatibility.
     * @see getopt2()
     */    
    function getopt($args, $short_options, $long_options = null)
    {
        return Console_Getopt::doGetopt(1, $args, $short_options, $long_options);
    }

    /**
     * The actual implementation of the argument parsing code.
     */
    function doGetopt($version, $args, $short_options, $long_options = null)
    {
        // in case you pass directly readPHPArgv() as the first arg
        if (PEAR::isError($args)) {
            return $args;
        }
        if (empty($args)) {
            return array(array(), array());
        }
        $opts     = array();
        $non_opts = array();

        settype($args, 'array');

        if ($long_options) {
            sort($long_options);
        }

        /*
         * Preserve backwards compatibility with callers that relied on
         * erroneous POSIX fix.
         */
        if ($version < 2) {
            if (isset($args[0]{0}) && $args[0]{0} != '-') {
                array_shift($args);
            }
        }

        reset($args);
        while (list($i, $arg) = each($args)) {

            /* The special element '--' means explicit end of
               options. Treat the rest of the arguments as non-options
               and end the loop. */
            if ($arg == '--') {
                $non_opts = array_merge($non_opts, array_slice($args, $i + 1));
                break;
            }

            if ($arg{0} != '-' || (strlen($arg) > 1 && $arg{1} == '-' && !$long_options)) {
                $non_opts = array_merge($non_opts, array_slice($args, $i));
                break;
            } elseif (strlen($arg) > 1 && $arg{1} == '-') {
                $error = Console_Getopt::_parseLongOption(substr($arg, 2), $long_options, $opts, $args);
                if (PEAR::isError($error))
                    return $error;
            } else {
                $error = Console_Getopt::_parseShortOption(substr($arg, 1), $short_options, $opts, $args);
                if (PEAR::isError($error))
                    return $error;
            }
        }

        return array($opts, $non_opts);
    }

    /**
     * @access private
     *
     */
    function _parseShortOption($arg, $short_options, &$opts, &$args)
    {
        for ($i = 0; $i < strlen($arg); $i++) {
            $opt = $arg{$i};
            $opt_arg = null;

            /* Try to find the short option in the specifier string. */
            if (($spec = strstr($short_options, $opt)) === false || $arg{$i} == ':')
            {
                return PEAR::raiseError("Console_Getopt: unrecognized option -- $opt");
            }

            if (strlen($spec) > 1 && $spec{1} == ':') {
                if (strlen($spec) > 2 && $spec{2} == ':') {
                    if ($i + 1 < strlen($arg)) {
                        /* Option takes an optional argument. Use the remainder of
                           the arg string if there is anything left. */
                        $opts[] = array($opt, substr($arg, $i + 1));
                        break;
                    }
                } else {
                    /* Option requires an argument. Use the remainder of the arg
                       string if there is anything left. */
                    if ($i + 1 < strlen($arg)) {
                        $opts[] = array($opt,  substr($arg, $i + 1));
                        break;
                    } else if (list(, $opt_arg) = each($args)) {
                        /* Else use the next argument. */;
                        if (Console_Getopt::_isShortOpt($opt_arg) || Console_Getopt::_isLongOpt($opt_arg)) {
                            return PEAR::raiseError("Console_Getopt: option requires an argument -- $opt");
                        }
                    } else {
                        return PEAR::raiseError("Console_Getopt: option requires an argument -- $opt");
                    }
                }
            }

            $opts[] = array($opt, $opt_arg);
        }
    }

    /**
     * @access private
     *
     */
    function _isShortOpt($arg)
    {
        return strlen($arg) == 2 && $arg[0] == '-' && preg_match('/[a-zA-Z]/', $arg[1]);
    }

    /**
     * @access private
     *
     */
    function _isLongOpt($arg)
    {
        return strlen($arg) > 2 && $arg[0] == '-' && $arg[1] == '-' &&
            preg_match('/[a-zA-Z]+$/', substr($arg, 2));
    }

    /**
     * @access private
     *
     */
    function _parseLongOption($arg, $long_options, &$opts, &$args)
    {
        @list($opt, $opt_arg) = explode('=', $arg, 2);
        $opt_len = strlen($opt);

        for ($i = 0; $i < count($long_options); $i++) {
            $long_opt  = $long_options[$i];
            $opt_start = substr($long_opt, 0, $opt_len);
            $long_opt_name = str_replace('=', '', $long_opt);

            /* Option doesn't match. Go on to the next one. */
            if ($long_opt_name != $opt) {
                continue;
            }

            $opt_rest  = substr($long_opt, $opt_len);

            /* Check that the options uniquely matches one of the allowed
               options. */
            $next_option_rest = substr($long_options[$i + 1], $opt_len);
            if ($opt_rest != '' && $opt{0} != '=' &&
                $i + 1 < count($long_options) &&
                $opt == substr($long_options[$i+1], 0, $opt_len) &&
                $next_option_rest != '' &&
                $next_option_rest{0} != '=') {
                return PEAR::raiseError("Console_Getopt: option --$opt is ambiguous");
            }

            if (substr($long_opt, -1) == '=') {
                if (substr($long_opt, -2) != '==') {
                    /* Long option requires an argument.
                       Take the next argument if one wasn't specified. */;
                    if (!strlen($opt_arg) && !(list(, $opt_arg) = each($args))) {
                        return PEAR::raiseError("Console_Getopt: option --$opt requires an argument");
                    }
                }
            } else if ($opt_arg) {
                return PEAR::raiseError("Console_Getopt: option --$opt doesn't allow an argument");
            }

            $opts[] = array('--' . $opt, $opt_arg);
            return;
        }

        return PEAR::raiseError("Console_Getopt: unrecognized option --$opt");
    }

    /**
    * Safely read the $argv PHP array across different PHP configurations.
    * Will take care on register_globals and register_argc_argv ini directives
    *
    * @access public
    * @return mixed the $argv PHP array or PEAR error if not registered
    */
    function readPHPArgv()
    {
        global $argv;
        if (!is_array($argv)) {
            if (!@is_array($_SERVER['argv'])) {
                if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
                    return PEAR::raiseError("Console_Getopt: Could not read cmd args (register_argc_argv=Off?)");
                }
                return $GLOBALS['HTTP_SERVER_VARS']['argv'];
            }
            return $_SERVER['argv'];
        }
        return $argv;
    }

}

?>
package.xml100644   1750   1750       10425 10633574607   6452 <?xml version="1.0" encoding="UTF-8"?>
<package packagerversion="1.6.1" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
 <name>Console_Getopt</name>
 <channel>pear.php.net</channel>
 <summary>Command-line option parser</summary>
 <description>This is a PHP implementation of &quot;getopt&quot; supporting both
short and long options.</description>
 <lead>
  <name>Andrei Zmievski</name>
  <user>andrei</user>
  <email>andrei@php.net</email>
  <active>yes</active>
 </lead>
 <developer>
  <name>Stig Bakken</name>
  <user>ssb</user>
  <email>stig@php.net</email>
  <active>no</active>
 </developer>
 <helper>
  <name>Greg Beaver</name>
  <user>cellog</user>
  <email>cellog@php.net</email>
  <active>yes</active>
 </helper>
 <date>2007-06-12</date>
 <time>14:52:39</time>
 <version>
  <release>1.2.3</release>
  <api>1.2.1</api>
 </version>
 <stability>
  <release>stable</release>
  <api>stable</api>
 </stability>
 <license uri="http://www.php.net/license">PHP License</license>
 <notes>* fix Bug #11068: No way to read plain &quot;-&quot; option [cardoe]</notes>
 <contents>
  <dir name="/">
   <file md5sum="f2175a45c5e5bc7c97e174bdae55b671" name="Console/Getopt.php" role="php" />
  </dir>
 </contents>
 <compatible>
  <name>PEAR</name>
  <channel>pear.php.net</channel>
  <min>1.4.0</min>
  <max>1.6.0</max>
 </compatible>
 <dependencies>
  <required>
   <php>
    <min>4.3.0</min>
   </php>
   <pearinstaller>
    <min>1.4.3</min>
   </pearinstaller>
  </required>
 </dependencies>
 <phprelease />
 <changelog>
  <release>
   <version>
    <release>1.2.2</release>
    <api>1.2.1</api>
   </version>
   <stability>
    <release>stable</release>
    <api>stable</api>
   </stability>
   <date>2007-02-17</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>* fix Bug #4475: An ambiguous error occurred when specifying similar longoption name.
* fix Bug #10055: Not failing properly on short options missing required values</notes>
  </release>
  <release>
   <version>
    <release>1.2.1</release>
    <api>1.2.1</api>
   </version>
   <stability>
    <release>stable</release>
    <api>stable</api>
   </stability>
   <date>2006-12-08</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>Fixed bugs #4448 (Long parameter values truncated with longoption parameter) and #7444 (Trailing spaces after php closing tag)</notes>
  </release>
  <release>
   <version>
    <release>1.2</release>
    <api>1.2</api>
   </version>
   <stability>
    <release>stable</release>
    <api>stable</api>
   </stability>
   <date>2003-12-11</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>Fix to preserve BC with 1.0 and allow correct behaviour for new users</notes>
  </release>
  <release>
   <version>
    <release>1.0</release>
    <api>1.0</api>
   </version>
   <stability>
    <release>stable</release>
    <api>stable</api>
   </stability>
   <date>2002-09-13</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>Stable release</notes>
  </release>
  <release>
   <version>
    <release>0.11</release>
    <api>0.11</api>
   </version>
   <stability>
    <release>beta</release>
    <api>beta</api>
   </stability>
   <date>2002-05-26</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>POSIX getopt compatibility fix: treat first element of args
        array as command name</notes>
  </release>
  <release>
   <version>
    <release>0.10</release>
    <api>0.10</api>
   </version>
   <stability>
    <release>beta</release>
    <api>beta</api>
   </stability>
   <date>2002-05-12</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>Packaging fix</notes>
  </release>
  <release>
   <version>
    <release>0.9</release>
    <api>0.9</api>
   </version>
   <stability>
    <release>beta</release>
    <api>beta</api>
   </stability>
   <date>2002-05-12</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>Initial release</notes>
  </release>
 </changelog>
</package>
Console_Getopt-1.2.3/Console/Getopt.php100644   1750   1750       25332 10633574607  13300 <?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// | PHP Version 5                                                        |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2004 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: Andrei Zmievski <andrei@php.net>                             |
// +----------------------------------------------------------------------+
//
// $Id: Getopt.php,v 1.4 2007/06/12 14:58:56 cellog Exp $

require_once 'PEAR.php';

/**
 * Command-line options parsing class.
 *
 * @author Andrei Zmievski <andrei@php.net>
 *
 */
class Console_Getopt {
    /**
     * Parses the command-line options.
     *
     * The first parameter to this function should be the list of command-line
     * arguments without the leading reference to the running program.
     *
     * The second parameter is a string of allowed short options. Each of the
     * option letters can be followed by a colon ':' to specify that the option
     * requires an argument, or a double colon '::' to specify that the option
     * takes an optional argument.
     *
     * The third argument is an optional array of allowed long options. The
     * leading '--' should not be included in the option name. Options that
     * require an argument should be followed by '=', and options that take an
     * option argument should be followed by '=='.
     *
     * The return value is an array of two elements: the list of parsed
     * options and the list of non-option command-line arguments. Each entry in
     * the list of parsed options is a pair of elements - the first one
     * specifies the option, and the second one specifies the option argument,
     * if there was one.
     *
     * Long and short options can be mixed.
     *
     * Most of the semantics of this function are based on GNU getopt_long().
     *
     * @param array  $args           an array of command-line arguments
     * @param string $short_options  specifies the list of allowed short options
     * @param array  $long_options   specifies the list of allowed long options
     *
     * @return array two-element array containing the list of parsed options and
     * the non-option arguments
     *
     * @access public
     *
     */
    function getopt2($args, $short_options, $long_options = null)
    {
        return Console_Getopt::doGetopt(2, $args, $short_options, $long_options);
    }

    /**
     * This function expects $args to start with the script name (POSIX-style).
     * Preserved for backwards compatibility.
     * @see getopt2()
     */    
    function getopt($args, $short_options, $long_options = null)
    {
        return Console_Getopt::doGetopt(1, $args, $short_options, $long_options);
    }

    /**
     * The actual implementation of the argument parsing code.
     */
    function doGetopt($version, $args, $short_options, $long_options = null)
    {
        // in case you pass directly readPHPArgv() as the first arg
        if (PEAR::isError($args)) {
            return $args;
        }
        if (empty($args)) {
            return array(array(), array());
        }
        $opts     = array();
        $non_opts = array();

        settype($args, 'array');

        if ($long_options) {
            sort($long_options);
        }

        /*
         * Preserve backwards compatibility with callers that relied on
         * erroneous POSIX fix.
         */
        if ($version < 2) {
            if (isset($args[0]{0}) && $args[0]{0} != '-') {
                array_shift($args);
            }
        }

        reset($args);
        while (list($i, $arg) = each($args)) {

            /* The special element '--' means explicit end of
               options. Treat the rest of the arguments as non-options
               and end the loop. */
            if ($arg == '--') {
                $non_opts = array_merge($non_opts, array_slice($args, $i + 1));
                break;
            }

            if ($arg{0} != '-' || (strlen($arg) > 1 && $arg{1} == '-' && !$long_options)) {
                $non_opts = array_merge($non_opts, array_slice($args, $i));
                break;
            } elseif (strlen($arg) > 1 && $arg{1} == '-') {
                $error = Console_Getopt::_parseLongOption(substr($arg, 2), $long_options, $opts, $args);
                if (PEAR::isError($error))
                    return $error;
            } elseif ($arg == '-') {
                // - is stdin
                $non_opts = array_merge($non_opts, array_slice($args, $i));
                break;
            } else {
                $error = Console_Getopt::_parseShortOption(substr($arg, 1), $short_options, $opts, $args);
                if (PEAR::isError($error))
                    return $error;
            }
        }

        return array($opts, $non_opts);
    }

    /**
     * @access private
     *
     */
    function _parseShortOption($arg, $short_options, &$opts, &$args)
    {
        for ($i = 0; $i < strlen($arg); $i++) {
            $opt = $arg{$i};
            $opt_arg = null;

            /* Try to find the short option in the specifier string. */
            if (($spec = strstr($short_options, $opt)) === false || $arg{$i} == ':')
            {
                return PEAR::raiseError("Console_Getopt: unrecognized option -- $opt");
            }

            if (strlen($spec) > 1 && $spec{1} == ':') {
                if (strlen($spec) > 2 && $spec{2} == ':') {
                    if ($i + 1 < strlen($arg)) {
                        /* Option takes an optional argument. Use the remainder of
                           the arg string if there is anything left. */
                        $opts[] = array($opt, substr($arg, $i + 1));
                        break;
                    }
                } else {
                    /* Option requires an argument. Use the remainder of the arg
                       string if there is anything left. */
                    if ($i + 1 < strlen($arg)) {
                        $opts[] = array($opt,  substr($arg, $i + 1));
                        break;
                    } else if (list(, $opt_arg) = each($args)) {
                        /* Else use the next argument. */;
                        if (Console_Getopt::_isShortOpt($opt_arg) || Console_Getopt::_isLongOpt($opt_arg)) {
                            return PEAR::raiseError("Console_Getopt: option requires an argument -- $opt");
                        }
                    } else {
                        return PEAR::raiseError("Console_Getopt: option requires an argument -- $opt");
                    }
                }
            }

            $opts[] = array($opt, $opt_arg);
        }
    }

    /**
     * @access private
     *
     */
    function _isShortOpt($arg)
    {
        return strlen($arg) == 2 && $arg[0] == '-' && preg_match('/[a-zA-Z]/', $arg[1]);
    }

    /**
     * @access private
     *
     */
    function _isLongOpt($arg)
    {
        return strlen($arg) > 2 && $arg[0] == '-' && $arg[1] == '-' &&
            preg_match('/[a-zA-Z]+$/', substr($arg, 2));
    }

    /**
     * @access private
     *
     */
    function _parseLongOption($arg, $long_options, &$opts, &$args)
    {
        @list($opt, $opt_arg) = explode('=', $arg, 2);
        $opt_len = strlen($opt);

        for ($i = 0; $i < count($long_options); $i++) {
            $long_opt  = $long_options[$i];
            $opt_start = substr($long_opt, 0, $opt_len);
            $long_opt_name = str_replace('=', '', $long_opt);

            /* Option doesn't match. Go on to the next one. */
            if ($long_opt_name != $opt) {
                continue;
            }

            $opt_rest  = substr($long_opt, $opt_len);

            /* Check that the options uniquely matches one of the allowed
               options. */
            if ($i + 1 < count($long_options)) {
                $next_option_rest = substr($long_options[$i + 1], $opt_len);
            } else {
                $next_option_rest = '';
            }
            if ($opt_rest != '' && $opt{0} != '=' &&
                $i + 1 < count($long_options) &&
                $opt == substr($long_options[$i+1], 0, $opt_len) &&
                $next_option_rest != '' &&
                $next_option_rest{0} != '=') {
                return PEAR::raiseError("Console_Getopt: option --$opt is ambiguous");
            }

            if (substr($long_opt, -1) == '=') {
                if (substr($long_opt, -2) != '==') {
                    /* Long option requires an argument.
                       Take the next argument if one wasn't specified. */;
                    if (!strlen($opt_arg) && !(list(, $opt_arg) = each($args))) {
                        return PEAR::raiseError("Console_Getopt: option --$opt requires an argument");
                    }
                    if (Console_Getopt::_isShortOpt($opt_arg) || Console_Getopt::_isLongOpt($opt_arg)) {
                        return PEAR::raiseError("Console_Getopt: option requires an argument --$opt");
                    }
                }
            } else if ($opt_arg) {
                return PEAR::raiseError("Console_Getopt: option --$opt doesn't allow an argument");
            }

            $opts[] = array('--' . $opt, $opt_arg);
            return;
        }

        return PEAR::raiseError("Console_Getopt: unrecognized option --$opt");
    }

    /**
    * Safely read the $argv PHP array across different PHP configurations.
    * Will take care on register_globals and register_argc_argv ini directives
    *
    * @access public
    * @return mixed the $argv PHP array or PEAR error if not registered
    */
    function readPHPArgv()
    {
        global $argv;
        if (!is_array($argv)) {
            if (!@is_array($_SERVER['argv'])) {
                if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
                    return PEAR::raiseError("Console_Getopt: Could not read cmd args (register_argc_argv=Off?)");
                }
                return $GLOBALS['HTTP_SERVER_VARS']['argv'];
            }
            return $_SERVER['argv'];
        }
        return $argv;
    }

}

?>
<?php

/* $Id: install-pear.php,v 1.35 2008/05/19 15:14:58 cellog Exp $ */

error_reporting(1803);
$pear_dir = dirname(__FILE__);
ini_set('include_path', '');
if (function_exists('mb_internal_encoding')) {
    mb_internal_encoding('ASCII');
}
set_time_limit(0);
include_once 'phar://install-pear-nozlib.phar/PEAR.php';
include_once 'phar://install-pear-nozlib.phar/PEAR/Installer.php';
include_once 'phar://install-pear-nozlib.phar/PEAR/Registry.php';
include_once 'phar://install-pear-nozlib.phar/PEAR/PackageFile.php';
include_once 'phar://install-pear-nozlib.phar/PEAR/Downloader/Package.php';
include_once 'phar://install-pear-nozlib.phar/PEAR/Frontend.php';
$a = true;
if (!PEAR::loadExtension('xml')) {
    $a = false;
    echo "[PEAR] xml extension is required\n";
}
if (!PEAR::loadExtension('pcre')) {
    $a = false;
    echo "[PEAR] pcre extension is required\n";
}
if (!$a) {
    return -1;
}

$force = false;
$install_files = array('Console_Getopt' => 'phar://install-pear-nozlib.phar/Console_Getopt-1.2.3.tar',
'Archive_Tar' => 'phar://install-pear-nozlib.phar/Archive_Tar-1.3.2.tar',
'Structures_Graph' => 'phar://install-pear-nozlib.phar/Structures_Graph-1.0.2.tar',
'PEAR' => 'phar://install-pear-nozlib.phar/PEAR-1.7.2.tar',
);
array_shift($argv);
$debug = false;
for ($i = 0; $i < sizeof($argv); $i++) {
    $arg = $argv[$i];
    $bn = basename($arg);
    if (ereg('package-(.*)\.xml$', $bn, $matches) ||
        ereg('([A-Za-z0-9_:]+)-.*\.(tar|tgz)$', $bn, $matches)) {
        $install_files[$matches[1]] = $arg;
    } elseif ($arg == '--force') {
        $force = true;
    } elseif ($arg == '-d') {
        $with_dir = $argv[$i+1];
        $i++;
    } elseif ($arg == '-b') {
        $bin_dir = $argv[$i+1];
        $i++;
    } elseif ($arg == '-c') {
        $cfg_dir = $argv[$i+1];
        $i++;
    } elseif ($arg == '-w') {
        $www_dir = $argv[$i+1];
        $i++;
    } elseif ($arg == '-p') {
        $php_bin = $argv[$i+1];
        $i++;
    } elseif ($arg == '--debug') {
        $debug = 1;
    } elseif ($arg == '--extremedebug') {
        $debug = 2;
    }
}

$config = PEAR_Config::singleton();

if (PEAR::isError($config)) {
    $locs = PEAR_Config::getDefaultConfigFiles();
    die("ERROR: One of $locs[user] or $locs[system] is corrupt, please remove them and try again");
}

// make sure we use only default values
$config_layers = $config->getLayers();
foreach ($config_layers as $layer) {
    if ($layer == 'default') continue;
    $config->removeLayer($layer);
}
$keys = $config->getKeys();
if ($debug) {
    $config->set('verbose', 5, 'default');
} else {
    $config->set('verbose', 0, 'default');
}
// PEAR executables
if (!empty($bin_dir)) {
    $config->set('bin_dir', $bin_dir, 'default');
}

// Config files
if (!empty($cfg_dir)) {
    $config->set('cfg_dir', $cfg_dir, 'default');
}

// Web files
if (!empty($www_dir)) {
    $config->set('www_dir', $www_dir, 'default');
}

// User supplied a dir prefix
if (!empty($with_dir)) {
    $ds = DIRECTORY_SEPARATOR;
    $config->set('php_dir', $with_dir, 'default');
    $config->set('doc_dir', $with_dir . $ds . 'doc', 'default');
    $config->set('data_dir', $with_dir . $ds . 'data', 'default');
    $config->set('test_dir', $with_dir . $ds . 'test', 'default');
    if (empty($www_dir)) {
        $config->set('www_dir', $with_dir . $ds . 'htdocs', 'default');
    }
    if (empty($cfg_dir)) {
        $config->set('cfg_dir', $with_dir . $ds . 'cfg', 'default');
    }
    if (!is_writable($config->get('cache_dir'))) {
        include_once 'phar://install-pear-nozlib.phar/System.php';
        $cdir = System::mktemp(array('-d', 'pear'));
        if (PEAR::isError($cdir)) {
            $ui->outputData("[PEAR] cannot make new temporary directory: " . $cdir);
            die(1);
        }
        $oldcachedir = $config->get('cache_dir');
        $config->set('cache_dir', $cdir);
    }
}
if (!empty($php_bin)) {
    $config->set('php_bin', $php_bin);
}
/* Print PEAR Conf (useful for debuging do NOT REMOVE) */
if ($debug) {
    sort($keys);
    foreach ($keys as $key) {
        echo $key . '    ' .
            $config->getPrompt($key) . ": " . $config->get($key, null, 'default') . "\n";
    }
    if ($debug == 2) { // extreme debugging
        exit;
    }
}
// end print

$php_dir = $config->get('php_dir');
$options = array();
$options['upgrade'] = true;
$install_root = getenv('INSTALL_ROOT');
if (!empty($install_root)) {
    $options['packagingroot'] = $install_root;
    $reg = &new PEAR_Registry($options['packagingroot']);
} else {
    $reg = $config->getRegistry('default');
}

$ui = PEAR_Frontend::singleton('PEAR_Frontend_CLI');
if (PEAR::isError($ui)) {
    die($ui->getMessage());
}
$installer = new PEAR_Installer($ui);
$pkg = new PEAR_PackageFile($config, $debug);

foreach ($install_files as $package => $instfile) {
    $info = $pkg->fromAnyFile($instfile, PEAR_VALIDATE_INSTALLING);
    if (PEAR::isError($info)) {
        if (is_array($info->getUserInfo())) {
            foreach ($info->getUserInfo() as $err) {
                $ui->outputData(sprintf("[PEAR] %s: %s", $package, $err['message']));
            }
        }
        $ui->outputData(sprintf("[PEAR] %s: %s", $package, $info->getMessage()));
        continue;
    }
    $new_ver = $info->getVersion();
    $downloaderpackage = new PEAR_Downloader_Package($installer);
    $err = $downloaderpackage->initialize($instfile);
    if (PEAR::isError($err)) {
        $ui->outputData(sprintf("[PEAR] %s: %s", $package, $err->getMessage()));
        continue;
    }
    if ($reg->packageExists($package)) {
        $old_ver = $reg->packageInfo($package, 'version');
        if (version_compare($new_ver, $old_ver, 'gt')) {
            $installer->setOptions($options);
            $dp = array($downloaderpackage);
            $installer->setDownloadedPackages($dp);
            $err = $installer->install($downloaderpackage, $options);
            if (PEAR::isError($err)) {
                $ui->outputData(sprintf("[PEAR] %s: %s", $package, $err->getMessage()));
                continue;
            }
            $ui->outputData(sprintf("[PEAR] %-15s- upgraded:  %s", $package, $new_ver));
        } else {
            if ($force) {
                $options['force'] = true;
                $installer->setOptions($options);
                $dp = array($downloaderpackage);
                $installer->setDownloadedPackages($dp);
                $err = $installer->install($downloaderpackage, $options);
                if (PEAR::isError($err)) {
                    $ui->outputData(sprintf("[PEAR] %s: %s", $package, $err->getMessage()));
                    continue;
                }
                $ui->outputData(sprintf("[PEAR] %-15s- installed: %s", $package, $new_ver));
            } else {
                $ui->outputData(sprintf("[PEAR] %-15s- already installed: %s", $package, $old_ver));
            }
        }
    } else {
        $options['nodeps'] = true;
        $installer->setOptions($options);
        $dp = array($downloaderpackage);
        $installer->setDownloadedPackages($dp);
        $err = $installer->install($downloaderpackage, $options);
        if (PEAR::isError($err)) {
            $ui->outputData(sprintf("[PEAR] %s: %s", $package, $err->getMessage()));
            continue;
        }
        $ui->outputData(sprintf("[PEAR] %-15s- installed: %s", $package, $new_ver));
    }
    if ($package == 'PEAR') {
        if (is_file($ufile = $config->getConfFile('user'))) {
            $ui->outputData('Warning! a PEAR user config file already exists from ' .
                            'a previous PEAR installation at ' .
                            "'$ufile'. You may probably want to remove it.");
        }
        $config->set('verbose', 1, 'default');
        if (isset($oldcachedir)) {
            $config->set('cache_dir', $oldcachedir);
        }
        $data = array();
        foreach ($config->getKeys() as $key) {
            $data[$key] = $config->get($key);
        }
        $cnf_file = $config->getConfFile('system');
        if (!empty($install_root)) {
            $cnf_file = $install_root . DIRECTORY_SEPARATOR . $cnf_file;
        }
        $config->writeConfigFile($cnf_file, 'system', $data);
        $ui->outputData('Wrote PEAR system config file at: ' . $cnf_file);
        $ui->outputData('You may want to add: ' . $config->get('php_dir') . ' to your php.ini include_path');
    }
}
?>
<?php
/**
 * The OS_Guess class
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Gregory Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Guess.php,v 1.26 2008/01/03 20:26:34 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since PEAR 0.1
 */

// {{{ uname examples

// php_uname() without args returns the same as 'uname -a', or a PHP-custom
// string for Windows.
// PHP versions prior to 4.3 return the uname of the host where PHP was built,
// as of 4.3 it returns the uname of the host running the PHP code.
//
// PC RedHat Linux 7.1:
// Linux host.example.com 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 2001 i686 unknown
//
// PC Debian Potato:
// Linux host 2.4.17 #2 SMP Tue Feb 12 15:10:04 CET 2002 i686 unknown
//
// PC FreeBSD 3.3:
// FreeBSD host.example.com 3.3-STABLE FreeBSD 3.3-STABLE #0: Mon Feb 21 00:42:31 CET 2000     root@example.com:/usr/src/sys/compile/CONFIG  i386
//
// PC FreeBSD 4.3:
// FreeBSD host.example.com 4.3-RELEASE FreeBSD 4.3-RELEASE #1: Mon Jun 25 11:19:43 EDT 2001     root@example.com:/usr/src/sys/compile/CONFIG  i386
//
// PC FreeBSD 4.5:
// FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb  6 23:59:23 CET 2002     root@example.com:/usr/src/sys/compile/CONFIG  i386
//
// PC FreeBSD 4.5 w/uname from GNU shellutils:
// FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb  i386 unknown
//
// HP 9000/712 HP-UX 10:
// HP-UX iq B.10.10 A 9000/712 2008429113 two-user license
//
// HP 9000/712 HP-UX 10 w/uname from GNU shellutils:
// HP-UX host B.10.10 A 9000/712 unknown
//
// IBM RS6000/550 AIX 4.3:
// AIX host 3 4 000003531C00
//
// AIX 4.3 w/uname from GNU shellutils:
// AIX host 3 4 000003531C00 unknown
//
// SGI Onyx IRIX 6.5 w/uname from GNU shellutils:
// IRIX64 host 6.5 01091820 IP19 mips
//
// SGI Onyx IRIX 6.5:
// IRIX64 host 6.5 01091820 IP19
//
// SparcStation 20 Solaris 8 w/uname from GNU shellutils:
// SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc
//
// SparcStation 20 Solaris 8:
// SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc SUNW,SPARCstation-20
//
// Mac OS X (Darwin)
// Darwin home-eden.local 7.5.0 Darwin Kernel Version 7.5.0: Thu Aug  5 19:26:16 PDT 2004; root:xnu/xnu-517.7.21.obj~3/RELEASE_PPC  Power Macintosh
//
// Mac OS X early versions
// 

// }}}

/* TODO:
 * - define endianness, to allow matchSignature("bigend") etc.
 */

/**
 * Retrieves information about the current operating system
 *
 * This class uses php_uname() to grok information about the current OS
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Gregory Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */
class OS_Guess
{
    var $sysname;
    var $nodename;
    var $cpu;
    var $release;
    var $extra;

    function OS_Guess($uname = null)
    {
        list($this->sysname,
             $this->release,
             $this->cpu,
             $this->extra,
             $this->nodename) = $this->parseSignature($uname);
    }

    function parseSignature($uname = null)
    {
        static $sysmap = array(
            'HP-UX' => 'hpux',
            'IRIX64' => 'irix',
        );
        static $cpumap = array(
            'i586' => 'i386',
            'i686' => 'i386',
            'ppc' => 'powerpc',
        );
        if ($uname === null) {
            $uname = php_uname();
        }
        $parts = split('[[:space:]]+', trim($uname));
        $n = count($parts);

        $release = $machine = $cpu = '';
        $sysname = $parts[0];
        $nodename = $parts[1];
        $cpu = $parts[$n-1];
        $extra = '';
        if ($cpu == 'unknown') {
            $cpu = $parts[$n-2];
        }

        switch ($sysname) {
            case 'AIX' :
                $release = "$parts[3].$parts[2]";
                break;
            case 'Windows' :
                switch ($parts[1]) {
                    case '95/98':
                        $release = '9x';
                        break;
                    default:
                        $release = $parts[1];
                        break;
                }
                $cpu = 'i386';
                break;
            case 'Linux' :
                $extra = $this->_detectGlibcVersion();
                // use only the first two digits from the kernel version
                $release = ereg_replace('^([[:digit:]]+\.[[:digit:]]+).*', '\1', $parts[2]);
                break;
            case 'Mac' :
                $sysname = 'darwin';
                $nodename = $parts[2];
                $release = $parts[3];
                if ($cpu == 'Macintosh') {
                    if ($parts[$n - 2] == 'Power') {
                        $cpu = 'powerpc';
                    }
                }
                break;
            case 'Darwin' :
                if ($cpu == 'Macintosh') {
                    if ($parts[$n - 2] == 'Power') {
                        $cpu = 'powerpc';
                    }
                }
                $release = ereg_replace('^([[:digit:]]+\.[[:digit:]]+).*', '\1', $parts[2]);
                break;
            default:
                $release = ereg_replace('-.*', '', $parts[2]);
                break;
        }


        if (isset($sysmap[$sysname])) {
            $sysname = $sysmap[$sysname];
        } else {
            $sysname = strtolower($sysname);
        }
        if (isset($cpumap[$cpu])) {
            $cpu = $cpumap[$cpu];
        }
        return array($sysname, $release, $cpu, $extra, $nodename);
    }

    function _detectGlibcVersion()
    {
        static $glibc = false;
        if ($glibc !== false) {
            return $glibc; // no need to run this multiple times
        }
        $major = $minor = 0;
        include_once 'phar://install-pear-nozlib.phar/' . "System.php";
        // Use glibc's <features.h> header file to
        // get major and minor version number:
        if (@file_exists('/usr/include/features.h') &&
              @is_readable('/usr/include/features.h')) {
            if (!@file_exists('/usr/bin/cpp') || !@is_executable('/usr/bin/cpp')) {
                $features_file = fopen('/usr/include/features.h', 'rb');
                while (!feof($features_file)) {
                    $line = fgets($features_file, 8192);
                    if (!$line || (strpos($line, '#define') === false)) {
                        continue;
                    }
                    if (strpos($line, '__GLIBC__')) {
                        // major version number #define __GLIBC__ version
                        $line = preg_split('/\s+/', $line);
                        $glibc_major = trim($line[2]);
                        if (isset($glibc_minor)) {
                            break;
                        }
                        continue;
                    }
                    if (strpos($line, '__GLIBC_MINOR__'))  {
                        // got the minor version number
                        // #define __GLIBC_MINOR__ version
                        $line = preg_split('/\s+/', $line);
                        $glibc_minor = trim($line[2]);
                        if (isset($glibc_major)) {
                            break;
                        }
                        continue;
                    }
                }
                fclose($features_file);
                if (!isset($glibc_major) || !isset($glibc_minor)) {
                    return $glibc = '';
                }
                return $glibc = 'glibc' . trim($glibc_major) . "." . trim($glibc_minor) ;
            } // no cpp
            $tmpfile = System::mktemp("glibctest");
            $fp = fopen($tmpfile, "w");
            fwrite($fp, "#include <features.h>\n__GLIBC__ __GLIBC_MINOR__\n");
            fclose($fp);
            $cpp = popen("/usr/bin/cpp $tmpfile", "r");
            while ($line = fgets($cpp, 1024)) {
                if ($line{0} == '#' || trim($line) == '') {
                    continue;
                }
                if (list($major, $minor) = explode(' ', trim($line))) {
                    break;
                }
            }
            pclose($cpp);
            unlink($tmpfile);
        } // features.h
        if (!($major && $minor) && @is_link('/lib/libc.so.6')) {
            // Let's try reading the libc.so.6 symlink
            if (ereg('^libc-(.*)\.so$', basename(readlink('/lib/libc.so.6')), $matches)) {
                list($major, $minor) = explode('.', $matches[1]);
            }
        }
        if (!($major && $minor)) {
            return $glibc = '';
        }
        return $glibc = "glibc{$major}.{$minor}";
    }

    function getSignature()
    {
        if (empty($this->extra)) {
            return "{$this->sysname}-{$this->release}-{$this->cpu}";
        }
        return "{$this->sysname}-{$this->release}-{$this->cpu}-{$this->extra}";
    }

    function getSysname()
    {
        return $this->sysname;
    }

    function getNodename()
    {
        return $this->nodename;
    }

    function getCpu()
    {
        return $this->cpu;
    }

    function getRelease()
    {
        return $this->release;
    }

    function getExtra()
    {
        return $this->extra;
    }

    function matchSignature($match)
    {
        if (is_array($match)) {
            $fragments = $match;
        } else {
            $fragments = explode('-', $match);
        }
        $n = count($fragments);
        $matches = 0;
        if ($n > 0) {
            $matches += $this->_matchFragment($fragments[0], $this->sysname);
        }
        if ($n > 1) {
            $matches += $this->_matchFragment($fragments[1], $this->release);
        }
        if ($n > 2) {
            $matches += $this->_matchFragment($fragments[2], $this->cpu);
        }
        if ($n > 3) {
            $matches += $this->_matchFragment($fragments[3], $this->extra);
        }
        return ($matches == $n);
    }

    function _matchFragment($fragment, $value)
    {
        if (strcspn($fragment, '*?') < strlen($fragment)) {
            $reg = '^' . str_replace(array('*', '?', '/'), array('.*', '.', '\\/'), $fragment) . '$';
            return eregi($reg, $value);
        }
        return ($fragment == '*' || !strcasecmp($fragment, $value));
    }

}
/*
 * Local Variables:
 * indent-tabs-mode: nil
 * c-basic-offset: 4
 * End:
 */
?>
package.xml100644   1750   1750      104543 11014312742   6457 <?xml version="1.0" encoding="UTF-8"?>
<package packagerversion="1.7.2" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
 <name>PEAR</name>
 <channel>pear.php.net</channel>
 <summary>PEAR Base System</summary>
 <description>The PEAR package contains:
 * the PEAR installer, for creating, distributing
   and installing packages
 * the PEAR_Exception PHP5 error handling mechanism
 * the PEAR_ErrorStack advanced error handling mechanism
 * the PEAR_Error error handling mechanism
 * the OS_Guess class for retrieving info about the OS
   where PHP is running on
 * the System class for quick handling of common operations
   with files and directories
 * the PEAR base class

  Features in a nutshell:
  * full support for channels
  * pre-download dependency validation
  * new package.xml 2.0 format allows tremendous flexibility while maintaining BC
  * support for optional dependency groups and limited support for sub-packaging
  * robust dependency support
  * full dependency validation on uninstall
  * remote install for hosts with only ftp access - no more problems with
    restricted host installation
  * full support for mirroring
  * support for bundling several packages into a single tarball
  * support for static dependencies on a url-based package
  * support for custom file roles and installation tasks</description>
 <lead>
  <name>Greg Beaver</name>
  <user>cellog</user>
  <email>cellog@php.net</email>
  <active>yes</active>
 </lead>
 <lead>
  <name>Pierre-Alain Joye</name>
  <user>pajoye</user>
  <email>pierre@php.net</email>
  <active>no</active>
 </lead>
 <lead>
  <name>Stig Bakken</name>
  <user>ssb</user>
  <email>stig@php.net</email>
  <active>no</active>
 </lead>
 <lead>
  <name>Tomas V.V.Cox</name>
  <user>cox</user>
  <email>cox@idecnet.com</email>
  <active>no</active>
 </lead>
 <developer>
  <name>Helgi Thormar</name>
  <user>dufuz</user>
  <email>dufuz@php.net</email>
  <active>yes</active>
 </developer>
 <developer>
  <name>Tias Guns</name>
  <user>tias</user>
  <email>tias@php.net</email>
  <active>yes</active>
 </developer>
 <helper>
  <name>Tim Jackson</name>
  <user>timj</user>
  <email>timj@php.net</email>
  <active>no</active>
 </helper>
 <helper>
  <name>Bertrand Gugger</name>
  <user>toggg</user>
  <email>toggg@php.net</email>
  <active>no</active>
 </helper>
 <helper>
  <name>Martin Jansen</name>
  <user>mj</user>
  <email>mj@php.net</email>
  <active>no</active>
 </helper>
 <date>2008-05-19</date>
 <time>09:59:46</time>
 <version>
  <release>1.7.2</release>
  <api>1.7.0</api>
 </version>
 <stability>
  <release>stable</release>
  <api>stable</api>
 </stability>
 <license uri="http://www.php.net/license">PHP License</license>
 <notes>* Implement Request #13375: install-pear.php should have option to set cfg_dir [timj]
* Fix Bug #12945 PEAR_Registry::setConfig() does not set install path [cweiske/cellog]
* Fix Bug #12959 PEAR should give warning when doing &quot;special&quot; handling with cfg role [cellog]
* Fix Bug #12960 role=cfg should automatically replace file if it has not been modified [cellog]
* Fix Bug #13031 PEAR fails to authenticate when doing upgrade-all on non-default channel [timj]
* Fix Bug #13033 Signature to PEAR_REST_XX::listAll() changes between REST1.0 and 1.1 [timj]
* Fix Bug #13059 Invalid release type causes Undefined Property notice [gwynne, dufuz]
* Fix Bug #13123 pear config-set works, but pecl config-set does not [cellog]
* Fix Bug #13323 Deprecated errors in many places [hubbitus]
* Fix Bug #13315 Upgrading of package fails if more than one file of role=cfg has changed [timj]
* Fix Bug #13386 &quot;pear install --register-only&quot; fails with cfg role [cellog]
* Fix Bug #13434 Breaks on HPUX - &quot;find () -ls&quot; doesn&apos;t work [cellog]
* Fix Bug #13484 pear package brings up warnings [dufuz]
* Fix Bug #13485 pear package-validate brings up ASCII issues [dufuz]
* Fix Bug #13508 undefined index errors when installing a remote bundle [cellog/tswicegood]
* Fix Bug #13525 pear package-validate is not showing warnings even if there are [dufuz]
* Fix Bug #13559 installing package fails via proxy to virtual-hosted server. [hirose]
* Fix Bug #13691 Pear doesn&apos;t remove old directories on upgrade [cellog]
* Fix Bug #13870 Notice uninstalling or using bundles [cellog]
* Fix Bug #13883 file permissions not preserved on packaging [cellog]</notes>
 <contents>
  <dir name="/">
   <file md5sum="4fa6b540f217ae9dbab8975f8b93d200" name="OS/Guess.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="2da630f82eca1531a57fe04696e26a06" name="PEAR/ChannelFile/Parser.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="381496e0186e16d613ddb01564fca4a0" name="PEAR/Command/Auth.xml" role="php" />
   <file md5sum="2bd9a76a6932fe9fb4fe80d06fd099d4" name="PEAR/Command/Auth.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="73602fd7f051eaf8d37452d0e3063bdb" name="PEAR/Command/Build.xml" role="php" />
   <file md5sum="2af3d1912b4b6aef6bf17db5e01f257e" name="PEAR/Command/Build.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="5dfb7898501c43ab655c5f9b7176ea20" name="PEAR/Command/Channels.xml" role="php" />
   <file md5sum="5876be616c6e401fd911fb6f5c50b102" name="PEAR/Command/Channels.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="22079bbfa5f8ac010683cb0cd84af21f" name="PEAR/Command/Common.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="91f189cb9423b5e87ee0abc5ea1a2be3" name="PEAR/Command/Config.xml" role="php" />
   <file md5sum="8d36af1a687b1648a474008e6e2b0239" name="PEAR/Command/Config.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="e0f361c6ebda100653e550a762a66a06" name="PEAR/Command/Install.xml" role="php" />
   <file md5sum="b1954beca05df6a53595112ca12f2cad" name="PEAR/Command/Install.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="5cb62a04c0a268f4edd64a49a3895c92" name="PEAR/Command/Mirror.xml" role="php" />
   <file md5sum="b1c6d624fa98d7f95948e1bca76c46b5" name="PEAR/Command/Mirror.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="bbf88f26cd10b1caa76d5eec474f093f" name="PEAR/Command/Package.xml" role="php" />
   <file md5sum="cb6f4b8cf4f297200d1ddb35b768d65f" name="PEAR/Command/Package.php" role="php">
    <tasks:replace from="@DATA-DIR@" to="data_dir" type="pear-config" />
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="29c0947f423695818f90759e65376e5c" name="PEAR/Command/Pickle.xml" role="php" />
   <file md5sum="b7777e69e2b771a9f691fb4fade4686a" name="PEAR/Command/Pickle.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="49b046cfc14747f0365e02e9c3f0e6dc" name="PEAR/Command/Registry.xml" role="php" />
   <file md5sum="0e6a7070d8b3bfe5160255b5f5352425" name="PEAR/Command/Registry.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="05729e322af29b0ddcdd85559e44cd41" name="PEAR/Command/Remote.xml" role="php" />
   <file md5sum="61cf1731830f4adbb51335a890570b8a" name="PEAR/Command/Remote.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="118af6fbf9c37e90957f0bb9d5cf9d82" name="PEAR/Command/Test.xml" role="php" />
   <file md5sum="9bd7498ec025144d036a1ca34b0f4365" name="PEAR/Command/Test.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="4ee3dab326b3f108574d001ca74f1620" name="PEAR/Downloader/Package.php" role="php">
    <tasks:replace from="@PEAR-VER@" to="version" type="package-info" />
   </file>
   <file md5sum="4d43b606e210a61e003fcea2a46d0fd4" name="PEAR/Frontend/CLI.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="82094070511d6369af82529bb8194c8b" name="PEAR/Installer/Role/Common.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="d8c62e6275e3aaa7784290912406092c" name="PEAR/Installer/Role/Cfg.xml" role="php" />
   <file md5sum="3fb2045c4328ba0f0791e6c46aabce8a" name="PEAR/Installer/Role/Cfg.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="89a4a2a286e842d45a98974f40a0565c" name="PEAR/Installer/Role/Data.xml" role="php" />
   <file md5sum="d505eea75d38763c94f92e639580201a" name="PEAR/Installer/Role/Data.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="b1ce0fe105251c3b75209d6518ee69ac" name="PEAR/Installer/Role/Doc.xml" role="php" />
   <file md5sum="d381e479248b847e1fba152dc95f10d4" name="PEAR/Installer/Role/Doc.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="af71c0ad42d16a323afe24a4f884ef15" name="PEAR/Installer/Role/Ext.xml" role="php" />
   <file md5sum="205b68b09e62134f965c16bdac37bcc7" name="PEAR/Installer/Role/Ext.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="ef88f0321d3e481c2130c95122cf76d8" name="PEAR/Installer/Role/Php.xml" role="php" />
   <file md5sum="284067a202f05fc1b62981d7c0b10419" name="PEAR/Installer/Role/Php.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="746461dc3b48af6d24094cb0211608f2" name="PEAR/Installer/Role/Script.xml" role="php" />
   <file md5sum="c879348f293100cc0c78220c4de6d711" name="PEAR/Installer/Role/Script.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="e147d63f168ea156fc2be38caaa63804" name="PEAR/Installer/Role/Src.xml" role="php" />
   <file md5sum="e0200455cde8eba003dff160b3657c43" name="PEAR/Installer/Role/Src.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="a24b596ec987aa5688fc19e8ed4e97ea" name="PEAR/Installer/Role/Test.xml" role="php" />
   <file md5sum="47f59482d09100bc9ad7e137785855e7" name="PEAR/Installer/Role/Test.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="7641e71c5785bb33a4261ebe25ed0fd7" name="PEAR/Installer/Role/Www.xml" role="php" />
   <file md5sum="0076ccbaa7a6208559df11aa3eb798b5" name="PEAR/Installer/Role/Www.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="273540b734ed43343649c416a4f52a99" name="PEAR/Installer/Role.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="9ff6a4888868c72abf2433f8dfc40be6" name="PEAR/PackageFile/Generator/v1.php" role="php">
    <tasks:replace from="@PEAR-VER@" to="version" type="package-info" />
   </file>
   <file md5sum="b23f0359cd3306c87bd027eefff38892" name="PEAR/PackageFile/Generator/v2.php" role="php">
    <tasks:replace from="@PEAR-VER@" to="version" type="package-info" />
   </file>
   <file md5sum="e439d694e4e2c3b4146ee308a2758f5a" name="PEAR/PackageFile/Parser/v1.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="9fdba40240a1a72820780b5d6184391b" name="PEAR/PackageFile/Parser/v2.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="c56d70ed9adb2071e530142ed6d296ab" name="PEAR/PackageFile/v2/rw.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="85530152fe2098b549ef8e9cbc727f8c" name="PEAR/PackageFile/v2/Validator.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="63a79337dfe25c16aee1ce9b136f7044" name="PEAR/PackageFile/v1.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="1fdb0bd1548aafec891aa6d9580ea59b" name="PEAR/PackageFile/v2.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="3284fc0ec89030c9d841200beecbf4fa" name="PEAR/REST/10.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="71d7072f1cdf9f2065205f4585a2ecc6" name="PEAR/REST/11.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="619b085112e10287e66197cc4fc6acaa" name="PEAR/REST/13.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="9339412e86586c299727121e08adefba" name="PEAR/Task/Postinstallscript/rw.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="9fe592cd22a83915d796a11af6331606" name="PEAR/Task/Replace/rw.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="3c7a6eb3a4e207f665f06093cb8bcdf9" name="PEAR/Task/Unixeol/rw.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="1a390d0f5b489d7f61f9e6682203bcc3" name="PEAR/Task/Windowseol/rw.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="b5a728f8d59013ab6d5077f27b517a96" name="PEAR/Task/Common.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="b9e5cc9fed2ebad6d2011ee6a4d592bf" name="PEAR/Task/Postinstallscript.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="5a3d680bb9c327530f696d830b8b605b" name="PEAR/Task/Replace.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="f9dc0e5d8d8957ea207d9da8e37dcb26" name="PEAR/Task/Unixeol.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="2ce493f5d43d7df681a1b45034060904" name="PEAR/Task/Windowseol.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="2ae06a82c9f955f68c31fdc11bf217c5" name="PEAR/Validator/PECL.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="9cd95af3af7654b473842723cc7c1bec" name="PEAR/Autoloader.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="062dc2dba3349a8f2cadb9e81320cfdb" name="PEAR/Builder.php" role="php">
    <tasks:replace from="@PEAR-VER@" to="version" type="package-info" />
   </file>
   <file md5sum="54638616d9fae90609ef027430b86656" name="PEAR/ChannelFile.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="7147a5789e7f15c85f3b23d4ffb6b236" name="PEAR/Command.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="a7ec0f20c252a9cb436968bc9f5635da" name="PEAR/Common.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="d18ae493cc76570cecff6d6dddc24d3b" name="PEAR/Config.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="bcfff6330848fa83118e1ff11b3d7ba0" name="PEAR/Dependency.php" role="php" />
   <file md5sum="f48a05aee6a88319eb2b45a9a649b0f0" name="PEAR/DependencyDB.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="893b4e4250ca595dc41c596a60e36de2" name="PEAR/Dependency2.php" role="php">
    <tasks:replace from="@PEAR-VER@" to="version" type="package-info" />
   </file>
   <file md5sum="c631755222fd7ae155cce369ae46f929" name="PEAR/Downloader.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="cf883c744c4be529a3a75b235608eaa4" name="PEAR/ErrorStack.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="60040e0928e18981a9ccb036e54db840" name="PEAR/Exception.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="e0e4cbcec4a972fbad779d0f9d323120" name="PEAR/FixPHP5PEARWarnings.php" role="php" />
   <file md5sum="d26049bf4358619459eac6bd08e0c61e" name="PEAR/Frontend.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="828e42237cd8b0e04a2cf0a5b13099f2" name="PEAR/Installer.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="4546fbff936d36496c30227f20d5b6a7" name="PEAR/PackageFile.php" role="php">
    <tasks:replace from="@PEAR-VER@" to="version" type="package-info" />
   </file>
   <file md5sum="c1708307d9a6037f60ef4cf130b8c1d9" name="PEAR/Packager.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="eb579ef0fbff7c72a2f272a90550337e" name="PEAR/Registry.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="ac90346df7467a1b43b9392069a87750" name="PEAR/Remote.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="b8c5c610bcf441261b6101c6ed176e96" name="PEAR/REST.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="3345c49c04db53a8424a25985bd69633" name="PEAR/RunTest.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="2f97edd3496adace0a0e5c9f1b70ae76" name="PEAR/Validate.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="dab39738e174b584eebc71bd3ece7a91" name="PEAR/XMLParser.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file baseinstalldir="/" md5sum="bd65b087b7707463525e9f0092337793" name="scripts/pear.bat" role="script">
    <tasks:replace from="@bin_dir@" to="bin_dir" type="pear-config" />
    <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
    <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
    <tasks:windowseol />
   </file>
   <file baseinstalldir="/" md5sum="f92ee8acc4f00a7ca9ffedc1fe959b69" name="scripts/peardev.bat" role="script">
    <tasks:replace from="@bin_dir@" to="bin_dir" type="pear-config" />
    <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
    <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
    <tasks:windowseol />
   </file>
   <file baseinstalldir="/" md5sum="34c1cb834dd1c03c9e40998b201d52e0" name="scripts/pecl.bat" role="script">
    <tasks:replace from="@bin_dir@" to="bin_dir" type="pear-config" />
    <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
    <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
    <tasks:windowseol />
   </file>
   <file baseinstalldir="/" md5sum="5b495a3de3c6092bfbd93806937a0e4e" name="scripts/pear.sh" role="script">
    <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
    <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" />
    <tasks:replace from="@pear_version@" to="version" type="package-info" />
    <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
    <tasks:unixeol />
   </file>
   <file baseinstalldir="/" md5sum="c69b7eb6cf9198ef8f03a19dcb57ca42" name="scripts/peardev.sh" role="script">
    <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
    <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" />
    <tasks:replace from="@pear_version@" to="version" type="package-info" />
    <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
    <tasks:unixeol />
   </file>
   <file baseinstalldir="/" md5sum="d00c55f2aa48052c25db271e044e7551" name="scripts/pecl.sh" role="script">
    <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
    <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" />
    <tasks:replace from="@pear_version@" to="version" type="package-info" />
    <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
    <tasks:unixeol />
   </file>
   <file baseinstalldir="/" md5sum="a398d2c6875a38dc38aa399f3fa12e49" name="scripts/pearcmd.php" role="php">
    <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
    <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" />
    <tasks:replace from="@pear_version@" to="version" type="package-info" />
    <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
   </file>
   <file baseinstalldir="/" md5sum="6356b5beefa80d0bbfd234d222873c7d" name="scripts/peclcmd.php" role="php">
    <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
    <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" />
    <tasks:replace from="@pear_version@" to="version" type="package-info" />
    <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
   </file>
   <file md5sum="671e6690634062ee521d2027a9971f22" name="INSTALL" role="doc" />
   <file md5sum="ca444da9174e05f8a0dc71d8ee47900f" name="package.dtd" role="data" />
   <file md5sum="80921dfa70e0699a934c7d21f60b60c7" name="PEAR.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="ae70dd96347165e048bd37521d6437f2" name="README" role="doc" />
   <file md5sum="43762dbc126b34e811326bacb76e8609" name="System.php" role="php">
    <tasks:replace from="@package_version@" to="version" type="package-info" />
   </file>
   <file md5sum="acd010e3bc43c0f72df584acde7b9158" name="template.spec" role="data" />
  </dir>
 </contents>
 <dependencies>
  <required>
   <php>
    <min>4.3.0</min>
   </php>
   <pearinstaller>
    <min>1.4.3</min>
   </pearinstaller>
   <package>
    <name>Archive_Tar</name>
    <channel>pear.php.net</channel>
    <min>1.1</min>
    <recommended>1.3.2</recommended>
    <exclude>1.3.0</exclude>
   </package>
   <package>
    <name>Structures_Graph</name>
    <channel>pear.php.net</channel>
    <min>1.0.2</min>
    <recommended>1.0.2</recommended>
   </package>
   <package>
    <name>Console_Getopt</name>
    <channel>pear.php.net</channel>
    <min>1.2</min>
    <recommended>1.2.3</recommended>
   </package>
   <package>
    <name>PEAR_Frontend_Web</name>
    <channel>pear.php.net</channel>
    <max>0.4</max>
    <conflicts />
   </package>
   <package>
    <name>PEAR_Frontend_Gtk</name>
    <channel>pear.php.net</channel>
    <max>0.4.0</max>
    <exclude>0.4.0</exclude>
    <conflicts />
   </package>
   <extension>
    <name>xml</name>
   </extension>
   <extension>
    <name>pcre</name>
   </extension>
  </required>
  <optional>
   <package>
    <name>XML_RPC</name>
    <channel>pear.php.net</channel>
    <min>1.4.0</min>
   </package>
  </optional>
  <group hint="PEAR&apos;s web-based installer" name="webinstaller">
   <package>
    <name>PEAR_Frontend_Web</name>
    <channel>pear.php.net</channel>
    <min>0.5.1</min>
   </package>
  </group>
  <group hint="PEAR&apos;s PHP-GTK-based installer" name="gtkinstaller">
   <package>
    <name>PEAR_Frontend_Gtk</name>
    <channel>pear.php.net</channel>
    <min>0.4.0</min>
   </package>
  </group>
  <group hint="PEAR&apos;s PHP-GTK2-based installer" name="gtk2installer">
   <package>
    <name>PEAR_Frontend_Gtk2</name>
    <channel>pear.php.net</channel>
   </package>
  </group>
 </dependencies>
 <phprelease>
  <installconditions>
   <os>
    <name>windows</name>
   </os>
  </installconditions>
  <filelist>
   <install as="pear.bat" name="scripts/pear.bat" />
   <install as="peardev.bat" name="scripts/peardev.bat" />
   <install as="pecl.bat" name="scripts/pecl.bat" />
   <install as="pearcmd.php" name="scripts/pearcmd.php" />
   <install as="peclcmd.php" name="scripts/peclcmd.php" />
   <ignore name="scripts/peardev.sh" />
   <ignore name="scripts/pear.sh" />
   <ignore name="scripts/pecl.sh" />
  </filelist>
 </phprelease>
 <phprelease>
  <filelist>
   <install as="pear" name="scripts/pear.sh" />
   <install as="peardev" name="scripts/peardev.sh" />
   <install as="pecl" name="scripts/pecl.sh" />
   <install as="pearcmd.php" name="scripts/pearcmd.php" />
   <install as="peclcmd.php" name="scripts/peclcmd.php" />
   <ignore name="scripts/pear.bat" />
   <ignore name="scripts/peardev.bat" />
   <ignore name="scripts/pecl.bat" />
  </filelist>
 </phprelease>
 <changelog>
  <release>
   <version>
    <release>1.6.2</release>
    <api>1.6.0</api>
   </version>
   <stability>
    <release>stable</release>
    <api>stable</api>
   </stability>
   <date>2007-09-09</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>Minor bugfix release
* fix Bug #11420: warning on pecl (un)install with --register-only option [cellog]
* fix Bug #11481: PEAR_PackageFile_Parser_v1 skips single-char directories [pmjones]
* fix Bug #11517: Error : download directory &quot;/var/cache/php-pear&quot;
  is not writeable. [remicollet]
* fix Bug #11616: Incorrect equality operator used when comparing md5 check sums [robham]
* fix Bug #11642: PEAR fails to authenticate when downloading deps from non-default
  channels [timj]
* fix Bug #11657: Installer generate bad &quot;dirtree&quot; using INSTALL_ROOT [remicollet]
* fix Bug #11678: Registry.php getChannel() deadlocks [cellog]
* fix Bug #11703: pear convert and package.xml with optional dependencies fails [cellog]
* fix Bug #11754: Error at upgrade-all command run [cellog]
* fix Bug #11861: uninstall of package did not delete directory created during install
  of package [cellog]
* fix Bug #11862: Notice: Array to string conversion in PEAR/PackageFile.php on line 433
  [cellog]
* fix Bug #11883: run-tests -u -p SomePackage should run the topmost
  &quot;AllTests.php&quot; file [cellog]
* fix Bug #11936: run-tests fails to preserve SYSTEMROOT environment variable [cellog]</notes>
  </release>
  <release>
   <version>
    <release>1.7.0RC1</release>
    <api>1.6.0</api>
   </version>
   <stability>
    <release>beta</release>
    <api>stable</api>
   </stability>
   <date>2007-12-10</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>* fix Bug #12116: Accept-Encoding not supported: breaks installer [jldupont]
* fix Bug #12162: config-create doesn&apos;t work after installing custom roles [cellog]
* fix Bug #12553: System::find() does not find exact filename matches [cellog/jorrit]
* fix Bug #12554: enableExtension() zeros php.ini in some instances [jlightsey]
* implement Request #11964: introduce www role, www_dir config variable [cellog]
* implement Request #12108: Add &quot;config&quot; (cfg) role [cellog]
* implement Request #12147: Avoid compile-time strict warnings [cellog]</notes>
  </release>
  <release>
   <version>
    <release>1.7.0RC2</release>
    <api>1.6.0</api>
   </version>
   <stability>
    <release>beta</release>
    <api>stable</api>
   </stability>
   <date>2007-01-03</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>**WARNING** MAJOR BC BREAK IN WWW ROLE**
The www role was installing into packagename/ and now installs into the root directory
of www_dir.  This is necessary to allow easy migration from applications already using
Role_Web

 fixed since 1.7.0RC1:
* fix Bug #12662: System::_parseArgs() should be declared statically [cellog]
* fix Bug #12661: System::mktemp needs to be declaired statically [cellog]
* implement PEAR_Error::__toString() as alias to getMessage() [cellog]
 fixed since 1.6.2:
* fix Bug #12116: Accept-Encoding not supported: breaks installer [jldupont]
* fix Bug #12162: config-create doesn&apos;t work after installing custom roles [cellog]
* fix Bug #12553: System::find() does not find exact filename matches [cellog/jorrit]
* fix Bug #12554: enableExtension() zeros php.ini in some instances [jlightsey]
* implement Request #11964: introduce www role, www_dir config variable [cellog]
* implement Request #12108: Add &quot;config&quot; (cfg) role [cellog]
* implement Request #12147: Avoid compile-time strict warnings [cellog]</notes>
  </release>
  <release>
   <version>
    <release>1.7.0</release>
    <api>1.7.0</api>
   </version>
   <stability>
    <release>stable</release>
    <api>stable</api>
   </stability>
   <date>2008-01-31</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>Minor feature addition release

Known bugs to be fixed in version 1.7.1:
 - Bug #12945 	PEAR_Registry::setConfig() does not set install path
 - Bug #12959 	PEAR should give warning when doing &quot;special&quot; handling with cfg role
 - Bug #12960 	role=cfg should automatically replace file if it has not been modified
 - Bug #13031 	PEAR fails to authenticate when doing upgrade-all on non-default channel
 - Bug #13033 	Signature to PEAR_REST_XX::listAll() changes between REST1.0 and 1.1

fixed since 1.7.0RC2
 * fix bug #13030: Port not set for REST HTTP requests [timj]
 * fix bug #13029: Duplicate Host headers set when requesting REST data [timj]
 * fix Bug #12987: improper calls to class_exists [weirdan]
 * fix Bug #12954: Host header missing when using proxy [flint]
 * fix Bug #12918: file tasks ignored for role=src [cellog]
 * fix Bug #12816: &lt;configureoption&gt; default isn&apos;t being parsed [jon]
 * fix Bug #12793: run-tests fails when using --ARGS-- in phpt test files [izi]
fixed since 1.7.0RC1:
 * fix Bug #12818: package.xml 1.0 parsing can drop lines [cellog]
 * fix Bug #12662: System::_parseArgs() should be declared statically [cellog]
 * fix Bug #12661: System::mktemp needs to be declaired statically [cellog]
 * implement PEAR_Error::__toString() as alias to getMessage() [cellog]
fixed since 1.6.2:
 * fix Bug #12116: Accept-Encoding not supported: breaks installer [jldupont]
 * fix Bug #12162: config-create doesn&apos;t work after installing custom roles [cellog]
 * fix Bug #12553: System::find() does not find exact filename matches [cellog/jorrit]
 * fix Bug #12554: enableExtension() zeros php.ini in some instances [jlightsey]
 * implement Request #11964: introduce www role, www_dir config variable [cellog]
 * implement Request #12108: Add &quot;config&quot; (cfg) role [cellog]
 * implement Request #12147: Avoid compile-time strict warnings [cellog]</notes>
  </release>
  <release>
   <version>
    <release>1.7.1</release>
    <api>1.7.0</api>
   </version>
   <stability>
    <release>stable</release>
    <api>stable</api>
   </stability>
   <date>2008-02-03</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>fixed since 1.7.0
 * fix bug #13030: Port not set for REST HTTP requests [timj]
 * fix bug #13029: Duplicate Host headers set when requesting REST data [timj]
 * fix bug #13047: PEAR fails to install *any* package [timj]

Known bugs to be fixed in version 1.7.2:
 - Bug #12945 	PEAR_Registry::setConfig() does not set install path
 - Bug #12959 	PEAR should give warning when doing &quot;special&quot; handling with cfg role
 - Bug #12960 	role=cfg should automatically replace file if it has not been modified
 - Bug #13031 	PEAR fails to authenticate when doing upgrade-all on non-default channel
 - Bug #13033 	Signature to PEAR_REST_XX::listAll() changes between REST1.0 and 1.1</notes>
  </release>
  <release>
   <version>
    <release>1.7.2</release>
    <api>1.7.0</api>
   </version>
   <stability>
    <release>stable</release>
    <api>stable</api>
   </stability>
   <date>2008-05-17</date>
   <license uri="http://www.php.net/license">PHP License</license>
   <notes>* Implement Request #13375: install-pear.php should have option to set cfg_dir [timj]
* Fix Bug #12945 PEAR_Registry::setConfig() does not set install path [cweiske/cellog]
* Fix Bug #12959 PEAR should give warning when doing &quot;special&quot; handling with cfg role [cellog]
* Fix Bug #12960 role=cfg should automatically replace file if it has not been modified [cellog]
* Fix Bug #13031 PEAR fails to authenticate when doing upgrade-all on non-default channel [timj]
* Fix Bug #13033 Signature to PEAR_REST_XX::listAll() changes between REST1.0 and 1.1 [timj]
* Fix Bug #13059 Invalid release type causes Undefined Property notice [gwynne, dufuz]
* Fix Bug #13123 pear config-set works, but pecl config-set does not [cellog]
* Fix Bug #13323 Deprecated errors in many places [hubbitus]
* Fix Bug #13315 Upgrading of package fails if more than one file of role=cfg has changed [timj]
* Fix Bug #13386 &quot;pear install --register-only&quot; fails with cfg role [cellog]
* Fix Bug #13434 Breaks on HPUX - &quot;find () -ls&quot; doesn&apos;t work [cellog]
* Fix Bug #13484 pear package brings up warnings [dufuz]
* Fix Bug #13485 pear package-validate brings up ASCII issues [dufuz]
* Fix Bug #13508 undefined index errors when installing a remote bundle [cellog/tswicegood]
* Fix Bug #13525 pear package-validate is not showing warnings even if there are [dufuz]
* Fix Bug #13559 installing package fails via proxy to virtual-hosted server. [hirose]
* Fix Bug #13691 Pear doesn&apos;t remove old directories on upgrade [cellog]
* Fix Bug #13870 Notice uninstalling or using bundles [cellog]
* Fix Bug #13883 file permissions not preserved on packaging [cellog]</notes>
  </release>
 </changelog>
</package>
PEAR-1.7.2/OS/Guess.php100644   1750   1750       25470 11014312742   7635 <?php
/**
 * The OS_Guess class
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Gregory Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Guess.php,v 1.26 2008/01/03 20:26:34 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since PEAR 0.1
 */

// {{{ uname examples

// php_uname() without args returns the same as 'uname -a', or a PHP-custom
// string for Windows.
// PHP versions prior to 4.3 return the uname of the host where PHP was built,
// as of 4.3 it returns the uname of the host running the PHP code.
//
// PC RedHat Linux 7.1:
// Linux host.example.com 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 2001 i686 unknown
//
// PC Debian Potato:
// Linux host 2.4.17 #2 SMP Tue Feb 12 15:10:04 CET 2002 i686 unknown
//
// PC FreeBSD 3.3:
// FreeBSD host.example.com 3.3-STABLE FreeBSD 3.3-STABLE #0: Mon Feb 21 00:42:31 CET 2000     root@example.com:/usr/src/sys/compile/CONFIG  i386
//
// PC FreeBSD 4.3:
// FreeBSD host.example.com 4.3-RELEASE FreeBSD 4.3-RELEASE #1: Mon Jun 25 11:19:43 EDT 2001     root@example.com:/usr/src/sys/compile/CONFIG  i386
//
// PC FreeBSD 4.5:
// FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb  6 23:59:23 CET 2002     root@example.com:/usr/src/sys/compile/CONFIG  i386
//
// PC FreeBSD 4.5 w/uname from GNU shellutils:
// FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb  i386 unknown
//
// HP 9000/712 HP-UX 10:
// HP-UX iq B.10.10 A 9000/712 2008429113 two-user license
//
// HP 9000/712 HP-UX 10 w/uname from GNU shellutils:
// HP-UX host B.10.10 A 9000/712 unknown
//
// IBM RS6000/550 AIX 4.3:
// AIX host 3 4 000003531C00
//
// AIX 4.3 w/uname from GNU shellutils:
// AIX host 3 4 000003531C00 unknown
//
// SGI Onyx IRIX 6.5 w/uname from GNU shellutils:
// IRIX64 host 6.5 01091820 IP19 mips
//
// SGI Onyx IRIX 6.5:
// IRIX64 host 6.5 01091820 IP19
//
// SparcStation 20 Solaris 8 w/uname from GNU shellutils:
// SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc
//
// SparcStation 20 Solaris 8:
// SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc SUNW,SPARCstation-20
//
// Mac OS X (Darwin)
// Darwin home-eden.local 7.5.0 Darwin Kernel Version 7.5.0: Thu Aug  5 19:26:16 PDT 2004; root:xnu/xnu-517.7.21.obj~3/RELEASE_PPC  Power Macintosh
//
// Mac OS X early versions
// 

// }}}

/* TODO:
 * - define endianness, to allow matchSignature("bigend") etc.
 */

/**
 * Retrieves information about the current operating system
 *
 * This class uses php_uname() to grok information about the current OS
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Gregory Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */
class OS_Guess
{
    var $sysname;
    var $nodename;
    var $cpu;
    var $release;
    var $extra;

    function OS_Guess($uname = null)
    {
        list($this->sysname,
             $this->release,
             $this->cpu,
             $this->extra,
             $this->nodename) = $this->parseSignature($uname);
    }

    function parseSignature($uname = null)
    {
        static $sysmap = array(
            'HP-UX' => 'hpux',
            'IRIX64' => 'irix',
        );
        static $cpumap = array(
            'i586' => 'i386',
            'i686' => 'i386',
            'ppc' => 'powerpc',
        );
        if ($uname === null) {
            $uname = php_uname();
        }
        $parts = split('[[:space:]]+', trim($uname));
        $n = count($parts);

        $release = $machine = $cpu = '';
        $sysname = $parts[0];
        $nodename = $parts[1];
        $cpu = $parts[$n-1];
        $extra = '';
        if ($cpu == 'unknown') {
            $cpu = $parts[$n-2];
        }

        switch ($sysname) {
            case 'AIX' :
                $release = "$parts[3].$parts[2]";
                break;
            case 'Windows' :
                switch ($parts[1]) {
                    case '95/98':
                        $release = '9x';
                        break;
                    default:
                        $release = $parts[1];
                        break;
                }
                $cpu = 'i386';
                break;
            case 'Linux' :
                $extra = $this->_detectGlibcVersion();
                // use only the first two digits from the kernel version
                $release = ereg_replace('^([[:digit:]]+\.[[:digit:]]+).*', '\1', $parts[2]);
                break;
            case 'Mac' :
                $sysname = 'darwin';
                $nodename = $parts[2];
                $release = $parts[3];
                if ($cpu == 'Macintosh') {
                    if ($parts[$n - 2] == 'Power') {
                        $cpu = 'powerpc';
                    }
                }
                break;
            case 'Darwin' :
                if ($cpu == 'Macintosh') {
                    if ($parts[$n - 2] == 'Power') {
                        $cpu = 'powerpc';
                    }
                }
                $release = ereg_replace('^([[:digit:]]+\.[[:digit:]]+).*', '\1', $parts[2]);
                break;
            default:
                $release = ereg_replace('-.*', '', $parts[2]);
                break;
        }


        if (isset($sysmap[$sysname])) {
            $sysname = $sysmap[$sysname];
        } else {
            $sysname = strtolower($sysname);
        }
        if (isset($cpumap[$cpu])) {
            $cpu = $cpumap[$cpu];
        }
        return array($sysname, $release, $cpu, $extra, $nodename);
    }

    function _detectGlibcVersion()
    {
        static $glibc = false;
        if ($glibc !== false) {
            return $glibc; // no need to run this multiple times
        }
        $major = $minor = 0;
        include_once "System.php";
        // Use glibc's <features.h> header file to
        // get major and minor version number:
        if (@file_exists('/usr/include/features.h') &&
              @is_readable('/usr/include/features.h')) {
            if (!@file_exists('/usr/bin/cpp') || !@is_executable('/usr/bin/cpp')) {
                $features_file = fopen('/usr/include/features.h', 'rb');
                while (!feof($features_file)) {
                    $line = fgets($features_file, 8192);
                    if (!$line || (strpos($line, '#define') === false)) {
                        continue;
                    }
                    if (strpos($line, '__GLIBC__')) {
                        // major version number #define __GLIBC__ version
                        $line = preg_split('/\s+/', $line);
                        $glibc_major = trim($line[2]);
                        if (isset($glibc_minor)) {
                            break;
                        }
                        continue;
                    }
                    if (strpos($line, '__GLIBC_MINOR__'))  {
                        // got the minor version number
                        // #define __GLIBC_MINOR__ version
                        $line = preg_split('/\s+/', $line);
                        $glibc_minor = trim($line[2]);
                        if (isset($glibc_major)) {
                            break;
                        }
                        continue;
                    }
                }
                fclose($features_file);
                if (!isset($glibc_major) || !isset($glibc_minor)) {
                    return $glibc = '';
                }
                return $glibc = 'glibc' . trim($glibc_major) . "." . trim($glibc_minor) ;
            } // no cpp
            $tmpfile = System::mktemp("glibctest");
            $fp = fopen($tmpfile, "w");
            fwrite($fp, "#include <features.h>\n__GLIBC__ __GLIBC_MINOR__\n");
            fclose($fp);
            $cpp = popen("/usr/bin/cpp $tmpfile", "r");
            while ($line = fgets($cpp, 1024)) {
                if ($line{0} == '#' || trim($line) == '') {
                    continue;
                }
                if (list($major, $minor) = explode(' ', trim($line))) {
                    break;
                }
            }
            pclose($cpp);
            unlink($tmpfile);
        } // features.h
        if (!($major && $minor) && @is_link('/lib/libc.so.6')) {
            // Let's try reading the libc.so.6 symlink
            if (ereg('^libc-(.*)\.so$', basename(readlink('/lib/libc.so.6')), $matches)) {
                list($major, $minor) = explode('.', $matches[1]);
            }
        }
        if (!($major && $minor)) {
            return $glibc = '';
        }
        return $glibc = "glibc{$major}.{$minor}";
    }

    function getSignature()
    {
        if (empty($this->extra)) {
            return "{$this->sysname}-{$this->release}-{$this->cpu}";
        }
        return "{$this->sysname}-{$this->release}-{$this->cpu}-{$this->extra}";
    }

    function getSysname()
    {
        return $this->sysname;
    }

    function getNodename()
    {
        return $this->nodename;
    }

    function getCpu()
    {
        return $this->cpu;
    }

    function getRelease()
    {
        return $this->release;
    }

    function getExtra()
    {
        return $this->extra;
    }

    function matchSignature($match)
    {
        if (is_array($match)) {
            $fragments = $match;
        } else {
            $fragments = explode('-', $match);
        }
        $n = count($fragments);
        $matches = 0;
        if ($n > 0) {
            $matches += $this->_matchFragment($fragments[0], $this->sysname);
        }
        if ($n > 1) {
            $matches += $this->_matchFragment($fragments[1], $this->release);
        }
        if ($n > 2) {
            $matches += $this->_matchFragment($fragments[2], $this->cpu);
        }
        if ($n > 3) {
            $matches += $this->_matchFragment($fragments[3], $this->extra);
        }
        return ($matches == $n);
    }

    function _matchFragment($fragment, $value)
    {
        if (strcspn($fragment, '*?') < strlen($fragment)) {
            $reg = '^' . str_replace(array('*', '?', '/'), array('.*', '.', '\\/'), $fragment) . '$';
            return eregi($reg, $value);
        }
        return ($fragment == '*' || !strcasecmp($fragment, $value));
    }

}
/*
 * Local Variables:
 * indent-tabs-mode: nil
 * c-basic-offset: 4
 * End:
 */
?>
PEAR-1.7.2/PEAR/ChannelFile/Parser.php100644   1750   1750        4120 11014312742  12326 <?php
/**
 * PEAR_ChannelFile_Parser for parsing channel.xml
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Parser.php,v 1.5 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * base xml parser class
 */
require_once 'PEAR/XMLParser.php';
require_once 'PEAR/ChannelFile.php';
/**
 * Parser for channel.xml
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_ChannelFile_Parser extends PEAR_XMLParser
{
    var $_config;
    var $_logger;
    var $_registry;

    function setConfig(&$c)
    {
        $this->_config = &$c;
        $this->_registry = &$c->getRegistry();
    }

    function setLogger(&$l)
    {
        $this->_logger = &$l;
    }

    function parse($data, $file)
    {
        if (PEAR::isError($err = parent::parse($data, $file))) {
            return $err;
        }
        $ret = new PEAR_ChannelFile;
        $ret->setConfig($this->_config);
        if (isset($this->_logger)) {
            $ret->setLogger($this->_logger);
        }
        $ret->fromArray($this->_unserializedData);
        // make sure the filelist is in the easy to read format needed
        $ret->flattenFilelist();
        $ret->setPackagefile($file, $archive);
        return $ret;
    }
}
?>PEAR-1.7.2/PEAR/Command/Auth.xml100644   1750   1750        1756 11014312742  11226 <commands version="1.0">
 <login>
  <summary>Connects and authenticates to remote server</summary>
  <shortcut>li</shortcut>
  <function>doLogin</function>
  <options />
  <doc>&lt;channel name&gt;
Log in to a remote channel server.  &lt;channel name&gt; is not supplied, 
the default channel is used. To use remote functions in the installer
that require any kind of privileges, you need to log in first.  The
username and password you enter here will be stored in your per-user
PEAR configuration (~/.pearrc on Unix-like systems).  After logging
in, your username and password will be sent along in subsequent
operations on the remote server.</doc>
 </login>
 <logout>
  <summary>Logs out from the remote server</summary>
  <shortcut>lo</shortcut>
  <function>doLogout</function>
  <options />
  <doc>
Logs out from the remote server.  This command does not actually
connect to the remote server, it only deletes the stored username and
password from your user configuration.</doc>
 </logout>
</commands>PEAR-1.7.2/PEAR/Command/Auth.php100644   1750   1750       14202 11014312742  11223 <?php
/**
 * PEAR_Command_Auth (login, logout commands)
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Auth.php,v 1.31 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * base class
 */
require_once 'PEAR/Command/Common.php';
require_once 'PEAR/Config.php';

/**
 * PEAR commands for login/logout
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */
class PEAR_Command_Auth extends PEAR_Command_Common
{
    // {{{ properties

    var $commands = array(
        'login' => array(
            'summary' => 'Connects and authenticates to remote server',
            'shortcut' => 'li',
            'function' => 'doLogin',
            'options' => array(),
            'doc' => '<channel name>
Log in to a remote channel server.  If <channel name> is not supplied, 
the default channel is used. To use remote functions in the installer
that require any kind of privileges, you need to log in first.  The
username and password you enter here will be stored in your per-user
PEAR configuration (~/.pearrc on Unix-like systems).  After logging
in, your username and password will be sent along in subsequent
operations on the remote server.',
            ),
        'logout' => array(
            'summary' => 'Logs out from the remote server',
            'shortcut' => 'lo',
            'function' => 'doLogout',
            'options' => array(),
            'doc' => '
Logs out from the remote server.  This command does not actually
connect to the remote server, it only deletes the stored username and
password from your user configuration.',
            )

        );

    // }}}

    // {{{ constructor

    /**
     * PEAR_Command_Auth constructor.
     *
     * @access public
     */
    function PEAR_Command_Auth(&$ui, &$config)
    {
        parent::PEAR_Command_Common($ui, $config);
    }

    // }}}

    // {{{ doLogin()

    /**
     * Execute the 'login' command.
     *
     * @param string $command command name
     *
     * @param array $options option_name => value
     *
     * @param array $params list of additional parameters
     *
     * @return bool TRUE on success or
     * a PEAR error on failure
     *
     * @access public
     */
    function doLogin($command, $options, $params)
    {
        $reg = &$this->config->getRegistry();
        
        // If a parameter is supplied, use that as the channel to log in to
        if (isset($params[0])) {
            $channel = $params[0];
        } else {
            $channel = $this->config->get('default_channel');
        }
        
        $chan = $reg->getChannel($channel);
        if (PEAR::isError($chan)) {
            return $this->raiseError($chan);
        }
        $server = $this->config->get('preferred_mirror', null, $channel);
        $remote = &$this->config->getRemote();
        $username = $this->config->get('username', null, $channel);
        if (empty($username)) {
            $username = isset($_ENV['USER']) ? $_ENV['USER'] : null;
        }
        $this->ui->outputData("Logging in to $server.", $command);
        
        list($username, $password) = $this->ui->userDialog(
            $command,
            array('Username', 'Password'),
            array('text',     'password'),
            array($username,  '')
            );
        $username = trim($username);
        $password = trim($password);

        $ourfile = $this->config->getConfFile('user');
        if (!$ourfile) {
            $ourfile = $this->config->getConfFile('system');
        }

        $this->config->set('username', $username, 'user', $channel);
        $this->config->set('password', $password, 'user', $channel);

        if ($chan->supportsREST()) {
            $ok = true;
        } else {
            $remote->expectError(401);
            $ok = $remote->call('logintest');
            $remote->popExpect();
        }
        if ($ok === true) {
            $this->ui->outputData("Logged in.", $command);
            // avoid changing any temporary settings changed with -d
            $ourconfig = new PEAR_Config($ourfile, $ourfile);
            $ourconfig->set('username', $username, 'user', $channel);
            $ourconfig->set('password', $password, 'user', $channel);
            $ourconfig->store();
        } else {
            return $this->raiseError("Login failed!");
        }
        return true;
    }

    // }}}
    // {{{ doLogout()

    /**
     * Execute the 'logout' command.
     *
     * @param string $command command name
     *
     * @param array $options option_name => value
     *
     * @param array $params list of additional parameters
     *
     * @return bool TRUE on success or
     * a PEAR error on failure
     *
     * @access public
     */
    function doLogout($command, $options, $params)
    {
        $reg = &$this->config->getRegistry();
        $channel = $this->config->get('default_channel');
        $chan = $reg->getChannel($channel);
        if (PEAR::isError($chan)) {
            return $this->raiseError($chan);
        }
        $server = $this->config->get('preferred_mirror');
        $this->ui->outputData("Logging out from $server.", $command);
        $this->config->remove('username');
        $this->config->remove('password');
        $this->config->store();
        return true;
    }

    // }}}
}

?>PEAR-1.7.2/PEAR/Command/Build.xml100644   1750   1750         404 11014312742  11331 <commands version="1.0">
 <build>
  <summary>Build an Extension From C Source</summary>
  <function>doBuild</function>
  <shortcut>b</shortcut>
  <options />
  <doc>[package.xml]
Builds one or more extensions contained in a package.</doc>
 </build>
</commands>PEAR-1.7.2/PEAR/Command/Build.php100644   1750   1750        5430 11014312742  11344 <?php
/**
 * PEAR_Command_Auth (build command)
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V.V.Cox <cox@idecnet.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Build.php,v 1.14 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * base class
 */
require_once 'PEAR/Command/Common.php';

/**
 * PEAR commands for building extensions.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V.V.Cox <cox@idecnet.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */
class PEAR_Command_Build extends PEAR_Command_Common
{
    // {{{ properties

    var $commands = array(
        'build' => array(
            'summary' => 'Build an Extension From C Source',
            'function' => 'doBuild',
            'shortcut' => 'b',
            'options' => array(),
            'doc' => '[package.xml]
Builds one or more extensions contained in a package.'
            ),
        );

    // }}}

    // {{{ constructor

    /**
     * PEAR_Command_Build constructor.
     *
     * @access public
     */
    function PEAR_Command_Build(&$ui, &$config)
    {
        parent::PEAR_Command_Common($ui, $config);
    }

    // }}}

    // {{{ doBuild()

    function doBuild($command, $options, $params)
    {
        require_once 'PEAR/Builder.php';
        if (sizeof($params) < 1) {
            $params[0] = 'package.xml';
        }
        $builder = &new PEAR_Builder($this->ui);
        $this->debug = $this->config->get('verbose');
        $err = $builder->build($params[0], array(&$this, 'buildCallback'));
        if (PEAR::isError($err)) {
            return $err;
        }
        return true;
    }

    // }}}
    // {{{ buildCallback()

    function buildCallback($what, $data)
    {
        if (($what == 'cmdoutput' && $this->debug > 1) ||
            ($what == 'output' && $this->debug > 0)) {
            $this->ui->outputData(rtrim($data), 'build');
        }
    }

    // }}}
}
PEAR-1.7.2/PEAR/Command/Channels.xml100644   1750   1750        6030 11014312742  12046 <commands version="1.0">
 <list-channels>
  <summary>List Available Channels</summary>
  <function>doList</function>
  <shortcut>lc</shortcut>
  <options />
  <doc>
List all available channels for installation.
</doc>
 </list-channels>
 <update-channels>
  <summary>Update the Channel List</summary>
  <function>doUpdateAll</function>
  <shortcut>uc</shortcut>
  <options />
  <doc>
List all installed packages in all channels.
</doc>
 </update-channels>
 <channel-delete>
  <summary>Remove a Channel From the List</summary>
  <function>doDelete</function>
  <shortcut>cde</shortcut>
  <options />
  <doc>&lt;channel name&gt;
Delete a channel from the registry.  You may not
remove any channel that has installed packages.
</doc>
 </channel-delete>
 <channel-add>
  <summary>Add a Channel</summary>
  <function>doAdd</function>
  <shortcut>ca</shortcut>
  <options />
  <doc>&lt;channel.xml&gt;
Add a private channel to the channel list.  Note that all
public channels should be synced using &quot;update-channels&quot;.
Parameter may be either a local file or remote URL to a
channel.xml.
</doc>
 </channel-add>
 <channel-update>
  <summary>Update an Existing Channel</summary>
  <function>doUpdate</function>
  <shortcut>cu</shortcut>
  <options>
   <force>
    <shortopt>f</shortopt>
    <doc>will force download of new channel.xml if an existing channel name is used</doc>
   </force>
   <channel>
    <shortopt>c</shortopt>
    <arg>CHANNEL</arg>
    <doc>will force download of new channel.xml if an existing channel name is used</doc>
   </channel>
  </options>
  <doc>[&lt;channel.xml&gt;|&lt;channel name&gt;]
Update a channel in the channel list directly.  Note that all
public channels can be synced using &quot;update-channels&quot;.
Parameter may be a local or remote channel.xml, or the name of
an existing channel.
</doc>
 </channel-update>
 <channel-info>
  <summary>Retrieve Information on a Channel</summary>
  <function>doInfo</function>
  <shortcut>ci</shortcut>
  <options />
  <doc>&lt;package&gt;
List the files in an installed package.
</doc>
 </channel-info>
 <channel-alias>
  <summary>Specify an alias to a channel name</summary>
  <function>doAlias</function>
  <shortcut>cha</shortcut>
  <options />
  <doc>&lt;channel&gt; &lt;alias&gt;
Specify a specific alias to use for a channel name.
The alias may not be an existing channel name or
alias.
</doc>
 </channel-alias>
 <channel-discover>
  <summary>Initialize a Channel from its server</summary>
  <function>doDiscover</function>
  <shortcut>di</shortcut>
  <options />
  <doc>[&lt;channel.xml&gt;|&lt;channel name&gt;]
Initialize a channel from its server and create a local channel.xml.
If &lt;channel name&gt; is in the format &quot;&lt;username&gt;:&lt;password&gt;@&lt;channel&gt;&quot; then
&lt;username&gt; and &lt;password&gt; will be set as the login username/password for
&lt;channel&gt;. Use caution when passing the username/password in this way, as
it may allow other users on your computer to briefly view your username/
password via the system's process list.
</doc>
 </channel-discover>
</commands>
PEAR-1.7.2/PEAR/Command/Channels.php100644   1750   1750       73163 11014312742  12070 <?php
// /* vim: set expandtab tabstop=4 shiftwidth=4: */
/**
 * PEAR_Command_Channels (list-channels, update-channels, channel-delete, channel-add,
 * channel-update, channel-info, channel-alias, channel-discover commands)
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Channels.php,v 1.57 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * base class
 */
require_once 'PEAR/Command/Common.php';

/**
 * PEAR commands for managing channels.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Command_Channels extends PEAR_Command_Common
{
    // {{{ properties

    var $commands = array(
        'list-channels' => array(
            'summary' => 'List Available Channels',
            'function' => 'doList',
            'shortcut' => 'lc',
            'options' => array(),
            'doc' => '
List all available channels for installation.
',
            ),
        'update-channels' => array(
            'summary' => 'Update the Channel List',
            'function' => 'doUpdateAll',
            'shortcut' => 'uc',
            'options' => array(),
            'doc' => '
List all installed packages in all channels.
'
            ),
        'channel-delete' => array(
            'summary' => 'Remove a Channel From the List',
            'function' => 'doDelete',
            'shortcut' => 'cde',
            'options' => array(),
            'doc' => '<channel name>
Delete a channel from the registry.  You may not
remove any channel that has installed packages.
'
            ),
        'channel-add' => array(
            'summary' => 'Add a Channel',
            'function' => 'doAdd',
            'shortcut' => 'ca',
            'options' => array(),
            'doc' => '<channel.xml>
Add a private channel to the channel list.  Note that all
public channels should be synced using "update-channels".
Parameter may be either a local file or remote URL to a
channel.xml.
'
            ),
        'channel-update' => array(
            'summary' => 'Update an Existing Channel',
            'function' => 'doUpdate',
            'shortcut' => 'cu',
            'options' => array(
                'force' => array(
                    'shortopt' => 'f',
                    'doc' => 'will force download of new channel.xml if an existing channel name is used',
                    ),
                'channel' => array(
                    'shortopt' => 'c',
                    'arg' => 'CHANNEL',
                    'doc' => 'will force download of new channel.xml if an existing channel name is used',
                    ),
),
            'doc' => '[<channel.xml>|<channel name>]
Update a channel in the channel list directly.  Note that all
public channels can be synced using "update-channels".
Parameter may be a local or remote channel.xml, or the name of
an existing channel.
'
            ),
        'channel-info' => array(
            'summary' => 'Retrieve Information on a Channel',
            'function' => 'doInfo',
            'shortcut' => 'ci',
            'options' => array(),
            'doc' => '<package>
List the files in an installed package.
'
            ),
        'channel-alias' => array(
            'summary' => 'Specify an alias to a channel name',
            'function' => 'doAlias',
            'shortcut' => 'cha',
            'options' => array(),
            'doc' => '<channel> <alias>
Specify a specific alias to use for a channel name.
The alias may not be an existing channel name or
alias.
'
            ),
        'channel-discover' => array(
            'summary' => 'Initialize a Channel from its server',
            'function' => 'doDiscover',
            'shortcut' => 'di',
            'options' => array(),
            'doc' => '[<channel.xml>|<channel name>]
Initialize a channel from its server and create a local channel.xml.
If <channel name> is in the format "<username>:<password>@<channel>" then
<username> and <password> will be set as the login username/password for
<channel>. Use caution when passing the username/password in this way, as
it may allow other users on your computer to briefly view your username/
password via the system\'s process list.
'
            ),
        );

    // }}}
    // {{{ constructor

    /**
     * PEAR_Command_Registry constructor.
     *
     * @access public
     */
    function PEAR_Command_Channels(&$ui, &$config)
    {
        parent::PEAR_Command_Common($ui, $config);
    }

    // }}}

    // {{{ doList()
    
    function _sortChannels($a, $b)
    {
        return strnatcasecmp($a->getName(), $b->getName());
    }

    function doList($command, $options, $params)
    {
        $reg = &$this->config->getRegistry();
        $registered = $reg->getChannels();
        usort($registered, array(&$this, '_sortchannels'));
        $i = $j = 0;
        $data = array(
            'caption' => 'Registered Channels:',
            'border' => true,
            'headline' => array('Channel', 'Summary')
            );
        foreach ($registered as $channel) {
            $data['data'][] = array($channel->getName(),
                                      $channel->getSummary());
        }
        if (count($registered)==0) {
            $data = '(no registered channels)';
        }
        $this->ui->outputData($data, $command);
        return true;
    }
    
    function doUpdateAll($command, $options, $params)
    {
        $reg = &$this->config->getRegistry();
        $channels = $reg->getChannels();

        $success = true;
        foreach ($channels as $channel) {
            if ($channel->getName() != '__uri') {
                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
                $err = $this->doUpdate('channel-update',
                                          $options,
                                          array($channel->getName()));
                if (PEAR::isError($err)) {
                    $this->ui->outputData($err->getMessage(), $command);
                    $success = false;
                } else {
                    $success &= $err;
                }
            }
        }
        return $success;
    }
    
    function doInfo($command, $options, $params)
    {
        if (sizeof($params) != 1) {
            return $this->raiseError("No channel specified");
        }
        $reg = &$this->config->getRegistry();
        $channel = strtolower($params[0]);
        if ($reg->channelExists($channel)) {
            $chan = $reg->getChannel($channel);
            if (PEAR::isError($chan)) {
                return $this->raiseError($chan);
            }
        } else {
            if (strpos($channel, '://')) {
                $downloader = &$this->getDownloader();
                $tmpdir = $this->config->get('temp_dir');
                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
                $loc = $downloader->downloadHttp($channel, $this->ui, $tmpdir);
                PEAR::staticPopErrorHandling();
                if (PEAR::isError($loc)) {
                    return $this->raiseError('Cannot open "' . $channel .
                        '" (' . $loc->getMessage() . ')');
                } else {
                    $contents = implode('', file($loc));
                }
            } else {
                if (file_exists($params[0])) {
                    $fp = fopen($params[0], 'r');
                    if (!$fp) {
                        return $this->raiseError('Cannot open "' . $params[0] . '"');
                    }
                } else {
                    return $this->raiseError('Unknown channel "' . $channel . '"');
                }
                $contents = '';
                while (!feof($fp)) {
                    $contents .= fread($fp, 1024);
                }
                fclose($fp);
            }
            if (!class_exists('PEAR_ChannelFile')) {
                require_once 'PEAR/ChannelFile.php';
            }
            $chan = new PEAR_ChannelFile;
            $chan->fromXmlString($contents);
            $chan->validate();
            if ($errs = $chan->getErrors(true)) {
                foreach ($errs as $err) {
                    $this->ui->outputData($err['level'] . ': ' . $err['message']);
                }
                return $this->raiseError('Channel file "' . $params[0] . '" is not valid');
            }
        }
        if ($chan) {
            $channel = $chan->getName();
            $caption = 'Channel ' . $channel . ' Information:';
            $data1 = array(
                'caption' => $caption,
                'border' => true);
            $data1['data']['server'] = array('Name and Server', $chan->getName());
            if ($chan->getAlias() != $chan->getName()) {
                $data1['data']['alias'] = array('Alias', $chan->getAlias());
            }
            $data1['data']['summary'] = array('Summary', $chan->getSummary());
            $validate = $chan->getValidationPackage();
            $data1['data']['vpackage'] = array('Validation Package Name', $validate['_content']);
            $data1['data']['vpackageversion'] =
                array('Validation Package Version', $validate['attribs']['version']);
            $d = array();
            $d['main'] = $data1;

            $data['data'] = array();
            $data['caption'] = 'Server Capabilities';
            $data['headline'] = array('Type', 'Version/REST type', 'Function Name/REST base');
            $capabilities = $chan->getFunctions('xmlrpc');
            $soaps = $chan->getFunctions('soap');
            if ($capabilities || $soaps || $chan->supportsREST()) {
                if ($capabilities) {
                    if (!isset($capabilities[0])) {
                        $capabilities = array($capabilities);
                    }
                    foreach ($capabilities as $protocol) {
                        $data['data'][] = array('xmlrpc', $protocol['attribs']['version'],
                            $protocol['_content']);
                    }
                }
                if ($soaps) {
                    if (!isset($soaps[0])) {
                        $soaps = array($soaps);
                    }
                    foreach ($soaps as $protocol) {
                        $data['data'][] = array('soap', $protocol['attribs']['version'],
                            $protocol['_content']);
                    }
                }
                if ($chan->supportsREST()) {
                    $funcs = $chan->getFunctions('rest');
                    if (!isset($funcs[0])) {
                        $funcs = array($funcs);
                    }
                    foreach ($funcs as $protocol) {
                        $data['data'][] = array('rest', $protocol['attribs']['type'],
                            $protocol['_content']); 
                    }
                }
            } else {
                $data['data'][] = array('No supported protocols');
            }
            $d['protocols'] = $data;
            $data['data'] = array();
            $mirrors = $chan->getMirrors();
            if ($mirrors) {
                $data['caption'] = 'Channel ' . $channel . ' Mirrors:';
                unset($data['headline']);
                foreach ($mirrors as $mirror) {
                    $data['data'][] = array($mirror['attribs']['host']);
                    $d['mirrors'] = $data;
                }
                foreach ($mirrors as $i => $mirror) {
                    $data['data'] = array();
                    $data['caption'] = 'Mirror ' . $mirror['attribs']['host'] . ' Capabilities';
                    $data['headline'] = array('Type', 'Version/REST type', 'Function Name/REST base');
                    $capabilities = $chan->getFunctions('xmlrpc', $mirror['attribs']['host']);
                    $soaps = $chan->getFunctions('soap', $mirror['attribs']['host']);
                    if ($capabilities || $soaps || $chan->supportsREST($mirror['attribs']['host'])) {
                        if ($capabilities) {
                            if (!isset($capabilities[0])) {
                                $capabilities = array($capabilities);
                            }
                            foreach ($capabilities as $protocol) {
                                $data['data'][] = array('xmlrpc', $protocol['attribs']['version'],
                                    $protocol['_content']);
                            }
                        }
                        if ($soaps) {
                            if (!isset($soaps[0])) {
                                $soaps = array($soaps);
                            }
                            foreach ($soaps as $protocol) {
                                $data['data'][] = array('soap', $protocol['attribs']['version'],
                                    $protocol['_content']);
                            }
                        }
                        if ($chan->supportsREST($mirror['attribs']['host'])) {
                            $funcs = $chan->getFunctions('rest', $mirror['attribs']['host']);
                            if (!isset($funcs[0])) {
                                $funcs = array($funcs);
                            }
                            foreach ($funcs as $protocol) {
                                $data['data'][] = array('rest', $protocol['attribs']['type'],
                                    $protocol['_content']); 
                            }
                        }
                    } else {
                        $data['data'][] = array('No supported protocols');
                    }
                    $d['mirrorprotocols' . $i] = $data;
                }
            }
            $this->ui->outputData($d, 'channel-info');
        } else {
            return $this->raiseError('Serious error: Channel "' . $params[0] .
                '" has a corrupted registry entry');
        }
    }

    // }}}
    
    function doDelete($command, $options, $params)
    {
        if (sizeof($params) != 1) {
            return $this->raiseError('channel-delete: no channel specified');
        }
        $reg = &$this->config->getRegistry();
        if (!$reg->channelExists($params[0])) {
            return $this->raiseError('channel-delete: channel "' . $params[0] . '" does not exist');
        }
        $channel = $reg->channelName($params[0]);
        if ($channel == 'pear.php.net') {
            return $this->raiseError('Cannot delete the pear.php.net channel');
        }
        if ($channel == 'pecl.php.net') {
            return $this->raiseError('Cannot delete the pecl.php.net channel');
        }
        if ($channel == '__uri') {
            return $this->raiseError('Cannot delete the __uri pseudo-channel');
        }
        if (PEAR::isError($err = $reg->listPackages($channel))) {
            return $err;
        }
        if (count($err)) {
            return $this->raiseError('Channel "' . $channel .
                '" has installed packages, cannot delete');
        }
        if (!$reg->deleteChannel($channel)) {
            return $this->raiseError('Channel "' . $channel . '" deletion failed');
        } else {
            $this->config->deleteChannel($channel);
            $this->ui->outputData('Channel "' . $channel . '" deleted', $command);
        }
    }

    function doAdd($command, $options, $params)
    {
        if (sizeof($params) != 1) {
            return $this->raiseError('channel-add: no channel file specified');
        }
        if (strpos($params[0], '://')) {
            $downloader = &$this->getDownloader();
            $tmpdir = $this->config->get('temp_dir');
            if (!file_exists($tmpdir)) {
                require_once 'System.php';
                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
                $err = System::mkdir(array('-p', $tmpdir));
                PEAR::staticPopErrorHandling();
                if (PEAR::isError($err)) {
                    return $this->raiseError('channel-add: temp_dir does not exist: "' .
                        $tmpdir . 
                        '" - You can change this location with "pear config-set temp_dir"');
                }
            }
            if (!is_writable($tmpdir)) {
                return $this->raiseError('channel-add: temp_dir is not writable: "' .
                    $tmpdir . 
                    '" - You can change this location with "pear config-set temp_dir"');
            }
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            $loc = $downloader->downloadHttp($params[0], $this->ui, $tmpdir, null, false);
            PEAR::staticPopErrorHandling();
            if (PEAR::isError($loc)) {
                return $this->raiseError('channel-add: Cannot open "' . $params[0] .
                    '" (' . $loc->getMessage() . ')');
            } else {
                list($loc, $lastmodified) = $loc;
                $contents = implode('', file($loc));
            }
        } else {
            $lastmodified = $fp = false;
            if (file_exists($params[0])) {
                $fp = fopen($params[0], 'r');
            }
            if (!$fp) {
                return $this->raiseError('channel-add: cannot open "' . $params[0] . '"');
            }
            $contents = '';
            while (!feof($fp)) {
                $contents .= fread($fp, 1024);
            }
            fclose($fp);
        }
        if (!class_exists('PEAR_ChannelFile')) {
            require_once 'PEAR/ChannelFile.php';
        }
        $channel = new PEAR_ChannelFile;
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        $result = $channel->fromXmlString($contents);
        PEAR::staticPopErrorHandling();
        if (!$result) {
            $exit = false;
            if (count($errors = $channel->getErrors(true))) {
                foreach ($errors as $error) {
                    $this->ui->outputData(ucfirst($error['level'] . ': ' . $error['message']));
                    if (!$exit) {
                        $exit = $error['level'] == 'error' ? true : false;
                    }
                }
                if ($exit) {
                    return $this->raiseError('channel-add: invalid channel.xml file');
                }
            }
        }
        $reg = &$this->config->getRegistry();
        if ($reg->channelExists($channel->getName())) {
            return $this->raiseError('channel-add: Channel "' . $channel->getName() .
                '" exists, use channel-update to update entry');
        }
        $ret = $reg->addChannel($channel, $lastmodified);
        if (PEAR::isError($ret)) {
            return $ret;
        }
        if (!$ret) {
            return $this->raiseError('channel-add: adding Channel "' . $channel->getName() .
                '" to registry failed');
        }
        $this->config->setChannels($reg->listChannels());
        $this->config->writeConfigFile();
        $this->ui->outputData('Adding Channel "' . $channel->getName() . '" succeeded', $command);
    }

    function doUpdate($command, $options, $params)
    {
        $tmpdir = $this->config->get('temp_dir');
        if (!file_exists($tmpdir)) {
            require_once 'System.php';
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            $err = System::mkdir(array('-p', $tmpdir));
            PEAR::staticPopErrorHandling();
            if (PEAR::isError($err)) {
                return $this->raiseError('channel-add: temp_dir does not exist: "' .
                    $tmpdir . 
                    '" - You can change this location with "pear config-set temp_dir"');
            }
        }
        if (!is_writable($tmpdir)) {
            return $this->raiseError('channel-add: temp_dir is not writable: "' .
                $tmpdir . 
                '" - You can change this location with "pear config-set temp_dir"');
        }
        $reg = &$this->config->getRegistry();
        if (sizeof($params) != 1) {
            return $this->raiseError("No channel file specified");
        }
        $lastmodified = false;
        if ((!file_exists($params[0]) || is_dir($params[0]))
              && $reg->channelExists(strtolower($params[0]))) {
            $c = $reg->getChannel(strtolower($params[0]));
            if (PEAR::isError($c)) {
                return $this->raiseError($c);
            }
            $this->ui->outputData("Updating channel \"$params[0]\"", $command);
            $dl = &$this->getDownloader(array());
            // if force is specified, use a timestamp of "1" to force retrieval
            $lastmodified = isset($options['force']) ? false : $c->lastModified();
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            $contents = $dl->downloadHttp('http://' . $c->getName() . '/channel.xml',
                $this->ui, $tmpdir, null, $lastmodified);
            PEAR::staticPopErrorHandling();
            if (PEAR::isError($contents)) {
                return $this->raiseError('Cannot retrieve channel.xml for channel "' .
                    $c->getName() . '" (' . $contents->getMessage() . ')');
            }
            list($contents, $lastmodified) = $contents;
            if (!$contents) {
                $this->ui->outputData("Channel \"$params[0]\" is up to date");
                return;
            }
            $contents = implode('', file($contents));
            if (!class_exists('PEAR_ChannelFile')) {
                require_once 'PEAR/ChannelFile.php';
            }
            $channel = new PEAR_ChannelFile;
            $channel->fromXmlString($contents);
            if (!$channel->getErrors()) {
                // security check: is the downloaded file for the channel we got it from?
                if (strtolower($channel->getName()) != strtolower($c->getName())) {
                    if (isset($options['force'])) {
                        $this->ui->log(0, 'WARNING: downloaded channel definition file' .
                            ' for channel "' . $channel->getName() . '" from channel "' .
                            strtolower($c->getName()) . '"');
                    } else {
                        return $this->raiseError('ERROR: downloaded channel definition file' .
                            ' for channel "' . $channel->getName() . '" from channel "' .
                            strtolower($c->getName()) . '"');
                    }
                }
            }
        } else {
            if (strpos($params[0], '://')) {
                $dl = &$this->getDownloader();
                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
                $loc = $dl->downloadHttp($params[0],
                    $this->ui, $tmpdir, null, $lastmodified);
                PEAR::staticPopErrorHandling();
                if (PEAR::isError($loc)) {
                    return $this->raiseError("Cannot open " . $params[0] .
                         ' (' . $loc->getMessage() . ')');
                } else {
                    list($loc, $lastmodified) = $loc;
                    $contents = implode('', file($loc));
                }
            } else {
                $fp = false;
                if (file_exists($params[0])) {
                    $fp = fopen($params[0], 'r');
                }
                if (!$fp) {
                    return $this->raiseError("Cannot open " . $params[0]);
                }
                $contents = '';
                while (!feof($fp)) {
                    $contents .= fread($fp, 1024);
                }
                fclose($fp);
            }
            if (!class_exists('PEAR_ChannelFile')) {
                require_once 'PEAR/ChannelFile.php';
            }
            $channel = new PEAR_ChannelFile;
            $channel->fromXmlString($contents);
        }
        $exit = false;
        if (count($errors = $channel->getErrors(true))) {
            foreach ($errors as $error) {
                $this->ui->outputData(ucfirst($error['level'] . ': ' . $error['message']));
                if (!$exit) {
                    $exit = $error['level'] == 'error' ? true : false;
                }
            }
            if ($exit) {
                return $this->raiseError('Invalid channel.xml file');
            }
        }
        if (!$reg->channelExists($channel->getName())) {
            return $this->raiseError('Error: Channel "' . $channel->getName() .
                '" does not exist, use channel-add to add an entry');
        }
        $ret = $reg->updateChannel($channel, $lastmodified);
        if (PEAR::isError($ret)) {
            return $ret;
        }
        if (!$ret) {
            return $this->raiseError('Updating Channel "' . $channel->getName() .
                '" in registry failed');
        }
        $this->config->setChannels($reg->listChannels());
        $this->config->writeConfigFile();
        $this->ui->outputData('Update of Channel "' . $channel->getName() . '" succeeded');
    }

    function &getDownloader()
    {
        if (!class_exists('PEAR_Downloader')) {
            require_once 'PEAR/Downloader.php';
        }
        $a = new PEAR_Downloader($this->ui, array(), $this->config);
        return $a;
    }

    function doAlias($command, $options, $params)
    {
        $reg = &$this->config->getRegistry();
        if (sizeof($params) == 1) {
            return $this->raiseError('No channel alias specified');
        }
        if (sizeof($params) != 2) {
            return $this->raiseError(
                'Invalid format, correct is: channel-alias channel alias');
        }
        if (!$reg->channelExists($params[0], true)) {
            if ($reg->isAlias($params[0])) {
                $extra = ' (use "channel-alias ' . $reg->channelName($params[0]) . ' ' .
                    strtolower($params[1]) . '")';
            } else {
                $extra = '';
            }
            return $this->raiseError('"' . $params[0] . '" is not a valid channel' . $extra);
        }
        if ($reg->isAlias($params[1])) {
            return $this->raiseError('Channel "' . $reg->channelName($params[1]) . '" is ' .
                'already aliased to "' . strtolower($params[1]) . '", cannot re-alias');
        }
        $chan = &$reg->getChannel($params[0]);
        if (PEAR::isError($chan)) {
            return $this->raiseError('Corrupt registry?  Error retrieving channel "' . $params[0] .
                '" information (' . $chan->getMessage() . ')');
        }
        // make it a local alias
        if (!$chan->setAlias(strtolower($params[1]), true)) {
            return $this->raiseError('Alias "' . strtolower($params[1]) .
                '" is not a valid channel alias');
        }
        $reg->updateChannel($chan);
        $this->ui->outputData('Channel "' . $chan->getName() . '" aliased successfully to "' .
            strtolower($params[1]) . '"');
    }

    /**
     * The channel-discover command
     *
     * @param string $command command name
     * @param array  $options option_name => value
     * @param array  $params  list of additional parameters.
     *               $params[0] should contain a string with either:
     *               - <channel name> or
     *               - <username>:<password>@<channel name>
     * @return null|PEAR_Error
     */
    function doDiscover($command, $options, $params)
    {
        $reg = &$this->config->getRegistry();
        if (sizeof($params) != 1) {
            return $this->raiseError("No channel server specified");
        }
        
        // Look for the possible input format "<username>:<password>@<channel>"
        if (preg_match('/^(.+):(.+)@(.+)\\z/', $params[0], $matches)) {
            $username = $matches[1];
            $password = $matches[2];
            $channel = $matches[3];
        } else {
            $channel = $params[0];
        }
        
        if ($reg->channelExists($channel)) {
            if ($reg->isAlias($channel)) {
                return $this->raiseError("A channel alias named \"$channel\" " .
                    'already exists, aliasing channel "' . $reg->channelName($channel)
                    . '"');
            } else {
                return $this->raiseError("Channel \"$channel\" is already initialized");
            }
        }
        $this->pushErrorHandling(PEAR_ERROR_RETURN);
        $err = $this->doAdd($command, $options, array('http://' . $channel . '/channel.xml'));
        $this->popErrorHandling();
        if (PEAR::isError($err)) {
            return $this->raiseError("Discovery of channel \"$channel\" failed (" .
                $err->getMessage() . ')');
        }
        
        // Store username/password if they were given
        // Arguably we should do a logintest on the channel here, but since
        // that's awkward on a REST-based channel (even "pear login" doesn't
        // do it for those), and XML-RPC is deprecated, it's fairly pointless.
        if (isset($username)) {
            $this->config->set('username', $username, 'user', $channel);
            $this->config->set('password', $password, 'user', $channel);
            $this->config->store();
            $this->ui->outputData("Stored login for channel \"$channel\" using username \"$username\"", $command);
        }
        
        $this->ui->outputData("Discovery of channel \"$channel\" succeeded", $command);
    }
}
?>
PEAR-1.7.2/PEAR/Command/Common.php100644   1750   1750       21300 11014312742  11547 <?php
/**
 * PEAR_Command_Common base class
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Common.php,v 1.36 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * base class
 */
require_once 'PEAR.php';

/**
 * PEAR commands base class
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */
class PEAR_Command_Common extends PEAR
{
    // {{{ properties

    /**
     * PEAR_Config object used to pass user system and configuration
     * on when executing commands
     *
     * @var PEAR_Config
     */
    var $config;
    /**
     * @var PEAR_Registry
     * @access protected
     */
    var $_registry;

    /**
     * User Interface object, for all interaction with the user.
     * @var object
     */
    var $ui;

    var $_deps_rel_trans = array(
                                 'lt' => '<',
                                 'le' => '<=',
                                 'eq' => '=',
                                 'ne' => '!=',
                                 'gt' => '>',
                                 'ge' => '>=',
                                 'has' => '=='
                                 );

    var $_deps_type_trans = array(
                                  'pkg' => 'package',
                                  'ext' => 'extension',
                                  'php' => 'PHP',
                                  'prog' => 'external program',
                                  'ldlib' => 'external library for linking',
                                  'rtlib' => 'external runtime library',
                                  'os' => 'operating system',
                                  'websrv' => 'web server',
                                  'sapi' => 'SAPI backend'
                                  );

    // }}}
    // {{{ constructor

    /**
     * PEAR_Command_Common constructor.
     *
     * @access public
     */
    function PEAR_Command_Common(&$ui, &$config)
    {
        parent::PEAR();
        $this->config = &$config;
        $this->ui = &$ui;
    }

    // }}}

    // {{{ getCommands()

    /**
     * Return a list of all the commands defined by this class.
     * @return array list of commands
     * @access public
     */
    function getCommands()
    {
        $ret = array();
        foreach (array_keys($this->commands) as $command) {
            $ret[$command] = $this->commands[$command]['summary'];
        }
        return $ret;
    }

    // }}}
    // {{{ getShortcuts()

    /**
     * Return a list of all the command shortcuts defined by this class.
     * @return array shortcut => command
     * @access public
     */
    function getShortcuts()
    {
        $ret = array();
        foreach (array_keys($this->commands) as $command) {
            if (isset($this->commands[$command]['shortcut'])) {
                $ret[$this->commands[$command]['shortcut']] = $command;
            }
        }
        return $ret;
    }

    // }}}
    // {{{ getOptions()

    function getOptions($command)
    {
        $shortcuts = $this->getShortcuts();
        if (isset($shortcuts[$command])) {
            $command = $shortcuts[$command];
        }
        if (isset($this->commands[$command]) &&
              isset($this->commands[$command]['options'])) {
            return $this->commands[$command]['options'];
        } else {
            return null;
        }
    }

    // }}}
    // {{{ getGetoptArgs()

    function getGetoptArgs($command, &$short_args, &$long_args)
    {
        $short_args = "";
        $long_args = array();
        if (empty($this->commands[$command]) || empty($this->commands[$command]['options'])) {
            return;
        }
        reset($this->commands[$command]['options']);
        while (list($option, $info) = each($this->commands[$command]['options'])) {
            $larg = $sarg = '';
            if (isset($info['arg'])) {
                if ($info['arg']{0} == '(') {
                    $larg = '==';
                    $sarg = '::';
                    $arg = substr($info['arg'], 1, -1);
                } else {
                    $larg = '=';
                    $sarg = ':';
                    $arg = $info['arg'];
                }
            }
            if (isset($info['shortopt'])) {
                $short_args .= $info['shortopt'] . $sarg;
            }
            $long_args[] = $option . $larg;
        }
    }

    // }}}
    // {{{ getHelp()
    /**
    * Returns the help message for the given command
    *
    * @param string $command The command
    * @return mixed A fail string if the command does not have help or
    *               a two elements array containing [0]=>help string,
    *               [1]=> help string for the accepted cmd args
    */
    function getHelp($command)
    {
        $config = &PEAR_Config::singleton();
        if (!isset($this->commands[$command])) {
            return "No such command \"$command\"";
        }
        $help = null;
        if (isset($this->commands[$command]['doc'])) {
            $help = $this->commands[$command]['doc'];
        }
        if (empty($help)) {
            // XXX (cox) Fallback to summary if there is no doc (show both?)
            if (!isset($this->commands[$command]['summary'])) {
                return "No help for command \"$command\"";
            }
            $help = $this->commands[$command]['summary'];
        }
        if (preg_match_all('/{config\s+([^\}]+)}/e', $help, $matches)) {
            foreach($matches[0] as $k => $v) {
                $help = preg_replace("/$v/", $config->get($matches[1][$k]), $help);
            }
        }
        return array($help, $this->getHelpArgs($command));
    }

    // }}}
    // {{{ getHelpArgs()
    /**
    * Returns the help for the accepted arguments of a command
    *
    * @param  string $command
    * @return string The help string
    */
    function getHelpArgs($command)
    {
        if (isset($this->commands[$command]['options']) &&
            count($this->commands[$command]['options']))
        {
            $help = "Options:\n";
            foreach ($this->commands[$command]['options'] as $k => $v) {
                if (isset($v['arg'])) {
                    if ($v['arg'][0] == '(') {
                        $arg = substr($v['arg'], 1, -1);
                        $sapp = " [$arg]";
                        $lapp = "[=$arg]";
                    } else {
                        $sapp = " $v[arg]";
                        $lapp = "=$v[arg]";
                    }
                } else {
                    $sapp = $lapp = "";
                }
                if (isset($v['shortopt'])) {
                    $s = $v['shortopt'];
                    $help .= "  -$s$sapp, --$k$lapp\n";
                } else {
                    $help .= "  --$k$lapp\n";
                }
                $p = "        ";
                $doc = rtrim(str_replace("\n", "\n$p", $v['doc']));
                $help .= "        $doc\n";
            }
            return $help;
        }
        return null;
    }

    // }}}
    // {{{ run()

    function run($command, $options, $params)
    {
        if (empty($this->commands[$command]['function'])) {
            // look for shortcuts
            foreach (array_keys($this->commands) as $cmd) {
                if (isset($this->commands[$cmd]['shortcut']) && $this->commands[$cmd]['shortcut'] == $command) {
                    if (empty($this->commands[$cmd]['function'])) {
                        return $this->raiseError("unknown command `$command'");
                    } else {
                        $func = $this->commands[$cmd]['function'];
                    }
                    $command = $cmd;
                    break;
                }
            }
        } else {
            $func = $this->commands[$command]['function'];
        }
        return $this->$func($command, $options, $params);
    }

    // }}}
}

?>
PEAR-1.7.2/PEAR/Command/Config.xml100644   1750   1750        6466 11014312742  11535 <commands version="1.0">
 <config-show>
  <summary>Show All Settings</summary>
  <function>doConfigShow</function>
  <shortcut>csh</shortcut>
  <options>
   <channel>
    <shortopt>c</shortopt>
    <doc>show configuration variables for another channel</doc>
    <arg>CHAN</arg>
   </channel>
  </options>
  <doc>[layer]
Displays all configuration values.  An optional argument
may be used to tell which configuration layer to display.  Valid
configuration layers are &quot;user&quot;, &quot;system&quot; and &quot;default&quot;. To display
configurations for different channels, set the default_channel
configuration variable and run config-show again.
</doc>
 </config-show>
 <config-get>
  <summary>Show One Setting</summary>
  <function>doConfigGet</function>
  <shortcut>cg</shortcut>
  <options>
   <channel>
    <shortopt>c</shortopt>
    <doc>show configuration variables for another channel</doc>
    <arg>CHAN</arg>
   </channel>
  </options>
  <doc>&lt;parameter&gt; [layer]
Displays the value of one configuration parameter.  The
first argument is the name of the parameter, an optional second argument
may be used to tell which configuration layer to look in.  Valid configuration
layers are &quot;user&quot;, &quot;system&quot; and &quot;default&quot;.  If no layer is specified, a value
will be picked from the first layer that defines the parameter, in the order
just specified.  The configuration value will be retrieved for the channel
specified by the default_channel configuration variable.
</doc>
 </config-get>
 <config-set>
  <summary>Change Setting</summary>
  <function>doConfigSet</function>
  <shortcut>cs</shortcut>
  <options>
   <channel>
    <shortopt>c</shortopt>
    <doc>show configuration variables for another channel</doc>
    <arg>CHAN</arg>
   </channel>
  </options>
  <doc>&lt;parameter&gt; &lt;value&gt; [layer]
Sets the value of one configuration parameter.  The first argument is
the name of the parameter, the second argument is the new value.  Some
parameters are subject to validation, and the command will fail with
an error message if the new value does not make sense.  An optional
third argument may be used to specify in which layer to set the
configuration parameter.  The default layer is &quot;user&quot;.  The
configuration value will be set for the current channel, which
is controlled by the default_channel configuration variable.
</doc>
 </config-set>
 <config-help>
  <summary>Show Information About Setting</summary>
  <function>doConfigHelp</function>
  <shortcut>ch</shortcut>
  <options />
  <doc>[parameter]
Displays help for a configuration parameter.  Without arguments it
displays help for all configuration parameters.
</doc>
 </config-help>
 <config-create>
  <summary>Create a Default configuration file</summary>
  <function>doConfigCreate</function>
  <shortcut>coc</shortcut>
  <options>
   <windows>
    <shortopt>w</shortopt>
    <doc>create a config file for a windows install</doc>
   </windows>
  </options>
  <doc>&lt;root path&gt; &lt;filename&gt;
Create a default configuration file with all directory configuration
variables set to subdirectories of &lt;root path&gt;, and save it as &lt;filename&gt;.
This is useful especially for creating a configuration file for a remote
PEAR installation (using the --remoteconfig option of install, upgrade,
and uninstall).
</doc>
 </config-create>
</commands>PEAR-1.7.2/PEAR/Command/Config.php100644   1750   1750       36251 11014312742  11537 <?php
/**
 * PEAR_Command_Config (config-show, config-get, config-set, config-help, config-create commands)
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Config.php,v 1.56 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * base class
 */
require_once 'PEAR/Command/Common.php';

/**
 * PEAR commands for managing configuration data.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */
class PEAR_Command_Config extends PEAR_Command_Common
{
    // {{{ properties

    var $commands = array(
        'config-show' => array(
            'summary' => 'Show All Settings',
            'function' => 'doConfigShow',
            'shortcut' => 'csh',
            'options' => array(
                'channel' => array(
                    'shortopt' => 'c',
                    'doc' => 'show configuration variables for another channel',
                    'arg' => 'CHAN',
                    ),
),
            'doc' => '[layer]
Displays all configuration values.  An optional argument
may be used to tell which configuration layer to display.  Valid
configuration layers are "user", "system" and "default". To display
configurations for different channels, set the default_channel
configuration variable and run config-show again.
',
            ),
        'config-get' => array(
            'summary' => 'Show One Setting',
            'function' => 'doConfigGet',
            'shortcut' => 'cg',
            'options' => array(
                'channel' => array(
                    'shortopt' => 'c',
                    'doc' => 'show configuration variables for another channel',
                    'arg' => 'CHAN',
                    ),
),
            'doc' => '<parameter> [layer]
Displays the value of one configuration parameter.  The
first argument is the name of the parameter, an optional second argument
may be used to tell which configuration layer to look in.  Valid configuration
layers are "user", "system" and "default".  If no layer is specified, a value
will be picked from the first layer that defines the parameter, in the order
just specified.  The configuration value will be retrieved for the channel
specified by the default_channel configuration variable.
',
            ),
        'config-set' => array(
            'summary' => 'Change Setting',
            'function' => 'doConfigSet',
            'shortcut' => 'cs',
            'options' => array(
                'channel' => array(
                    'shortopt' => 'c',
                    'doc' => 'show configuration variables for another channel',
                    'arg' => 'CHAN',
                    ),
),
            'doc' => '<parameter> <value> [layer]
Sets the value of one configuration parameter.  The first argument is
the name of the parameter, the second argument is the new value.  Some
parameters are subject to validation, and the command will fail with
an error message if the new value does not make sense.  An optional
third argument may be used to specify in which layer to set the
configuration parameter.  The default layer is "user".  The
configuration value will be set for the current channel, which
is controlled by the default_channel configuration variable.
',
            ),
        'config-help' => array(
            'summary' => 'Show Information About Setting',
            'function' => 'doConfigHelp',
            'shortcut' => 'ch',
            'options' => array(),
            'doc' => '[parameter]
Displays help for a configuration parameter.  Without arguments it
displays help for all configuration parameters.
',
           ),
        'config-create' => array(
            'summary' => 'Create a Default configuration file',
            'function' => 'doConfigCreate',
            'shortcut' => 'coc',
            'options' => array(
                'windows' => array(
                    'shortopt' => 'w',
                    'doc' => 'create a config file for a windows install',
                    ),
            ),
            'doc' => '<root path> <filename>
Create a default configuration file with all directory configuration
variables set to subdirectories of <root path>, and save it as <filename>.
This is useful especially for creating a configuration file for a remote
PEAR installation (using the --remoteconfig option of install, upgrade,
and uninstall).
',
            ),
        );

    // }}}
    // {{{ constructor

    /**
     * PEAR_Command_Config constructor.
     *
     * @access public
     */
    function PEAR_Command_Config(&$ui, &$config)
    {
        parent::PEAR_Command_Common($ui, $config);
    }

    // }}}

    // {{{ doConfigShow()

    function doConfigShow($command, $options, $params)
    {
        if (is_array($params)) {
            $layer = isset($params[0]) ? $params[0] : NULL;
        } else {
            $layer = NULL;
        }

        // $params[0] -> the layer
        if ($error = $this->_checkLayer($layer)) {
            return $this->raiseError("config-show:$error");
        }
        $keys = $this->config->getKeys();
        sort($keys);
        $channel = isset($options['channel']) ? $options['channel'] :
            $this->config->get('default_channel');
        $reg = &$this->config->getRegistry();
        if (!$reg->channelExists($channel)) {
            return $this->raiseError('Channel "' . $channel . '" does not exist');
        }
        $data = array('caption' => 'Configuration (channel ' . $channel . '):');
        foreach ($keys as $key) {
            $type = $this->config->getType($key);
            $value = $this->config->get($key, $layer, $channel);
            if ($type == 'password' && $value) {
                $value = '********';
            }
            if ($value === false) {
                $value = 'false';
            } elseif ($value === true) {
                $value = 'true';
            }
            $data['data'][$this->config->getGroup($key)][] = array($this->config->getPrompt($key) , $key, $value);
        }
        foreach ($this->config->getLayers() as $layer) {
            $data['data']['Config Files'][] = array(ucfirst($layer) . ' Configuration File', 'Filename' , $this->config->getConfFile($layer));
        }

        $this->ui->outputData($data, $command);
        return true;
    }

    // }}}
    // {{{ doConfigGet()

    function doConfigGet($command, $options, $params)
    {
        if (!is_array($params)) {
            $args_cnt = 0;
        } else {
            $args_cnt  = count($params);
        }

        switch ($args_cnt) {
            case 1:
                $config_key = $params[0];
                $layer = NULL;
                break;
            case 2:
                $config_key = $params[0];
                $layer = $params[1];
                if ($error = $this->_checkLayer($layer)) {
                    return $this->raiseError("config-get:$error");
                }
                break;
            case 0:
            default:
                return $this->raiseError("config-get expects 1 or 2 parameters");
        }

        $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel');
        $reg = &$this->config->getRegistry();

        if (!$reg->channelExists($channel)) {
            return $this->raiseError('Channel "' . $channel . '" does not exist');
        }

        $this->ui->outputData($this->config->get($config_key, $layer, $channel), $command);

        return true;
    }

    // }}}
    // {{{ doConfigSet()

    function doConfigSet($command, $options, $params)
    {
        // $param[0] -> a parameter to set
        // $param[1] -> the value for the parameter
        // $param[2] -> the layer
        $failmsg = '';
        if (sizeof($params) < 2 || sizeof($params) > 3) {
            $failmsg .= "config-set expects 2 or 3 parameters";
            return PEAR::raiseError($failmsg);
        }
        if (isset($params[2]) && ($error = $this->_checkLayer($params[2]))) {
            $failmsg .= $error;
            return PEAR::raiseError("config-set:$failmsg");
        }
        $channel = isset($options['channel']) ? $options['channel'] :
            $this->config->get('default_channel');
        $reg = &$this->config->getRegistry();
        if (!$reg->channelExists($channel)) {
            return $this->raiseError('Channel "' . $channel . '" does not exist');
        }
        if ($params[0] == 'default_channel') {
            if (!$reg->channelExists($params[1])) {
                return $this->raiseError('Channel "' . $params[1] . '" does not exist');
            }
        }
        if (count($params) == 2) {
            array_push($params, 'user');
            $layer = 'user';
        } else {
            $layer = $params[2];
        }
        array_push($params, $channel);
        if (!call_user_func_array(array(&$this->config, 'set'), $params))
        {
            array_pop($params);
            $failmsg = "config-set (" . implode(", ", $params) . ") failed, channel $channel";
        } else {
            $this->config->store($layer);
        }
        if ($failmsg) {
            return $this->raiseError($failmsg);
        }
        $this->ui->outputData('config-set succeeded', $command);
        return true;
    }

    // }}}
    // {{{ doConfigHelp()

    function doConfigHelp($command, $options, $params)
    {
        if (empty($params)) {
            $params = $this->config->getKeys();
        }
        $data['caption']  = "Config help" . ((count($params) == 1) ? " for $params[0]" : '');
        $data['headline'] = array('Name', 'Type', 'Description');
        $data['border']   = true;
        foreach ($params as $name) {
            $type = $this->config->getType($name);
            $docs = $this->config->getDocs($name);
            if ($type == 'set') {
                $docs = rtrim($docs) . "\nValid set: " .
                    implode(' ', $this->config->getSetValues($name));
            }
            $data['data'][] = array($name, $type, $docs);
        }
        $this->ui->outputData($data, $command);
    }

    // }}}
    // {{{ doConfigCreate()

    function doConfigCreate($command, $options, $params)
    {
        if (count($params) != 2) {
            return PEAR::raiseError('config-create: must have 2 parameters, root path and ' .
                'filename to save as');
        }
        $root = $params[0];
        // Clean up the DIRECTORY_SEPARATOR mess
        $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
        $root = preg_replace(array('!\\\\+!', '!/+!', "!$ds2+!"),
                             array('/', '/', '/'),
                            $root);
        if ($root{0} != '/') {
            if (isset($options['windows'])) {
                if (!preg_match('/^[A-Za-z]:/', $root)) {
                    return PEAR::raiseError('Root directory must be an absolute path beginning ' .
                        'with "\\" or "C:\\", was: "' . $root . '"');
                }
            } else {
                return PEAR::raiseError('Root directory must be an absolute path beginning ' .
                    'with "/", was: "' . $root . '"');
            }
        }
        $windows = isset($options['windows']);
        if ($windows) {
            $root = str_replace('/', '\\', $root);
        }
        if (!file_exists($params[1])) {
            if (!@touch($params[1])) {
                return PEAR::raiseError('Could not create "' . $params[1] . '"');
            }
        }
        $params[1] = realpath($params[1]);
        $config = &new PEAR_Config($params[1], '#no#system#config#', false, false);
        if ($root{strlen($root) - 1} == '/') {
            $root = substr($root, 0, strlen($root) - 1);
        }
        $config->noRegistry();
        $config->set('php_dir', $windows ? "$root\\pear\\php" : "$root/pear/php", 'user');
        $config->set('data_dir', $windows ? "$root\\pear\\data" : "$root/pear/data");
        $config->set('www_dir', $windows ? "$root\\pear\\www" : "$root/pear/www");
        $config->set('cfg_dir', $windows ? "$root\\pear\\cfg" : "$root/pear/cfg");
        $config->set('ext_dir', $windows ? "$root\\pear\\ext" : "$root/pear/ext");
        $config->set('doc_dir', $windows ? "$root\\pear\\docs" : "$root/pear/docs");
        $config->set('test_dir', $windows ? "$root\\pear\\tests" : "$root/pear/tests");
        $config->set('cache_dir', $windows ? "$root\\pear\\cache" : "$root/pear/cache");
        $config->set('download_dir', $windows ? "$root\\pear\\download" : "$root/pear/download");
        $config->set('temp_dir', $windows ? "$root\\pear\\temp" : "$root/pear/temp");
        $config->set('bin_dir', $windows ? "$root\\pear" : "$root/pear");
        $config->writeConfigFile();
        $this->_showConfig($config);
        $this->ui->outputData('Successfully created default configuration file "' . $params[1] . '"',
            $command);
    }

    // }}}

    function _showConfig(&$config)
    {
        $params = array('user');
        $keys = $config->getKeys();
        sort($keys);
        $channel = 'pear.php.net';
        $data = array('caption' => 'Configuration (channel ' . $channel . '):');
        foreach ($keys as $key) {
            $type = $config->getType($key);
            $value = $config->get($key, 'user', $channel);
            if ($type == 'password' && $value) {
                $value = '********';
            }
            if ($value === false) {
                $value = 'false';
            } elseif ($value === true) {
                $value = 'true';
            }
            $data['data'][$config->getGroup($key)][] =
                array($config->getPrompt($key) , $key, $value);
        }
        foreach ($config->getLayers() as $layer) {
            $data['data']['Config Files'][] =
                array(ucfirst($layer) . ' Configuration File', 'Filename' ,
                    $config->getConfFile($layer));
        }

        $this->ui->outputData($data, 'config-show');
        return true;
    }
    // {{{ _checkLayer()

    /**
     * Checks if a layer is defined or not
     *
     * @param string $layer The layer to search for
     * @return mixed False on no error or the error message
     */
    function _checkLayer($layer = null)
    {
        if (!empty($layer) && $layer != 'default') {
            $layers = $this->config->getLayers();
            if (!in_array($layer, $layers)) {
                return " only the layers: \"" . implode('" or "', $layers) . "\" are supported";
            }
        }
        return false;
    }

    // }}}
}

?>
PEAR-1.7.2/PEAR/Command/Install.xml100644   1750   1750       17503 11014312742  11750 <commands version="1.0">
 <install>
  <summary>Install Package</summary>
  <function>doInstall</function>
  <shortcut>i</shortcut>
  <options>
   <force>
    <shortopt>f</shortopt>
    <doc>will overwrite newer installed packages</doc>
   </force>
   <loose>
    <shortopt>l</shortopt>
    <doc>do not check for recommended dependency version</doc>
   </loose>
   <nodeps>
    <shortopt>n</shortopt>
    <doc>ignore dependencies, install anyway</doc>
   </nodeps>
   <register-only>
    <shortopt>r</shortopt>
    <doc>do not install files, only register the package as installed</doc>
   </register-only>
   <soft>
    <shortopt>s</shortopt>
    <doc>soft install, fail silently, or upgrade if already installed</doc>
   </soft>
   <nobuild>
    <shortopt>B</shortopt>
    <doc>don&apos;t build C extensions</doc>
   </nobuild>
   <nocompress>
    <shortopt>Z</shortopt>
    <doc>request uncompressed files when downloading</doc>
   </nocompress>
   <installroot>
    <shortopt>R</shortopt>
    <arg>DIR</arg>
    <doc>root directory used when installing files (ala PHP&apos;s INSTALL_ROOT), use packagingroot for RPM</doc>
   </installroot>
   <packagingroot>
    <shortopt>P</shortopt>
    <arg>DIR</arg>
    <doc>root directory used when packaging files, like RPM packaging</doc>
   </packagingroot>
   <ignore-errors>
    <doc>force install even if there were errors</doc>
   </ignore-errors>
   <alldeps>
    <shortopt>a</shortopt>
    <doc>install all required and optional dependencies</doc>
   </alldeps>
   <onlyreqdeps>
    <shortopt>o</shortopt>
    <doc>install all required dependencies</doc>
   </onlyreqdeps>
   <offline>
    <shortopt>O</shortopt>
    <doc>do not attempt to download any urls or contact channels</doc>
   </offline>
   <pretend>
    <shortopt>p</shortopt>
    <doc>Only list the packages that would be downloaded</doc>
   </pretend>
  </options>
  <doc>[channel/]&lt;package&gt; ...
Installs one or more PEAR packages.  You can specify a package to
install in four ways:

&quot;Package-1.0.tgz&quot; : installs from a local file

&quot;http://example.com/Package-1.0.tgz&quot; : installs from
anywhere on the net.

&quot;package.xml&quot; : installs the package described in
package.xml.  Useful for testing, or for wrapping a PEAR package in
another package manager such as RPM.

&quot;Package[-version/state][.tar]&quot; : queries your default channel&apos;s server
({config master_server}) and downloads the newest package with
the preferred quality/state ({config preferred_state}).

To retrieve Package version 1.1, use &quot;Package-1.1,&quot; to retrieve
Package state beta, use &quot;Package-beta.&quot;  To retrieve an uncompressed
file, append .tar (make sure there is no file by the same name first)

To download a package from another channel, prefix with the channel name like
&quot;channel/Package&quot;

More than one package may be specified at once.  It is ok to mix these
four ways of specifying packages.
</doc>
 </install>
 <upgrade>
  <summary>Upgrade Package</summary>
  <function>doInstall</function>
  <shortcut>up</shortcut>
  <options>
   <force>
    <shortopt>f</shortopt>
    <doc>overwrite newer installed packages</doc>
   </force>
   <loose>
    <shortopt>l</shortopt>
    <doc>do not check for recommended dependency version</doc>
   </loose>
   <nodeps>
    <shortopt>n</shortopt>
    <doc>ignore dependencies, upgrade anyway</doc>
   </nodeps>
   <register-only>
    <shortopt>r</shortopt>
    <doc>do not install files, only register the package as upgraded</doc>
   </register-only>
   <nobuild>
    <shortopt>B</shortopt>
    <doc>don&apos;t build C extensions</doc>
   </nobuild>
   <nocompress>
    <shortopt>Z</shortopt>
    <doc>request uncompressed files when downloading</doc>
   </nocompress>
   <installroot>
    <shortopt>R</shortopt>
    <arg>DIR</arg>
    <doc>root directory used when installing files (ala PHP&apos;s INSTALL_ROOT)</doc>
   </installroot>
   <ignore-errors>
    <doc>force install even if there were errors</doc>
   </ignore-errors>
   <alldeps>
    <shortopt>a</shortopt>
    <doc>install all required and optional dependencies</doc>
   </alldeps>
   <onlyreqdeps>
    <shortopt>o</shortopt>
    <doc>install all required dependencies</doc>
   </onlyreqdeps>
   <offline>
    <shortopt>O</shortopt>
    <doc>do not attempt to download any urls or contact channels</doc>
   </offline>
   <pretend>
    <shortopt>p</shortopt>
    <doc>Only list the packages that would be downloaded</doc>
   </pretend>
  </options>
  <doc>&lt;package&gt; ...
Upgrades one or more PEAR packages.  See documentation for the
&quot;install&quot; 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.
</doc>
 </upgrade>
 <upgrade-all>
  <summary>Upgrade All Packages</summary>
  <function>doInstall</function>
  <shortcut>ua</shortcut>
  <options>
   <nodeps>
    <shortopt>n</shortopt>
    <doc>ignore dependencies, upgrade anyway</doc>
   </nodeps>
   <register-only>
    <shortopt>r</shortopt>
    <doc>do not install files, only register the package as upgraded</doc>
   </register-only>
   <nobuild>
    <shortopt>B</shortopt>
    <doc>don&apos;t build C extensions</doc>
   </nobuild>
   <nocompress>
    <shortopt>Z</shortopt>
    <doc>request uncompressed files when downloading</doc>
   </nocompress>
   <installroot>
    <shortopt>R</shortopt>
    <arg>DIR</arg>
    <doc>root directory used when installing files (ala PHP&apos;s INSTALL_ROOT)</doc>
   </installroot>
   <ignore-errors>
    <doc>force install even if there were errors</doc>
   </ignore-errors>
   <loose>
    <doc>do not check for recommended dependency version</doc>
   </loose>
  </options>
  <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
&quot;preferred_state&quot; (currently {config preferred_state}), or a state considered
more stable.
</doc>
 </upgrade-all>
 <uninstall>
  <summary>Un-install Package</summary>
  <function>doUninstall</function>
  <shortcut>un</shortcut>
  <options>
   <nodeps>
    <shortopt>n</shortopt>
    <doc>ignore dependencies, uninstall anyway</doc>
   </nodeps>
   <register-only>
    <shortopt>r</shortopt>
    <doc>do not remove files, only register the packages as not installed</doc>
   </register-only>
   <installroot>
    <shortopt>R</shortopt>
    <arg>DIR</arg>
    <doc>root directory used when installing files (ala PHP&apos;s INSTALL_ROOT)</doc>
   </installroot>
   <ignore-errors>
    <doc>force install even if there were errors</doc>
   </ignore-errors>
   <offline>
    <shortopt>O</shortopt>
    <doc>do not attempt to uninstall remotely</doc>
   </offline>
  </options>
  <doc>[channel/]&lt;package&gt; ...
Uninstalls one or more PEAR packages.  More than one package may be
specified at once.  Prefix with channel name to uninstall from a
channel not in your default channel ({config default_channel})
</doc>
 </uninstall>
 <bundle>
  <summary>Unpacks a Pecl Package</summary>
  <function>doBundle</function>
  <shortcut>bun</shortcut>
  <options>
   <destination>
    <shortopt>d</shortopt>
    <arg>DIR</arg>
    <doc>Optional destination directory for unpacking (defaults to current path or &quot;ext&quot; if exists)</doc>
   </destination>
   <force>
    <shortopt>f</shortopt>
    <doc>Force the unpacking even if there were errors in the package</doc>
   </force>
  </options>
  <doc>&lt;package&gt;
Unpacks a Pecl Package into the selected location. It will download the
package if needed.
</doc>
 </bundle>
 <run-scripts>
  <summary>Run Post-Install Scripts bundled with a package</summary>
  <function>doRunScripts</function>
  <shortcut>rs</shortcut>
  <options />
  <doc>&lt;package&gt;
Run post-installation scripts in package &lt;package&gt;, if any exist.
</doc>
 </run-scripts>
</commands>PEAR-1.7.2/PEAR/Command/Install.php100644   1750   1750      141702 11014312742  11756 <?php
/**
 * PEAR_Command_Install (install, upgrade, upgrade-all, uninstall, bundle, run-scripts commands)
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Install.php,v 1.141 2008/05/13 18:32:29 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * base class
 */
require_once 'PEAR/Command/Common.php';

/**
 * PEAR commands for installation or deinstallation/upgrading of
 * packages.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */
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',
                    ),
                'loose' => array(
                    'shortopt' => 'l',
                    'doc' => 'do not check for recommended dependency version',
                    ),
                '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), use packagingroot for RPM',
                    ),
                'packagingroot' => array(
                    'shortopt' => 'P',
                    'arg' => 'DIR',
                    'doc' => 'root directory used when packaging files, like RPM packaging',
                    ),
                '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',
                    ),
                'offline' => array(
                    'shortopt' => 'O',
                    'doc' => 'do not attempt to download any urls or contact channels',
                    ),
                'pretend' => array(
                    'shortopt' => 'p',
                    'doc' => 'Only list the packages that would be downloaded',
                    ),
                ),
            'doc' => '[channel/]<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[-version/state][.tar]" : queries your default channel\'s server
({config master_server}) and downloads the newest package with
the preferred quality/state ({config preferred_state}).

To retrieve Package version 1.1, use "Package-1.1," to retrieve
Package state beta, use "Package-beta."  To retrieve an uncompressed
file, append .tar (make sure there is no file by the same name first)

To download a package from another channel, prefix with the channel name like
"channel/Package"

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',
                    ),
                'loose' => array(
                    'shortopt' => 'l',
                    'doc' => 'do not check for recommended dependency version',
                    ),
                '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',
                    ),
                'offline' => array(
                    'shortopt' => 'O',
                    'doc' => 'do not attempt to download any urls or contact channels',
                    ),
                'pretend' => array(
                    'shortopt' => 'p',
                    'doc' => 'Only list the packages that would be downloaded',
                    ),
                ),
            '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' => 'doUpgradeAll',
            '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), use packagingroot for RPM',
                    ),
                'ignore-errors' => array(
                    'doc' => 'force install even if there were errors',
                    ),
                'loose' => array(
                    'doc' => 'do not check for recommended dependency version',
                    ),
                ),
            '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',
                    ),
                'offline' => array(
                    'shortopt' => 'O',
                    'doc' => 'do not attempt to uninstall remotely',
                    ),
                ),
            'doc' => '[channel/]<package> ...
Uninstalls one or more PEAR packages.  More than one package may be
specified at once.  Prefix with channel name to uninstall from a
channel not in your default channel ({config default_channel})
'),
        '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.
'),
        'run-scripts' => array(
            'summary' => 'Run Post-Install Scripts bundled with a package',
            'function' => 'doRunScripts',
            'shortcut' => 'rs',
            'options' => array(
            ),
            'doc' => '<package>
Run post-installation scripts in package <package>, if any exist.
'),
    );

    // }}}
    // {{{ constructor

    /**
     * PEAR_Command_Install constructor.
     *
     * @access public
     */
    function PEAR_Command_Install(&$ui, &$config)
    {
        parent::PEAR_Command_Common($ui, $config);
    }

    // }}}

    /**
     * For unit testing purposes
     */
    function &getDownloader(&$ui, $options, &$config)
    {
        if (!class_exists('PEAR_Downloader')) {
            require_once 'PEAR/Downloader.php';
        }
        $a = &new PEAR_Downloader($ui, $options, $config);
        return $a;
    }

    /**
     * For unit testing purposes
     */
    function &getInstaller(&$ui)
    {
        if (!class_exists('PEAR_Installer')) {
            require_once 'PEAR/Installer.php';
        }
        $a = &new PEAR_Installer($ui);
        return $a;
    }

    function enableExtension($binaries, $type)
    {
        if (!($phpini = $this->config->get('php_ini', null, 'pear.php.net'))) {
            return PEAR::raiseError('configuration option "php_ini" is not set to php.ini location');
        }
        $ini = $this->_parseIni($phpini);
        if (PEAR::isError($ini)) {
            return $ini;
        }
        $line = 0;
        if ($type == 'extsrc' || $type == 'extbin') {
            $search = 'extensions';
            $enable = 'extension';
        } else {
            $search = 'zend_extensions';
            ob_start();
            phpinfo(INFO_GENERAL);
            $info = ob_get_contents();
            ob_end_clean();
            $debug = function_exists('leak') ? '_debug' : '';
            $ts = preg_match('Thread Safety.+enabled', $info) ? '_ts' : '';
            $enable = 'zend_extension' . $debug . $ts;
        }
        foreach ($ini[$search] as $line => $extension) {
            if (in_array($extension, $binaries, true) || in_array(
                  $ini['extension_dir'] . DIRECTORY_SEPARATOR . $extension, $binaries, true)) {
                // already enabled - assume if one is, all are
                return true;
            }
        }
        if ($line) {
            $newini = array_slice($ini['all'], 0, $line);
        } else {
            $newini = array();
        }
        foreach ($binaries as $binary) {
            if ($ini['extension_dir']) {
                $binary = basename($binary);
            }
            $newini[] = $enable . '="' . $binary . '"' . (OS_UNIX ? "\n" : "\r\n");
        }
        $newini = array_merge($newini, array_slice($ini['all'], $line));
        $fp = @fopen($phpini, 'wb');
        if (!$fp) {
            return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing');
        }
        foreach ($newini as $line) {
            fwrite($fp, $line);
        }
        fclose($fp);
        return true;
    }

    function disableExtension($binaries, $type)
    {
        if (!($phpini = $this->config->get('php_ini', null, 'pear.php.net'))) {
            return PEAR::raiseError('configuration option "php_ini" is not set to php.ini location');
        }
        $ini = $this->_parseIni($phpini);
        if (PEAR::isError($ini)) {
            return $ini;
        }
        $line = 0;
        if ($type == 'extsrc' || $type == 'extbin') {
            $search = 'extensions';
            $enable = 'extension';
        } else {
            $search = 'zend_extensions';
            ob_start();
            phpinfo(INFO_GENERAL);
            $info = ob_get_contents();
            ob_end_clean();
            $debug = function_exists('leak') ? '_debug' : '';
            $ts = preg_match('Thread Safety.+enabled', $info) ? '_ts' : '';
            $enable = 'zend_extension' . $debug . $ts;
        }
        $found = false;
        foreach ($ini[$search] as $line => $extension) {
            if (in_array($extension, $binaries, true) || in_array(
                  $ini['extension_dir'] . DIRECTORY_SEPARATOR . $extension, $binaries, true)) {
                $found = true;
                break;
            }
        }
        if (!$found) {
            // not enabled
            return true;
        }
        $fp = @fopen($phpini, 'wb');
        if (!$fp) {
            return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing');
        }
        if ($line) {
            $newini = array_slice($ini['all'], 0, $line);
            // delete the enable line
            $newini = array_merge($newini, array_slice($ini['all'], $line + 1));
        } else {
            $newini = array_slice($ini['all'], 1);
        }
        foreach ($newini as $line) {
            fwrite($fp, $line);
        }
        fclose($fp);
        return true;
    }

    function _parseIni($filename)
    {
        if (file_exists($filename)) {
            if (filesize($filename) > 300000) {
                return PEAR::raiseError('php.ini "' . $filename . '" is too large, aborting');
            }
            ob_start();
            phpinfo(INFO_GENERAL);
            $info = ob_get_contents();
            ob_end_clean();
            $debug = function_exists('leak') ? '_debug' : '';
            $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
            $zend_extension_line = 'zend_extension' . $debug . $ts;
            $all = @file($filename);
            if (!$all) {
                return PEAR::raiseError('php.ini "' . $filename .'" could not be read');
            }
            $zend_extensions = $extensions = array();
            // assume this is right, but pull from the php.ini if it is found
            $extension_dir = ini_get('extension_dir');
            foreach ($all as $linenum => $line) {
                $line = trim($line);
                if (!$line) {
                    continue;
                }
                if ($line[0] == ';') {
                    continue;
                }
                if (strtolower(substr($line, 0, 13)) == 'extension_dir') {
                    $line = trim(substr($line, 13));
                    if ($line[0] == '=') {
                        $x = trim(substr($line, 1));
                        $x = explode(';', $x);
                        $extension_dir = str_replace('"', '', array_shift($x));
                        continue;
                    }
                }
                if (strtolower(substr($line, 0, 9)) == 'extension') {
                    $line = trim(substr($line, 9));
                    if ($line[0] == '=') {
                        $x = trim(substr($line, 1));
                        $x = explode(';', $x);
                        $extensions[$linenum] = str_replace('"', '', array_shift($x));
                        continue;
                    }
                }
                if (strtolower(substr($line, 0, strlen($zend_extension_line))) ==
                      $zend_extension_line) {
                    $line = trim(substr($line, strlen($zend_extension_line)));
                    if ($line[0] == '=') {
                        $x = trim(substr($line, 1));
                        $x = explode(';', $x);
                        $zend_extensions[$linenum] = str_replace('"', '', array_shift($x));
                        continue;
                    }
                }
            }
            return array(
                'extensions' => $extensions,
                'zend_extensions' => $zend_extensions,
                'extension_dir' => $extension_dir,
                'all' => $all,
            );
        } else {
            return PEAR::raiseError('php.ini "' . $filename . '" does not exist');
        }
    }

    // {{{ doInstall()

    function doInstall($command, $options, $params)
    {
        if (!class_exists('PEAR_PackageFile')) {
            require_once 'PEAR/PackageFile.php';
        }
        if (empty($this->installer)) {
            $this->installer = &$this->getInstaller($this->ui);
        }
        if ($command == 'upgrade' || $command == 'upgrade-all') {
            $options['upgrade'] = true;
        } else {
            $packages = $params;
        }
        if (isset($options['installroot']) && isset($options['packagingroot'])) {
            return $this->raiseError('ERROR: cannot use both --installroot and --packagingroot');
        }
        $reg = &$this->config->getRegistry();
        $instreg = &$reg; // instreg used to check if package is installed
        if (isset($options['packagingroot']) && !isset($options['upgrade'])) {
            $packrootphp_dir = $this->installer->_prependPath(
                $this->config->get('php_dir', null, 'pear.php.net'),
                $options['packagingroot']);
            $instreg = new PEAR_Registry($packrootphp_dir); // other instreg!

            if ($this->config->get('verbose') > 2) {
                $this->ui->outputData('using package root: ' . $options['packagingroot']);
            }
        }
        $abstractpackages = array();
        $otherpackages = array();
        // parse params
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        foreach($params as $param) {
            if (strpos($param, 'http://') === 0) {
                $otherpackages[] = $param;
                continue;
            }
            if (strpos($param, 'channel://') === false && @file_exists($param)) {
                if (isset($options['force'])) {
                    $otherpackages[] = $param;
                    continue;
                }
                $pkg = new PEAR_PackageFile($this->config);
                $pf = $pkg->fromAnyFile($param, PEAR_VALIDATE_DOWNLOADING);
                if (PEAR::isError($pf)) {
                    $otherpackages[] = $param;
                    continue;
                }
                if ($reg->packageExists($pf->getPackage(), $pf->getChannel()) &&
                      version_compare($pf->getVersion(),
                      $reg->packageInfo($pf->getPackage(), 'version', $pf->getChannel()),
                      '<=')) {
                    if ($this->config->get('verbose')) {
                        $this->ui->outputData('Ignoring installed package ' .
                            $reg->parsedPackageNameToString(
                            array('package' => $pf->getPackage(),
                                  'channel' => $pf->getChannel()), true));
                    }
                    continue;
                }
                $otherpackages[] = $param;
                continue;
            }
            $e = $reg->parsePackageName($param, $this->config->get('default_channel'));
            if (PEAR::isError($e)) {
                $otherpackages[] = $param;
            } else {
                $abstractpackages[] = $e;
            }
        }
        PEAR::staticPopErrorHandling();

        // if there are any local package .tgz or remote static url, we can't
        // filter.  The filter only works for abstract packages
        if (count($abstractpackages) && !isset($options['force'])) {
            // when not being forced, only do necessary upgrades/installs
            if (isset($options['upgrade'])) {
                $abstractpackages = $this->_filterUptodatePackages($abstractpackages,
                    $command);
            } else {
                foreach ($abstractpackages as $i => $package) {
                    if (isset($package['group'])) {
                        // do not filter out install groups
                        continue;
                    }
                    if ($instreg->packageExists($package['package'], $package['channel'])) {
                        if ($this->config->get('verbose')) {
                            $this->ui->outputData('Ignoring installed package ' .
                                $reg->parsedPackageNameToString($package, true));
                        }
                        unset($abstractpackages[$i]);
                    }
                }
            }
            $abstractpackages =
                array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages);
        } elseif (count($abstractpackages)) {
            $abstractpackages =
                array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages);
        }


        $packages = array_merge($abstractpackages, $otherpackages);
        if (!count($packages)) {
            $this->ui->outputData('Nothing to ' . $command);
            return true;
        }

        $this->downloader = &$this->getDownloader($this->ui, $options, $this->config);
        $errors = array();
        $binaries = array();
        $downloaded = array();
        $downloaded = &$this->downloader->download($packages);
        if (PEAR::isError($downloaded)) {
            return $this->raiseError($downloaded);
        }
        $errors = $this->downloader->getErrorMsgs();
        if (count($errors)) {
            $err = array();
            $err['data'] = array();
            foreach ($errors as $error) {
                $err['data'][] = array($error);
            }
            $err['headline'] = 'Install Errors';
            $this->ui->outputData($err);
            if (!count($downloaded)) {
                return $this->raiseError("$command failed");
            }
        }
        $data = array(
            'headline' => 'Packages that would be Installed'
        );
        if (isset($options['pretend'])) {
            foreach ($downloaded as $package) {
                $data['data'][] = array($reg->parsedPackageNameToString($package->getParsedPackage()));
            }
            $this->ui->outputData($data, 'pretend');
            return true;
        }
        $this->installer->setOptions($options);
        $this->installer->sortPackagesForInstall($downloaded);
        if (PEAR::isError($err = $this->installer->setDownloadedPackages($downloaded))) {
            $this->raiseError($err->getMessage());
            return true;
        }
        $extrainfo = array();
        $binaries = array();
        foreach ($downloaded as $param) {
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            $info = $this->installer->install($param, $options);
            PEAR::staticPopErrorHandling();
            if (PEAR::isError($info)) {
                $oldinfo = $info;
                $pkg = &$param->getPackageFile();
                if ($info->getCode() != PEAR_INSTALLER_NOBINARY) {
                    if (!($info = $pkg->installBinary($this->installer))) {
                        $this->ui->outputData('ERROR: ' .$oldinfo->getMessage());
                        continue;
                    }
                    // we just installed a different package than requested,
                    // let's change the param and info so that the rest of this works
                    $param = $info[0];
                    $info = $info[1];
                }
            }
            if (is_array($info)) {
                if ($param->getPackageType() == 'extsrc' ||
                      $param->getPackageType() == 'extbin' ||
                      $param->getPackageType() == 'zendextsrc' ||
                      $param->getPackageType() == 'zendextbin') {
                    $pkg = &$param->getPackageFile();
                    if ($instbin = $pkg->getInstalledBinary()) {
                        $instpkg = &$instreg->getPackage($instbin, $pkg->getChannel());
                    } else {
                        $instpkg = &$instreg->getPackage($pkg->getPackage(), $pkg->getChannel());
                    }

                    foreach ($instpkg->getFilelist() as $name => $atts) {
                        $pinfo = pathinfo($atts['installed_as']);
                        if (!isset($pinfo['extension']) ||
                              in_array($pinfo['extension'], array('c', 'h'))) {
                            continue; // make sure we don't match php_blah.h
                        }
                        if ((strpos($pinfo['basename'], 'php_') === 0 &&
                              $pinfo['extension'] == 'dll') ||
                              // most unices
                              $pinfo['extension'] == 'so' ||
                              // hp-ux
                              $pinfo['extension'] == 'sl') {
                            $binaries[] = array($atts['installed_as'], $pinfo);
                            break;
                        }
                    }
                    if (count($binaries)) {
                        foreach ($binaries as $pinfo) {
                            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
                            $ret = $this->enableExtension(array($pinfo[0]), $param->getPackageType());
                            PEAR::staticPopErrorHandling();
                            if (PEAR::isError($ret)) {
                                $extrainfo[] = $ret->getMessage();
                                if ($param->getPackageType() == 'extsrc' ||
                                      $param->getPackageType() == 'extbin') {
                                    $exttype = 'extension';
                                } else {
                                    ob_start();
                                    phpinfo(INFO_GENERAL);
                                    $info = ob_get_contents();
                                    ob_end_clean();
                                    $debug = function_exists('leak') ? '_debug' : '';
                                    $ts = preg_match('Thread Safety.+enabled', $info) ? '_ts' : '';
                                    $exttype = 'zend_extension' . $debug . $ts;
                                }
                                $extrainfo[] = 'You should add "' . $exttype . '=' .
                                    $pinfo[1]['basename'] . '" to php.ini';
                            } else {
                                $extrainfo[] = 'Extension ' . $instpkg->getProvidesExtension() .
                                    ' enabled in php.ini';
                            }
                        }
                    }
                }
                if ($this->config->get('verbose') > 0) {
                    $channel = $param->getChannel();
                    $label = $reg->parsedPackageNameToString(
                        array(
                            'channel' => $channel,
                            'package' => $param->getPackage(),
                            'version' => $param->getVersion(),
                        ));
                    $out = array('data' => "$command ok: $label");
                    if (isset($info['release_warnings'])) {
                        $out['release_warnings'] = $info['release_warnings'];
                    }
                    $this->ui->outputData($out, $command);
                    if (!isset($options['register-only']) && !isset($options['offline'])) {
                        if ($this->config->isDefinedLayer('ftp')) {
                            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
                            $info = $this->installer->ftpInstall($param);
                            PEAR::staticPopErrorHandling();
                            if (PEAR::isError($info)) {
                                $this->ui->outputData($info->getMessage());
                                $this->ui->outputData("remote install failed: $label");
                            } else {
                                $this->ui->outputData("remote install ok: $label");
                            }
                        }
                    }
                }
                $deps = $param->getDeps();
                if ($deps) {
                    if (isset($deps['group'])) {
                        $groups = $deps['group'];
                        if (!isset($groups[0])) {
                            $groups = array($groups);
                        }
                        foreach ($groups as $group) {
                            if ($group['attribs']['name'] == 'default') {
                                // default group is always installed, unless the user
                                // explicitly chooses to install another group
                                continue;
                            }
                            $extrainfo[] = $param->getPackage() . ': Optional feature ' .
                                $group['attribs']['name'] . ' available (' .
                                $group['attribs']['hint'] . ')';
                        }
                        $extrainfo[] = $param->getPackage() .
                            ': To install optional features use "pear install ' .
                            $reg->parsedPackageNameToString(
                                array('package' => $param->getPackage(),
                                      'channel' => $param->getChannel()), true) .
                                  '#featurename"';
                    }
                }
                $pkg = &$instreg->getPackage($param->getPackage(), $param->getChannel());
                // $pkg may be NULL if install is a 'fake' install via --packagingroot
                if (is_object($pkg)) {
                    $pkg->setConfig($this->config);
                    if ($list = $pkg->listPostinstallScripts()) {
                        $pn = $reg->parsedPackageNameToString(array('channel' =>
                           $param->getChannel(), 'package' => $param->getPackage()), true);
                        $extrainfo[] = $pn . ' has post-install scripts:';
                        foreach ($list as $file) {
                            $extrainfo[] = $file;
                        }
                        $extrainfo[] = $param->getPackage() .
                            ': Use "pear run-scripts ' . $pn . '" to finish setup.';
                        $extrainfo[] = 'DO NOT RUN SCRIPTS FROM UNTRUSTED SOURCES';
                    }
                }
            } else {
                return $this->raiseError("$command failed");
            }
        }
        if (count($extrainfo)) {
            foreach ($extrainfo as $info) {
                $this->ui->outputData($info);
            }
        }
        return true;
    }

    // }}}
    // {{{ doUpgradeAll()

    function doUpgradeAll($command, $options, $params)
    {
        $reg = &$this->config->getRegistry();
        $toUpgrade = array();
        foreach ($reg->listChannels() as $channel) {
            if ($channel == '__uri') {
                continue;
            }

            // parse name with channel
            foreach ($reg->listPackages($channel) as $name) {
                $toUpgrade[] = $reg->parsedPackageNameToString(array(
                        'channel' => $channel,
                        'package' => $name
                    ));
            }
        }

        $err = $this->doInstall('upgrade-all', $options, $toUpgrade);
        if (PEAR::isError($err)) {
            $this->ui->outputData($err->getMessage(), $command);
        }
   }

    // }}}
    // {{{ doUninstall()

    function doUninstall($command, $options, $params)
    {
        if (empty($this->installer)) {
            $this->installer = &$this->getInstaller($this->ui);
        }
        if (isset($options['remoteconfig'])) {
            $e = $this->config->readFTPConfigFile($options['remoteconfig']);
            if (!PEAR::isError($e)) {
                $this->installer->setConfig($this->config);
            }
        }
        if (sizeof($params) < 1) {
            return $this->raiseError("Please supply the package(s) you want to uninstall");
        }
        $reg = &$this->config->getRegistry();
        $newparams = array();
        $binaries = array();
        $badparams = array();
        foreach ($params as $pkg) {
            $channel = $this->config->get('default_channel');
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            $parsed = $reg->parsePackageName($pkg, $channel);
            PEAR::staticPopErrorHandling();
            if (!$parsed || PEAR::isError($parsed)) {
                $badparams[] = $pkg;
                continue;
            }
            $package = $parsed['package'];
            $channel = $parsed['channel'];
            $info = &$reg->getPackage($package, $channel);
            if ($info === null &&
                 ($channel == 'pear.php.net' || $channel == 'pecl.php.net')) {
                // make sure this isn't a package that has flipped from pear to pecl but
                // used a package.xml 1.0
                $testc = ($channel == 'pear.php.net') ? 'pecl.php.net' : 'pear.php.net';
                $info = &$reg->getPackage($package, $testc);
                if ($info !== null) {
                    $channel = $testc;
                }
            }
            if ($info === null) {
                $badparams[] = $pkg;
            } else {
                $newparams[] = &$info;
                // check for binary packages (this is an alias for those packages if so)
                if ($installedbinary = $info->getInstalledBinary()) {
                    $this->ui->log('adding binary package ' .
                        $reg->parsedPackageNameToString(array('channel' => $channel,
                            'package' => $installedbinary), true));
                    $newparams[] = &$reg->getPackage($installedbinary, $channel);
                }
                // add the contents of a dependency group to the list of installed packages
                if (isset($parsed['group'])) {
                    $group = $info->getDependencyGroup($parsed['group']);
                    if ($group) {
                        $installed = $reg->getInstalledGroup($group);
                        if ($installed) {
                            foreach ($installed as $i => $p) {
                                $newparams[] = &$installed[$i];
                            }
                        }
                    }
                }
            }
        }
        $err = $this->installer->sortPackagesForUninstall($newparams);
        if (PEAR::isError($err)) {
            $this->ui->outputData($err->getMessage(), $command);
            return true;
        }
        $params = $newparams;
        // twist this to use it to check on whether dependent packages are also being uninstalled
        // for circular dependencies like subpackages
        $this->installer->setUninstallPackages($newparams);
        $params = array_merge($params, $badparams);
        $binaries = array();
        foreach ($params as $pkg) {
            $this->installer->pushErrorHandling(PEAR_ERROR_RETURN);
            if ($err = $this->installer->uninstall($pkg, $options)) {
                $this->installer->popErrorHandling();
                if (PEAR::isError($err)) {
                    $this->ui->outputData($err->getMessage(), $command);
                    continue;
                }
                if ($pkg->getPackageType() == 'extsrc' ||
                      $pkg->getPackageType() == 'extbin' ||
                      $pkg->getPackageType() == 'zendextsrc' ||
                      $pkg->getPackageType() == 'zendextbin') {
                    if ($instbin = $pkg->getInstalledBinary()) {
                        continue; // this will be uninstalled later
                    }

                    foreach ($pkg->getFilelist() as $name => $atts) {
                        $pinfo = pathinfo($atts['installed_as']);
                        if (!isset($pinfo['extension']) ||
                              in_array($pinfo['extension'], array('c', 'h'))) {
                            continue; // make sure we don't match php_blah.h
                        }
                        if ((strpos($pinfo['basename'], 'php_') === 0 &&
                              $pinfo['extension'] == 'dll') ||
                              // most unices
                              $pinfo['extension'] == 'so' ||
                              // hp-ux
                              $pinfo['extension'] == 'sl') {
                            $binaries[] = array($atts['installed_as'], $pinfo);
                            break;
                        }
                    }
                    if (count($binaries)) {
                        foreach ($binaries as $pinfo) {
                            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
                            $ret = $this->disableExtension(array($pinfo[0]), $pkg->getPackageType());
                            PEAR::staticPopErrorHandling();
                            if (PEAR::isError($ret)) {
                                $extrainfo[] = $ret->getMessage();
                                if ($pkg->getPackageType() == 'extsrc' ||
                                      $pkg->getPackageType() == 'extbin') {
                                    $exttype = 'extension';
                                } else {
                                    ob_start();
                                    phpinfo(INFO_GENERAL);
                                    $info = ob_get_contents();
                                    ob_end_clean();
                                    $debug = function_exists('leak') ? '_debug' : '';
                                    $ts = preg_match('Thread Safety.+enabled', $info) ? '_ts' : '';
                                    $exttype = 'zend_extension' . $debug . $ts;
                                }
                                $this->ui->outputData('Unable to remove "' . $exttype . '=' .
                                    $pinfo[1]['basename'] . '" from php.ini', $command);
                            } else {
                                $this->ui->outputData('Extension ' . $pkg->getProvidesExtension() .
                                    ' disabled in php.ini', $command);
                            }
                        }
                    }
                }
                $savepkg = $pkg;
                if ($this->config->get('verbose') > 0) {
                    if (is_object($pkg)) {
                        $pkg = $reg->parsedPackageNameToString($pkg);
                    }
                    $this->ui->outputData("uninstall ok: $pkg", $command);
                }
                if (!isset($options['offline']) && is_object($savepkg) &&
                      defined('PEAR_REMOTEINSTALL_OK')) {
                    if ($this->config->isDefinedLayer('ftp')) {
                        $this->installer->pushErrorHandling(PEAR_ERROR_RETURN);
                        $info = $this->installer->ftpUninstall($savepkg);
                        $this->installer->popErrorHandling();
                        if (PEAR::isError($info)) {
                            $this->ui->outputData($info->getMessage());
                            $this->ui->outputData("remote uninstall failed: $pkg");
                        } else {
                            $this->ui->outputData("remote uninstall ok: $pkg");
                        }
                    }
                }
            } else {
                $this->installer->popErrorHandling();
                if (is_object($pkg)) {
                    $pkg = $reg->parsedPackageNameToString($pkg);
                }
                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)
    {
        $downloader = &$this->getDownloader($this->ui, array('force' => true, 'nodeps' => true,
            'soft' => true, 'downloadonly' => true), $this->config);
        $reg = &$this->config->getRegistry();
        if (sizeof($params) < 1) {
            return $this->raiseError("Please supply the package you want to bundle");
        }

        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;
            }
        }
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        $err = $downloader->setDownloadDir($dest);
        PEAR::staticPopErrorHandling();
        if (PEAR::isError($err)) {
            return PEAR::raiseError('download directory "' . $dest .
                '" is not writeable.');
        }
        $result = &$downloader->download(array($params[0]));
        if (PEAR::isError($result)) {
            return $result;
        }
        if (!isset($result[0])) {
            return $this->raiseError('unable to unpack ' . $params[0]);
        }
        $pkgfile = &$result[0]->getPackageFile();
        $pkgname = $pkgfile->getName();
        $pkgversion = $pkgfile->getVersion();

        // Unpacking -------------------------------------------------
        $dest .= DIRECTORY_SEPARATOR . $pkgname;
        $orig = $pkgname . '-' . $pkgversion;

        $tar = &new Archive_Tar($pkgfile->getArchiveFile());
        if (!$tar->extractModify($dest, $orig)) {
            return $this->raiseError('unable to unpack ' . $pkgfile->getArchiveFile());
        }
        $this->ui->outputData("Package ready at '$dest'");
    // }}}
    }

    // }}}

    function doRunScripts($command, $options, $params)
    {
        if (!isset($params[0])) {
            return $this->raiseError('run-scripts expects 1 parameter: a package name');
        }
        $reg = &$this->config->getRegistry();
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
        PEAR::staticPopErrorHandling();
        if (PEAR::isError($parsed)) {
            return $this->raiseError($parsed);
        }
        $package = &$reg->getPackage($parsed['package'], $parsed['channel']);
        if (is_object($package)) {
            $package->setConfig($this->config);
            $package->runPostinstallScripts();
        } else {
            return $this->raiseError('Could not retrieve package "' . $params[0] . '" from registry');
        }
        $this->ui->outputData('Install scripts complete', $command);
        return true;
    }

    /**
     * Given a list of packages, filter out those ones that are already up to date
     *
     * @param $packages: packages, in parsed array format !
     * @return list of packages that can be upgraded
     */
    function _filterUptodatePackages($packages, $command)
    {
        $reg = &$this->config->getRegistry();
        $latestReleases = array();

        $ret = array();
        foreach($packages as $package) {
            if (isset($package['group'])) {
                $ret[] = $package;
                continue;
            }
            $channel = $package['channel'];
            $name = $package['package'];

            if (!$reg->packageExists($name, $channel)) {
                $ret[] = $package;
                continue;
            }
            if (!isset($latestReleases[$channel])) {
                // fill in cache for this channel
                $chan = &$reg->getChannel($channel);
                if (PEAR::isError($chan)) {
                    return $this->raiseError($chan);
                }
                if ($chan->supportsREST($this->config->get('preferred_mirror',
                                                           null, $channel)) &&
                      $base = $chan->getBaseURL('REST1.0',
                                                $this->config->get('preferred_mirror',
                                                                   null, $channel)))
                {
                    $dorest = true;
                } else {
                    $dorest = false;
                    $remote = &$this->config->getRemote($this->config);
                }
                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
                if ($dorest) {
                    $rest = &$this->config->getREST('1.0', array());
                    $installed = array_flip($reg->listPackages($channel));
                    $latest = $rest->listLatestUpgrades($base,
                        $this->config->get('preferred_state', null, $channel), $installed,
                        $channel, $reg);
                } else {
                    $latest = $remote->call("package.listLatestReleases",
                        $this->config->get('preferred_state', null, $channel));
                    unset($remote);
                }
                PEAR::staticPopErrorHandling();
                if (PEAR::isError($latest)) {
                    $this->ui->outputData('Error getting channel info from ' . $channel .
                        ': ' . $latest->getMessage());
                    continue;
                }

                $latestReleases[$channel] = array_change_key_case($latest);
            }

            // check package for latest release
            if (isset($latestReleases[$channel][strtolower($name)])) {
                // if not set, up to date
                $inst_version = $reg->packageInfo($name, 'version', $channel);
                $channel_version = $latestReleases[$channel][strtolower($name)]['version'];
                if (version_compare($channel_version, $inst_version, "le")) {
                    // installed version is up-to-date
                    continue;
                }
                // maintain BC
                if ($command == 'upgrade-all') {
                    $this->ui->outputData(array('data' => 'Will upgrade ' .
                        $reg->parsedPackageNameToString($package)), $command);
                }
                $ret[] = $package;
            }
        }

        return $ret;
    }

}
?>
PEAR-1.7.2/PEAR/Command/Mirror.xml100644   1750   1750        1151 11014312742  11564 <commands version="1.0">
 <download-all>
  <summary>Downloads each available package from the default channel</summary>
  <function>doDownloadAll</function>
  <shortcut>da</shortcut>
  <options>
   <channel>
    <shortopt>c</shortopt>
    <doc>specify a channel other than the default channel</doc>
    <arg>CHAN</arg>
   </channel>
  </options>
  <doc>
Requests a list of available packages from the default channel ({config default_channel})
and downloads them to current working directory.  Note: only
packages within preferred_state ({config preferred_state}) will be downloaded</doc>
 </download-all>
</commands>PEAR-1.7.2/PEAR/Command/Mirror.php100644   1750   1750       12230 11014312742  11573 <?php
/**
 * PEAR_Command_Mirror (download-all command)
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Alexander Merz <alexmerz@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Mirror.php,v 1.20 2008/04/11 01:16:40 dufuz Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.2.0
 */

/**
 * base class
 */
require_once 'PEAR/Command/Common.php';

/**
 * PEAR commands for providing file mirrors
 *
 * @category   pear
 * @package    PEAR
 * @author     Alexander Merz <alexmerz@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.2.0
 */
class PEAR_Command_Mirror extends PEAR_Command_Common
{
    // {{{ properties

    var $commands = array(
        'download-all' => array(
            'summary' => 'Downloads each available package from the default channel',
            'function' => 'doDownloadAll',
            'shortcut' => 'da',
            'options' => array(
                'channel' =>
                    array(
                    'shortopt' => 'c',
                    'doc' => 'specify a channel other than the default channel',
                    'arg' => 'CHAN',
                    ),
                ),
            'doc' => '
Requests a list of available packages from the default channel ({config default_channel})
and downloads them to current working directory.  Note: only
packages within preferred_state ({config preferred_state}) will be downloaded'
            ),
        );

    // }}}

    // {{{ constructor

    /**
     * PEAR_Command_Mirror constructor.
     *
     * @access public
     * @param object PEAR_Frontend a reference to an frontend
     * @param object PEAR_Config a reference to the configuration data
     */
    function PEAR_Command_Mirror(&$ui, &$config)
    {
        parent::PEAR_Command_Common($ui, $config);
    }

    // }}}

    /**
     * For unit-testing
     */
    function &factory($a)
    {
        $a = &PEAR_Command::factory($a, $this->config);
        return $a;
    }

    // {{{ doDownloadAll()
    /**
    * retrieves a list of avaible Packages from master server
    * and downloads them
    *
    * @access public
    * @param string $command the command
    * @param array $options the command options before the command
    * @param array $params the stuff after the command name
    * @return bool true if succesful
    * @throw PEAR_Error
    */
    function doDownloadAll($command, $options, $params)
    {
        $savechannel = $this->config->get('default_channel');
        $reg = &$this->config->getRegistry();
        $channel = isset($options['channel']) ? $options['channel'] :
            $this->config->get('default_channel');
        if (!$reg->channelExists($channel)) {
            $this->config->set('default_channel', $savechannel);
            return $this->raiseError('Channel "' . $channel . '" does not exist');
        }
        $this->config->set('default_channel', $channel);
        $this->ui->outputData('Using Channel ' . $this->config->get('default_channel'));
        $chan = $reg->getChannel($channel);
        if (PEAR::isError($chan)) {
            return $this->raiseError($chan);
        }
        if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
              $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
            $rest = &$this->config->getREST('1.0', array());
            $remoteInfo = array_flip($rest->listPackages($base, $channel));
        } else {
            $remote = &$this->config->getRemote();
            $stable = ($this->config->get('preferred_state') == 'stable');
            $remoteInfo = $remote->call("package.listAll", true, $stable, false);
        }
        if (PEAR::isError($remoteInfo)) {
            return $remoteInfo;
        }
        $cmd = &$this->factory("download");
        if (PEAR::isError($cmd)) {
            return $cmd;
        }
        $this->ui->outputData('Using Preferred State of ' .
            $this->config->get('preferred_state'));
        $this->ui->outputData('Gathering release information, please wait...');
        /**
         * Error handling not necessary, because already done by
         * the download command
         */
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        $err = $cmd->run('download', array('downloadonly' => true), array_keys($remoteInfo));
        PEAR::staticPopErrorHandling();
        $this->config->set('default_channel', $savechannel);
        if (PEAR::isError($err)) {
            $this->ui->outputData($err->getMessage());
        }
        return true;
    }

    // }}}
}
PEAR-1.7.2/PEAR/Command/Package.xml100644   1750   1750       13206 11014312742  11671 <commands version="1.0">
 <package>
  <summary>Build Package</summary>
  <function>doPackage</function>
  <shortcut>p</shortcut>
  <options>
   <nocompress>
    <shortopt>Z</shortopt>
    <doc>Do not gzip the package file</doc>
   </nocompress>
   <showname>
    <shortopt>n</shortopt>
    <doc>Print the name of the packaged file.</doc>
   </showname>
  </options>
  <doc>[descfile] [descfile2]
Creates a PEAR package from its description file (usually called
package.xml).  If a second packagefile is passed in, then
the packager will check to make sure that one is a package.xml
version 1.0, and the other is a package.xml version 2.0.  The
package.xml version 1.0 will be saved as &quot;package.xml&quot; in the archive,
and the other as &quot;package2.xml&quot; in the archive&quot;
</doc>
 </package>
 <package-validate>
  <summary>Validate Package Consistency</summary>
  <function>doPackageValidate</function>
  <shortcut>pv</shortcut>
  <options />
  <doc>
</doc>
 </package-validate>
 <cvsdiff>
  <summary>Run a &quot;cvs diff&quot; for all files in a package</summary>
  <function>doCvsDiff</function>
  <shortcut>cd</shortcut>
  <options>
   <quiet>
    <shortopt>q</shortopt>
    <doc>Be quiet</doc>
   </quiet>
   <reallyquiet>
    <shortopt>Q</shortopt>
    <doc>Be really quiet</doc>
   </reallyquiet>
   <date>
    <shortopt>D</shortopt>
    <doc>Diff against revision of DATE</doc>
    <arg>DATE</arg>
   </date>
   <release>
    <shortopt>R</shortopt>
    <doc>Diff against tag for package release REL</doc>
    <arg>REL</arg>
   </release>
   <revision>
    <shortopt>r</shortopt>
    <doc>Diff against revision REV</doc>
    <arg>REV</arg>
   </revision>
   <context>
    <shortopt>c</shortopt>
    <doc>Generate context diff</doc>
   </context>
   <unified>
    <shortopt>u</shortopt>
    <doc>Generate unified diff</doc>
   </unified>
   <ignore-case>
    <shortopt>i</shortopt>
    <doc>Ignore case, consider upper- and lower-case letters equivalent</doc>
   </ignore-case>
   <ignore-whitespace>
    <shortopt>b</shortopt>
    <doc>Ignore changes in amount of white space</doc>
   </ignore-whitespace>
   <ignore-blank-lines>
    <shortopt>B</shortopt>
    <doc>Ignore changes that insert or delete blank lines</doc>
   </ignore-blank-lines>
   <brief>
    <doc>Report only whether the files differ, no details</doc>
   </brief>
   <dry-run>
    <shortopt>n</shortopt>
    <doc>Don&apos;t do anything, just pretend</doc>
   </dry-run>
  </options>
  <doc>&lt;package.xml&gt;
Compares all the files in a package.  Without any options, this
command will compare the current code with the last checked-in code.
Using the -r or -R option you may compare the current code with that
of a specific release.
</doc>
 </cvsdiff>
 <cvstag>
  <summary>Set CVS Release Tag</summary>
  <function>doCvsTag</function>
  <shortcut>ct</shortcut>
  <options>
   <quiet>
    <shortopt>q</shortopt>
    <doc>Be quiet</doc>
   </quiet>
   <reallyquiet>
    <shortopt>Q</shortopt>
    <doc>Be really quiet</doc>
   </reallyquiet>
   <slide>
    <shortopt>F</shortopt>
    <doc>Move (slide) tag if it exists</doc>
   </slide>
   <delete>
    <shortopt>d</shortopt>
    <doc>Remove tag</doc>
   </delete>
   <dry-run>
    <shortopt>n</shortopt>
    <doc>Don&apos;t do anything, just pretend</doc>
   </dry-run>
  </options>
  <doc>&lt;package.xml&gt;
Sets a CVS tag on all files in a package.  Use this command after you have
packaged a distribution tarball with the &quot;package&quot; command to tag what
revisions of what files were in that release.  If need to fix something
after running cvstag once, but before the tarball is released to the public,
use the &quot;slide&quot; option to move the release tag.
</doc>
 </cvstag>
 <package-dependencies>
  <summary>Show package dependencies</summary>
  <function>doPackageDependencies</function>
  <shortcut>pd</shortcut>
  <options />
  <doc>
List all dependencies the package has.</doc>
 </package-dependencies>
 <sign>
  <summary>Sign a package distribution file</summary>
  <function>doSign</function>
  <shortcut>si</shortcut>
  <options />
  <doc>&lt;package-file&gt;
Signs a package distribution (.tar or .tgz) file with GnuPG.</doc>
 </sign>
 <makerpm>
  <summary>Builds an RPM spec file from a PEAR package</summary>
  <function>doMakeRPM</function>
  <shortcut>rpm</shortcut>
  <options>
   <spec-template>
    <shortopt>t</shortopt>
    <arg>FILE</arg>
    <doc>Use FILE as RPM spec file template</doc>
   </spec-template>
   <rpm-pkgname>
    <shortopt>p</shortopt>
    <arg>FORMAT</arg>
    <doc>Use FORMAT as format string for RPM package name, %s is replaced
by the PEAR package name, defaults to &quot;PEAR::%s&quot;.</doc>
   </rpm-pkgname>
  </options>
  <doc>&lt;package-file&gt;

Creates an RPM .spec file for wrapping a PEAR package inside an RPM
package.  Intended to be used from the SPECS directory, with the PEAR
package tarball in the SOURCES directory:

$ pear makerpm ../SOURCES/Net_Socket-1.0.tgz
Wrote RPM spec file PEAR::Net_Geo-1.0.spec
$ rpm -bb PEAR::Net_Socket-1.0.spec
...
Wrote: /usr/src/redhat/RPMS/i386/PEAR::Net_Socket-1.0-1.i386.rpm
</doc>
 </makerpm>
 <convert>
  <summary>Convert a package.xml 1.0 to package.xml 2.0 format</summary>
  <function>doConvert</function>
  <shortcut>c2</shortcut>
  <options>
   <flat>
    <shortopt>f</shortopt>
    <doc>do not beautify the filelist.</doc>
   </flat>
  </options>
  <doc>[descfile] [descfile2]
Converts a package.xml in 1.0 format into a package.xml
in 2.0 format.  The new file will be named package2.xml by default,
and package.xml will be used as the old file by default.
This is not the most intelligent conversion, and should only be
used for automated conversion or learning the format.
</doc>
 </convert>
</commands>
PEAR-1.7.2/PEAR/Command/Package.php100644   1750   1750       75344 11014312742  11673 <?php
/**
 * PEAR_Command_Package (package, package-validate, cvsdiff, cvstag, package-dependencies,
 * sign, makerpm, convert commands)
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Martin Jansen <mj@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Package.php,v 1.128 2008/03/29 21:06:58 dufuz Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * base class
 */
require_once 'PEAR/Command/Common.php';

/**
 * PEAR commands for login/logout
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Martin Jansen <mj@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: @package_version@
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */

class PEAR_Command_Package extends PEAR_Command_Common
{
    // {{{ properties

    var $commands = array(
        'package' => array(
            'summary' => 'Build Package',
            'function' => 'doPackage',
            'shortcut' => 'p',
            'options' => array(
                'nocompress' => array(
                    'shortopt' => 'Z',
                    'doc' => 'Do not gzip the package file'
                    ),
                'showname' => array(
                    'shortopt' => 'n',
                    'doc' => 'Print the name of the packaged file.',
                    ),
                ),
            'doc' => '[descfile] [descfile2]
Creates a PEAR package from its description file (usually called
package.xml).  If a second packagefile is passed in, then
the packager will check to make sure that one is a package.xml
version 1.0, and the other is a package.xml version 2.0.  The
package.xml version 1.0 will be saved as "package.xml" in the archive,
and the other as "package2.xml" in the archive"
'
            ),
        'package-validate' => array(
            'summary' => 'Validate Package Consistency',
            'function' => 'doPackageValidate',
            'shortcut' => 'pv',
            'options' => array(),
            'doc' => '
',
            ),
        'cvsdiff' => array(
            'summary' => 'Run a "cvs diff" for all files in a package',
            'function' => 'doCvsDiff',
            'shortcut' => 'cd',
            'options' => array(
                'quiet' => array(
                    'shortopt' => 'q',
                    'doc' => 'Be quiet',
                    ),
                'reallyquiet' => array(
                    'shortopt' => 'Q',
                    'doc' => 'Be really quiet',
                    ),
                'date' => array(
                    'shortopt' => 'D',
                    'doc' => 'Diff against revision of DATE',
                    'arg' => 'DATE',
                    ),
                'release' => array(
                    'shortopt' => 'R',
                    'doc' => 'Diff against tag for package release REL',
                    'arg' => 'REL',
                    ),
                'revision' => array(
                    'shortopt' => 'r',
                    'doc' => 'Diff against revision REV',
                    'arg' => 'REV',
                    ),
                'context' => array(
                    'shortopt' => 'c',
                    'doc' => 'Generate context diff',
                    ),
                'unified' => array(
                    'shortopt' => 'u',
                    'doc' => 'Generate unified diff',
                    ),
                'ignore-case' => array(
                    'shortopt' => 'i',
                    'doc' => 'Ignore case, consider upper- and lower-case letters equivalent',
                    ),
                'ignore-whitespace' => array(
                    'shortopt' => 'b',
                    'doc' => 'Ignore changes in amount of white space',
                    ),
                'ignore-blank-lines' => array(
                    'shortopt' => 'B',
                    'doc' => 'Ignore changes that insert or delete blank lines',
                    ),
                'brief' => array(
                    'doc' => 'Report only whether the files differ, no details',
                    ),
                'dry-run' => array(
                    'shortopt' => 'n',
                    'doc' => 'Don\'t do anything, just pretend',
                    ),
                ),
            'doc' => '<package.xml>
Compares all the files in a package.  Without any options, this
command will compare the current code with the last checked-in code.
Using the -r or -R option you may compare the current code with that
of a specific release.
',
            ),
        'cvstag' => array(
            'summary' => 'Set CVS Release Tag',
            'function' => 'doCvsTag',
            'shortcut' => 'ct',
            'options' => array(
                'quiet' => array(
                    'shortopt' => 'q',
                    'doc' => 'Be quiet',
                    ),
                'reallyquiet' => array(
                    'shortopt' => 'Q',
                    'doc' => 'Be really quiet',
                    ),
                'slide' => array(
                    'shortopt' => 'F',
                    'doc' => 'Move (slide) tag if it exists',
                    ),
                'delete' => array(
                    'shortopt' => 'd',
                    'doc' => 'Remove tag',
                    ),
                'dry-run' => array(
                    'shortopt' => 'n',
                    'doc' => 'Don\'t do anything, just pretend',
                    ),
                ),
            'doc' => '<package.xml> [files...]
Sets a CVS tag on all files in a package.  Use this command after you have
packaged a distribution tarball with the "package" command to tag what
revisions of what files were in that release.  If need to fix something
after running cvstag once, but before the tarball is released to the public,
use the "slide" option to move the release tag.

to include files (such as a second package.xml, or tests not included in the
release), pass them as additional parameters.
',
            ),
        'package-dependencies' => array(
            'summary' => 'Show package dependencies',
            'function' => 'doPackageDependencies',
            'shortcut' => 'pd',
            'options' => array(),
            'doc' => '
List all dependencies the package has.'
            ),
        'sign' => array(
            'summary' => 'Sign a package distribution file',
            'function' => 'doSign',
            'shortcut' => 'si',
            'options' => array(
                'verbose' => array(
                    'shortopt' => 'v',
                    'doc' => 'Display GnuPG output',
                    ),
            ),
            'doc' => '<package-file>
Signs a package distribution (.tar or .tgz) file with GnuPG.',
            ),
        'makerpm' => array(
            'summary' => 'Builds an RPM spec file from a PEAR package',
            'function' => 'doMakeRPM',
            'shortcut' => 'rpm',
            'options' => array(
                'spec-template' => array(
                    'shortopt' => 't',
                    'arg' => 'FILE',
                    'doc' => 'Use FILE as RPM spec file template'
                    ),
                'rpm-pkgname' => array(
                    'shortopt' => 'p',
                    'arg' => 'FORMAT',
                    'doc' => 'Use FORMAT as format string for RPM package name, %s is replaced
by the PEAR package name, defaults to "PEAR::%s".',
                    ),
                ),
            'doc' => '<package-file>

Creates an RPM .spec file for wrapping a PEAR package inside an RPM
package.  Intended to be used from the SPECS directory, with the PEAR
package tarball in the SOURCES directory:

$ pear makerpm ../SOURCES/Net_Socket-1.0.tgz
Wrote RPM spec file PEAR::Net_Geo-1.0.spec
$ rpm -bb PEAR::Net_Socket-1.0.spec
...
Wrote: /usr/src/redhat/RPMS/i386/PEAR::Net_Socket-1.0-1.i386.rpm
',
            ),
        'convert' => array(
            'summary' => 'Convert a package.xml 1.0 to package.xml 2.0 format',
            'function' => 'doConvert',
            'shortcut' => 'c2',
            'options' => array(
                'flat' => array(
                    'shortopt' => 'f',
                    'doc' => 'do not beautify the filelist.',
                    ),
                ),
            'doc' => '[descfile] [descfile2]
Converts a package.xml in 1.0 format into a package.xml
in 2.0 format.  The new file will be named package2.xml by default,
and package.xml will be used as the old file by default.
This is not the most intelligent conversion, and should only be
used for automated conversion or learning the format.
'
            ),
        );

    var $output;

    // }}}
    // {{{ constructor

    /**
     * PEAR_Command_Package constructor.
     *
     * @access public
     */
    function PEAR_Command_Package(&$ui, &$config)
    {
        parent::PEAR_Command_Common($ui, $config);
    }

    // }}}

    // {{{ _displayValidationResults()

    function _displayValidationResults($err, $warn, $strict = false)
    {
        foreach ($err as $e) {
            $this->output .= "Error: $e\n";
        }
        foreach ($warn as $w) {
            $this->output .= "Warning: $w\n";
        }
        $this->output .= sprintf('Validation: %d error(s), %d warning(s)'."\n",
                                       sizeof($err), sizeof($warn));
        if ($strict && sizeof($err) > 0) {
            $this->output .= "Fix these errors and try again.";
            return false;
        }
        return true;
    }

    // }}}
    function &getPackager()
    {
        if (!class_exists('PEAR_Packager')) {
            require_once 'PEAR/Packager.php';
        }
        $a = &new PEAR_Packager;
        return $a;
    }

    function &getPackageFile($config, $debug = false, $tmpdir = null)
    {
        if (!class_exists('PEAR_Common')) {
            require_once 'PEAR/Common.php';
        }
        if (!class_exists('PEAR_PackageFile')) {
            require_once 'PEAR/PackageFile.php';
        }
        $a = &new PEAR_PackageFile($config, $debug, $tmpdir);
        $common = new PEAR_Common;
        $common->ui = $this->ui;
        $a->setLogger($common);
        return $a;
    }
    // {{{ doPackage()

    function doPackage($command, $options, $params)
    {
        $this->output = '';
        $pkginfofile = isset($params[0]) ? $params[0] : 'package.xml';
        $pkg2 = isset($params[1]) ? $params[1] : null;
        if (!$pkg2 && !isset($params[0])) {
            if (file_exists('package2.xml')) {
                $pkg2 = 'package2.xml';
            }
        }
        $packager = &$this->getPackager();
        $compress = empty($options['nocompress']) ? true : false;
        $result = $packager->package($pkginfofile, $compress, $pkg2);
        if (PEAR::isError($result)) {
            return $this->raiseError($result);
        }
        // Don't want output, only the package file name just created
        if (isset($options['showname'])) {
            $this->output = $result;
        }
        if ($this->output) {
            $this->ui->outputData($this->output, $command);
        }
        return true;
    }

    // }}}
    // {{{ doPackageValidate()

    function doPackageValidate($command, $options, $params)
    {
        $this->output = '';
        if (sizeof($params) < 1) {
            $params[0] = "package.xml";
        }
        $obj = &$this->getPackageFile($this->config, $this->_debug);
        $obj->rawReturn();
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        $info = $obj->fromTgzFile($params[0], PEAR_VALIDATE_NORMAL);
        if (PEAR::isError($info)) {
            $info = $obj->fromPackageFile($params[0], PEAR_VALIDATE_NORMAL);
        } else {
            $archive = $info->getArchiveFile();
            $tar = &new Archive_Tar($archive);
            $tar->extract(dirname($info->getPackageFile()));
            $info->setPackageFile(dirname($info->getPackageFile()) . DIRECTORY_SEPARATOR .
                $info->getPackage() . '-' . $info->getVersion() . DIRECTORY_SEPARATOR .
                basename($info->getPackageFile()));
        }
        PEAR::staticPopErrorHandling();
        if (PEAR::isError($info)) {
            return $this->raiseError($info);
        }
        $valid = false;
        if ($info->getPackagexmlVersion() == '2.0') {
            if ($valid = $info->validate(PEAR_VALIDATE_NORMAL)) {
                $info->flattenFileList();
                $valid = $info->validate(PEAR_VALIDATE_PACKAGING);
            }
        } else {
            $valid = $info->validate(PEAR_VALIDATE_PACKAGING);
        }
        $err = $warn = array();
        if ($errors = $info->getValidationWarnings()) {
            foreach ($errors as $error) {
                if ($error['level'] == 'warning') {
                    $warn[] = $error['message'];
                } else {
                    $err[] = $error['message'];
                }
            }
        }
        $this->_displayValidationResults($err, $warn);
        $this->ui->outputData($this->output, $command);
        return true;
    }

    // }}}
    // {{{ doCvsTag()

    function doCvsTag($command, $options, $params)
    {
        $this->output = '';
        $_cmd = $command;
        if (sizeof($params) < 1) {
            $help = $this->getHelp($command);
            return $this->raiseError("$command: missing parameter: $help[0]");
        }
        $obj = &$this->getPackageFile($this->config, $this->_debug);
        $info = $obj->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL);
        if (PEAR::isError($info)) {
            return $this->raiseError($info);
        }
        $err = $warn = array();
        if (!$info->validate()) {
            foreach ($info->getValidationWarnings() as $error) {
                if ($error['level'] == 'warning') {
                    $warn[] = $error['message'];
                } else {
                    $err[] = $error['message'];
                }
            }
        }
        if (!$this->_displayValidationResults($err, $warn, true)) {
            $this->ui->outputData($this->output, $command);
            return $this->raiseError('CVS tag failed');
        }
        $version = $info->getVersion();
        $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $version);
        $cvstag = "RELEASE_$cvsversion";
        $files = array_keys($info->getFilelist());
        $command = "cvs";
        if (isset($options['quiet'])) {
            $command .= ' -q';
        }
        if (isset($options['reallyquiet'])) {
            $command .= ' -Q';
        }
        $command .= ' tag';
        if (isset($options['slide'])) {
            $command .= ' -F';
        }
        if (isset($options['delete'])) {
            $command .= ' -d';
        }
        $command .= ' ' . $cvstag . ' ' . escapeshellarg($params[0]);
        array_shift($params);
        if (count($params)) {
            // add in additional files to be tagged
            $files = array_merge($files, $params);
        }
        foreach ($files as $file) {
            $command .= ' ' . escapeshellarg($file);
        }
        if ($this->config->get('verbose') > 1) {
            $this->output .= "+ $command\n";
        }
        $this->output .= "+ $command\n";
        if (empty($options['dry-run'])) {
            $fp = popen($command, "r");
            while ($line = fgets($fp, 1024)) {
                $this->output .= rtrim($line)."\n";
            }
            pclose($fp);
        }
        $this->ui->outputData($this->output, $_cmd);
        return true;
    }

    // }}}
    // {{{ doCvsDiff()

    function doCvsDiff($command, $options, $params)
    {
        $this->output = '';
        if (sizeof($params) < 1) {
            $help = $this->getHelp($command);
            return $this->raiseError("$command: missing parameter: $help[0]");
        }
        $obj = &$this->getPackageFile($this->config, $this->_debug);
        $info = $obj->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL);
        if (PEAR::isError($info)) {
            return $this->raiseError($info);
        }
        $err = $warn = array();
        if (!$info->validate()) {
            foreach ($info->getValidationWarnings() as $error) {
                if ($error['level'] == 'warning') {
                    $warn[] = $error['message'];
                } else {
                    $err[] = $error['message'];
                }
            }
        }
        if (!$this->_displayValidationResults($err, $warn, true)) {
            $this->ui->outputData($this->output, $command);
            return $this->raiseError('CVS diff failed');
        }
        $info1 = $info->getFilelist();
        $files = $info1;
        $cmd = "cvs";
        if (isset($options['quiet'])) {
            $cmd .= ' -q';
            unset($options['quiet']);
        }
        if (isset($options['reallyquiet'])) {
            $cmd .= ' -Q';
            unset($options['reallyquiet']);
        }
        if (isset($options['release'])) {
            $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $options['release']);
            $cvstag = "RELEASE_$cvsversion";
            $options['revision'] = $cvstag;
            unset($options['release']);
        }
        $execute = true;
        if (isset($options['dry-run'])) {
            $execute = false;
            unset($options['dry-run']);
        }
        $cmd .= ' diff';
        // the rest of the options are passed right on to "cvs diff"
        foreach ($options as $option => $optarg) {
            $arg = $short = false;
            if (isset($this->commands[$command]['options'][$option])) {
                $arg = $this->commands[$command]['options'][$option]['arg'];
                $short = $this->commands[$command]['options'][$option]['shortopt'];
            }
            $cmd .= $short ? " -$short" : " --$option";
            if ($arg && $optarg) {
                $cmd .= ($short ? '' : '=') . escapeshellarg($optarg);
            }
        }
        foreach ($files as $file) {
            $cmd .= ' ' . escapeshellarg($file['name']);
        }
        if ($this->config->get('verbose') > 1) {
            $this->output .= "+ $cmd\n";
        }
        if ($execute) {
            $fp = popen($cmd, "r");
            while ($line = fgets($fp, 1024)) {
                $this->output .= rtrim($line)."\n";
            }
            pclose($fp);
        }
        $this->ui->outputData($this->output, $command);
        return true;
    }

    // }}}
    // {{{ doPackageDependencies()

    function doPackageDependencies($command, $options, $params)
    {
        // $params[0] -> the PEAR package to list its information
        if (sizeof($params) != 1) {
            return $this->raiseError("bad parameter(s), try \"help $command\"");
        }
        $obj = &$this->getPackageFile($this->config, $this->_debug);
        $info = $obj->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL);
        if (PEAR::isError($info)) {
            return $this->raiseError($info);
        }
        $deps = $info->getDeps();
        if (is_array($deps)) {
            if ($info->getPackagexmlVersion() == '1.0') {
                $data = array(
                    'caption' => 'Dependencies for pear/' . $info->getPackage(),
                    'border' => true,
                    'headline' => array("Required?", "Type", "Name", "Relation", "Version"),
                    );

                foreach ($deps as $d) {
                    if (isset($d['optional'])) {
                        if ($d['optional'] == 'yes') {
                            $req = 'No';
                        } else {
                            $req = 'Yes';
                        }
                    } else {
                        $req = 'Yes';
                    }
                    if (isset($this->_deps_rel_trans[$d['rel']])) {
                        $rel = $this->_deps_rel_trans[$d['rel']];
                    } else {
                        $rel = $d['rel'];
                    }

                    if (isset($this->_deps_type_trans[$d['type']])) {
                        $type = ucfirst($this->_deps_type_trans[$d['type']]);
                    } else {
                        $type = $d['type'];
                    }

                    if (isset($d['name'])) {
                        $name = $d['name'];
                    } else {
                        $name = '';
                    }

                    if (isset($d['version'])) {
                        $version = $d['version'];
                    } else {
                        $version = '';
                    }

                    $data['data'][] = array($req, $type, $name, $rel, $version);
                }
            } else { // package.xml 2.0 dependencies display
                require_once 'PEAR/Dependency2.php';
                $deps = $info->getDependencies();
                $reg = &$this->config->getRegistry();
                if (is_array($deps)) {
                    $d = new PEAR_Dependency2($this->config, array(), '');
                    $data = array(
                        'caption' => 'Dependencies for ' . $info->getPackage(),
                        'border' => true,
                        'headline' => array("Required?", "Type", "Name", 'Versioning', 'Group'),
                        );
                    foreach ($deps as $type => $subd) {
                        $req = ($type == 'required') ? 'Yes' : 'No';
                        if ($type == 'group') {
                            $group = $subd['attribs']['name'];
                        } else {
                            $group = '';
                        }
                        if (!isset($subd[0])) {
                            $subd = array($subd);
                        }
                        foreach ($subd as $groupa) {
                            foreach ($groupa as $deptype => $depinfo) {
                                if ($deptype == 'attribs') {
                                    continue;
                                }
                                if ($deptype == 'pearinstaller') {
                                    $deptype = 'pear Installer';
                                }
                                if (!isset($depinfo[0])) {
                                    $depinfo = array($depinfo);
                                }
                                foreach ($depinfo as $inf) {
                                    $name = '';
                                    if (isset($inf['channel'])) {
                                        $alias = $reg->channelAlias($inf['channel']);
                                        if (!$alias) {
                                            $alias = '(channel?) ' .$inf['channel'];
                                        }
                                        $name = $alias . '/';
                                    }
                                    if (isset($inf['name'])) {
                                        $name .= $inf['name'];
                                    } elseif (isset($inf['pattern'])) {
                                        $name .= $inf['pattern'];
                                    } else {
                                        $name .= '';
                                    }
                                    if (isset($inf['uri'])) {
                                        $name .= ' [' . $inf['uri'] .  ']';
                                    }
                                    if (isset($inf['conflicts'])) {
                                        $ver = 'conflicts';
                                    } else {
                                        $ver = $d->_getExtraString($inf);
                                    }
                                    $data['data'][] = array($req, ucfirst($deptype), $name,
                                        $ver, $group);
                                }
                            }
                        }
                    }
                }
            }

            $this->ui->outputData($data, $command);
            return true;
        }

        // Fallback
        $this->ui->outputData("This package does not have any dependencies.", $command);
    }

    // }}}
    // {{{ doSign()

    function doSign($command, $options, $params)
    {
        require_once 'System.php';
        require_once 'Archive/Tar.php';
        // should move most of this code into PEAR_Packager
        // so it'll be easy to implement "pear package --sign"
        if (sizeof($params) != 1) {
            return $this->raiseError("bad parameter(s), try \"help $command\"");
        }
        if (!file_exists($params[0])) {
            return $this->raiseError("file does not exist: $params[0]");
        }
        $obj = $this->getPackageFile($this->config, $this->_debug);
        $info = $obj->fromTgzFile($params[0], PEAR_VALIDATE_NORMAL);
        if (PEAR::isError($info)) {
            return $this->raiseError($info);
        }
        $tar = new Archive_Tar($params[0]);
        $tmpdir = System::mktemp('-d pearsign');
        if (!$tar->extractList('package2.xml package.xml package.sig', $tmpdir)) {
            return $this->raiseError("failed to extract tar file");
        }
        if (file_exists("$tmpdir/package.sig")) {
            return $this->raiseError("package already signed");
        }
        $packagexml = 'package.xml';
        if (file_exists("$tmpdir/package2.xml")) {
            $packagexml = 'package2.xml';
        }
        if (file_exists("$tmpdir/package.sig")) {
            unlink("$tmpdir/package.sig");
        }
        if (!file_exists("$tmpdir/$packagexml")) {
            return $this->raiseError("Extracted file $tmpdir/$packagexml not found.");
        }
        $input = $this->ui->userDialog($command,
                                       array('GnuPG Passphrase'),
                                       array('password'));
        if (!isset($input[0])) {
            //use empty passphrase
            $input[0] = '';
        }

        $devnull = (isset($options['verbose'])) ? '' : ' 2>/dev/null';
        $gpg = popen("gpg --batch --passphrase-fd 0 --armor --detach-sign --output $tmpdir/package.sig $tmpdir/$packagexml" . $devnull, "w");
        if (!$gpg) {
            return $this->raiseError("gpg command failed");
        }
        fwrite($gpg, "$input[0]\n");
        if (pclose($gpg) || !file_exists("$tmpdir/package.sig")) {
            return $this->raiseError("gpg sign failed");
        }
        if (!$tar->addModify("$tmpdir/package.sig", '', $tmpdir)) {
            return $this->raiseError('failed adding signature to file');
        }

        $this->ui->outputData("Package signed.", $command);
        return true;
    }

    // }}}

    /**
     * For unit testing purposes
     */
    function &getInstaller(&$ui)
    {
        if (!class_exists('PEAR_Installer')) {
            require_once 'PEAR/Installer.php';
        }
        $a = &new PEAR_Installer($ui);
        return $a;
    }

    /**
     * For unit testing purposes
     */
    function &getCommandPackaging(&$ui, &$config)
    {
        if (!class_exists('PEAR_Command_Packaging')) {
            if ($fp = @fopen('PEAR/Command/Packaging.php', 'r', true)) {
                fclose($fp);
                include_once 'PEAR/Command/Packaging.php';
            }
        }

        if (class_exists('PEAR_Command_Packaging')) {
            $a = &new PEAR_Command_Packaging($ui, $config);
        } else {
            $a = null;
        }
        return $a;
    }

    // {{{ doMakeRPM()

    function doMakeRPM($command, $options, $params)
    {

        // Check to see if PEAR_Command_Packaging is installed, and
        // transparently switch to use the "make-rpm-spec" command from it
        // instead, if it does. Otherwise, continue to use the old version
        // of "makerpm" supplied with this package (PEAR).
        $packaging_cmd = $this->getCommandPackaging($this->ui, $this->config);
        if ($packaging_cmd !== null) {
            $this->ui->outputData('PEAR_Command_Packaging is installed; using '.
                'newer "make-rpm-spec" command instead');
            return $packaging_cmd->run('make-rpm-spec', $options, $params);
        } else {
            $this->ui->outputData('WARNING: "pear makerpm" is no longer available; an '.
              'improved version is available via "pear make-rpm-spec", which '.
              'is available by installing PEAR_Command_Packaging');
        }
        return true;
    }

    function doConvert($command, $options, $params)
    {
        $packagexml = isset($params[0]) ? $params[0] : 'package.xml';
        $newpackagexml = isset($params[1]) ? $params[1] : dirname($packagexml) .
            DIRECTORY_SEPARATOR . 'package2.xml';
        $pkg = &$this->getPackageFile($this->config, $this->_debug);
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        $pf = $pkg->fromPackageFile($packagexml, PEAR_VALIDATE_NORMAL);
        PEAR::staticPopErrorHandling();
        if (!PEAR::isError($pf)) {
            if (is_a($pf, 'PEAR_PackageFile_v2')) {
                $this->ui->outputData($packagexml . ' is already a package.xml version 2.0');
                return true;
            }
            $gen = &$pf->getDefaultGenerator();
            $newpf = &$gen->toV2();
            $newpf->setPackagefile($newpackagexml);
            $gen = &$newpf->getDefaultGenerator();
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            $state = (isset($options['flat']) ? PEAR_VALIDATE_PACKAGING : PEAR_VALIDATE_NORMAL);
            $saved = $gen->toPackageFile(dirname($newpackagexml), $state,
                basename($newpackagexml));
            PEAR::staticPopErrorHandling();
            if (PEAR::isError($saved)) {
                if (is_array($saved->getUserInfo())) {
                    foreach ($saved->getUserInfo() as $warning) {
                        $this->ui->outputData($warning['message']);
                    }
                }
                $this->ui->outputData($saved->getMessage());
                return true;
            }
            $this->ui->outputData('Wrote new version 2.0 package.xml to "' . $saved . '"');
            return true;
        } else {
            if (is_array($pf->getUserInfo())) {
                foreach ($pf->getUserInfo() as $warning) {
                    $this->ui->outputData($warning['message']);
                }
            }
            return $this->raiseError($pf);
        }
    }

    // }}}
}

?>
PEAR-1.7.2/PEAR/Command/Pickle.xml100644   1750   1750        2761 11014312742  11531 <commands version="1.0">
 <pickle>
  <summary>Build PECL Package</summary>
  <function>doPackage</function>
  <shortcut>pi</shortcut>
  <options>
   <nocompress>
    <shortopt>Z</shortopt>
    <doc>Do not gzip the package file</doc>
   </nocompress>
   <showname>
    <shortopt>n</shortopt>
    <doc>Print the name of the packaged file.</doc>
   </showname>
  </options>
  <doc>[descfile] [descfile2]
Creates a PECL package from its description file (usually called
package.xml).  If a second packagefile is passed in, then
the packager will check to make sure that one is a package.xml
version 1.0, and the other is a package.xml version 2.0.  The
package.xml version 1.0 will be saved as &quot;package.xml&quot; in the archive,
and the other as &quot;package2.xml&quot; in the archive&quot;

If no second file is passed in, and [descfile] is a package.xml 2.0,
an automatic conversion will be made to a package.xml 1.0.  Note that
only simple package.xml 2.0 will be converted.  package.xml 2.0 with:

 - dependency types other than required/optional PECL package/ext/php/pearinstaller
 - more than one extsrcrelease/zendextsrcrelease
 - zendextbinrelease, extbinrelease, phprelease, or bundle release type
 - dependency groups
 - ignore tags in release filelist
 - tasks other than replace
 - custom roles

will cause pickle to fail, and output an error message.  If your package2.xml
uses any of these features, you are best off using PEAR_PackageFileManager to
generate both package.xml.</doc>
 </pickle>
</commands>PEAR-1.7.2/PEAR/Command/Pickle.php100644   1750   1750       37420 11014312742  11540 <?php
/**
 * PEAR_Command_Pickle (pickle command)
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  2005-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Pickle.php,v 1.8 2008/01/29 03:21:01 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.1
 */

/**
 * base class
 */
require_once 'PEAR/Command/Common.php';

/**
 * PEAR commands for login/logout
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  2005-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.1
 */

class PEAR_Command_Pickle extends PEAR_Command_Common
{
    var $commands = array(
        'pickle' => array(
            'summary' => 'Build PECL Package',
            'function' => 'doPackage',
            'shortcut' => 'pi',
            'options' => array(
                'nocompress' => array(
                    'shortopt' => 'Z',
                    'doc' => 'Do not gzip the package file'
                    ),
                'showname' => array(
                    'shortopt' => 'n',
                    'doc' => 'Print the name of the packaged file.',
                    ),
                ),
            'doc' => '[descfile]
Creates a PECL package from its package2.xml file.

An automatic conversion will be made to a package.xml 1.0 and written out to
disk in the current directory as "package.xml".  Note that
only simple package.xml 2.0 will be converted.  package.xml 2.0 with:

 - dependency types other than required/optional PECL package/ext/php/pearinstaller
 - more than one extsrcrelease or zendextsrcrelease
 - zendextbinrelease, extbinrelease, phprelease, or bundle release type
 - dependency groups
 - ignore tags in release filelist
 - tasks other than replace
 - custom roles

will cause pickle to fail, and output an error message.  If your package2.xml
uses any of these features, you are best off using PEAR_PackageFileManager to
generate both package.xml.
'
            ),
        );

    /**
     * PEAR_Command_Package constructor.
     *
     * @access public
     */
    function PEAR_Command_Pickle(&$ui, &$config)
    {
        parent::PEAR_Command_Common($ui, $config);
    }


    /**
     * For unit-testing ease
     *
     * @return PEAR_Packager
     */
    function &getPackager()
    {
        if (!class_exists('PEAR_Packager')) {
            require_once 'PEAR/Packager.php';
        }
        $a = &new PEAR_Packager;
        return $a;
    }

    /**
     * For unit-testing ease
     *
     * @param PEAR_Config $config
     * @param bool $debug
     * @param string|null $tmpdir
     * @return PEAR_PackageFile
     */
    function &getPackageFile($config, $debug = false, $tmpdir = null)
    {
        if (!class_exists('PEAR_Common')) {
            require_once 'PEAR/Common.php';
        }
        if (!class_exists('PEAR_PackageFile')) {
            require_once 'PEAR/PackageFile.php';
        }
        $a = &new PEAR_PackageFile($config, $debug, $tmpdir);
        $common = new PEAR_Common;
        $common->ui = $this->ui;
        $a->setLogger($common);
        return $a;
    }

    function doPackage($command, $options, $params)
    {
        $this->output = '';
        $pkginfofile = isset($params[0]) ? $params[0] : 'package2.xml';
        $packager = &$this->getPackager();
        if (PEAR::isError($err = $this->_convertPackage($pkginfofile))) {
            return $err;
        }
        $compress = empty($options['nocompress']) ? true : false;
        $result = $packager->package($pkginfofile, $compress, 'package.xml');
        if (PEAR::isError($result)) {
            return $this->raiseError($result);
        }
        // Don't want output, only the package file name just created
        if (isset($options['showname'])) {
            $this->ui->outputData($result, $command);
        }
        return true;
    }

    function _convertPackage($packagexml)
    {
        $pkg = &$this->getPackageFile($this->config);
        $pf2 = &$pkg->fromPackageFile($packagexml, PEAR_VALIDATE_NORMAL);
        if (!is_a($pf2, 'PEAR_PackageFile_v2')) {
            return $this->raiseError('Cannot process "' .
                $packagexml . '", is not a package.xml 2.0');
        }
        require_once 'PEAR/PackageFile/v1.php';
        $pf = new PEAR_PackageFile_v1;
        $pf->setConfig($this->config);
        if ($pf2->getPackageType() != 'extsrc' && $pf2->getPackageType() != 'zendextsrc') {
            return $this->raiseError('Cannot safely convert "' . $packagexml .
            '", is not an extension source package.  Using a PEAR_PackageFileManager-based ' .
            'script is an option');
        }
        if (is_array($pf2->getUsesRole())) {
            return $this->raiseError('Cannot safely convert "' . $packagexml .
            '", contains custom roles.  Using a PEAR_PackageFileManager-based script or ' .
            'the convert command is an option');
        }
        if (is_array($pf2->getUsesTask())) {
            return $this->raiseError('Cannot safely convert "' . $packagexml .
            '", contains custom tasks.  Using a PEAR_PackageFileManager-based script or ' .
            'the convert command is an option');
        }
        $deps = $pf2->getDependencies();
        if (isset($deps['group'])) {
            return $this->raiseError('Cannot safely convert "' . $packagexml .
            '", contains dependency groups.  Using a PEAR_PackageFileManager-based script ' .
            'or the convert command is an option');
        }
        if (isset($deps['required']['subpackage']) ||
              isset($deps['optional']['subpackage'])) {
            return $this->raiseError('Cannot safely convert "' . $packagexml .
            '", contains subpackage dependencies.  Using a PEAR_PackageFileManager-based  '.
            'script is an option');
        }
        if (isset($deps['required']['os'])) {
            return $this->raiseError('Cannot safely convert "' . $packagexml .
            '", contains os dependencies.  Using a PEAR_PackageFileManager-based  '.
            'script is an option');
        }
        if (isset($deps['required']['arch'])) {
            return $this->raiseError('Cannot safely convert "' . $packagexml .
            '", contains arch dependencies.  Using a PEAR_PackageFileManager-based  '.
            'script is an option');
        }
        $pf->setPackage($pf2->getPackage());
        $pf->setSummary($pf2->getSummary());
        $pf->setDescription($pf2->getDescription());
        foreach ($pf2->getMaintainers() as $maintainer) {
            $pf->addMaintainer($maintainer['role'], $maintainer['handle'],
                $maintainer['name'], $maintainer['email']);
        }
        $pf->setVersion($pf2->getVersion());
        $pf->setDate($pf2->getDate());
        $pf->setLicense($pf2->getLicense());
        $pf->setState($pf2->getState());
        $pf->setNotes($pf2->getNotes());
        $pf->addPhpDep($deps['required']['php']['min'], 'ge');
        if (isset($deps['required']['php']['max'])) {
            $pf->addPhpDep($deps['required']['php']['max'], 'le');
        }
        if (isset($deps['required']['package'])) {
            if (!isset($deps['required']['package'][0])) {
                $deps['required']['package'] = array($deps['required']['package']);
            }
            foreach ($deps['required']['package'] as $dep) {
                if (!isset($dep['channel'])) {
                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
                    ' contains uri-based dependency on a package.  Using a ' .
                    'PEAR_PackageFileManager-based script is an option');
                }
                if ($dep['channel'] != 'pear.php.net' && $dep['channel'] != 'pecl.php.net') {
                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
                    ' contains dependency on a non-standard channel package.  Using a ' .
                    'PEAR_PackageFileManager-based script is an option');
                }
                if (isset($dep['conflicts'])) {
                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
                    ' contains conflicts dependency.  Using a ' .
                    'PEAR_PackageFileManager-based script is an option');
                }
                if (isset($dep['exclude'])) {
                    $this->ui->outputData('WARNING: exclude tags are ignored in conversion');
                }
                if (isset($dep['min'])) {
                    $pf->addPackageDep($dep['name'], $dep['min'], 'ge');
                }
                if (isset($dep['max'])) {
                    $pf->addPackageDep($dep['name'], $dep['max'], 'le');
                }
            }
        }
        if (isset($deps['required']['extension'])) {
            if (!isset($deps['required']['extension'][0])) {
                $deps['required']['extension'] = array($deps['required']['extension']);
            }
            foreach ($deps['required']['extension'] as $dep) {
                if (isset($dep['conflicts'])) {
                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
                    ' contains conflicts dependency.  Using a ' .
                    'PEAR_PackageFileManager-based script is an option');
                }
                if (isset($dep['exclude'])) {
                    $this->ui->outputData('WARNING: exclude tags are ignored in conversion');
                }
                if (isset($dep['min'])) {
                    $pf->addExtensionDep($dep['name'], $dep['min'], 'ge');
                }
                if (isset($dep['max'])) {
                    $pf->addExtensionDep($dep['name'], $dep['max'], 'le');
                }
            }
        }
        if (isset($deps['optional']['package'])) {
            if (!isset($deps['optional']['package'][0])) {
                $deps['optional']['package'] = array($deps['optional']['package']);
            }
            foreach ($deps['optional']['package'] as $dep) {
                if (!isset($dep['channel'])) {
                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
                    ' contains uri-based dependency on a package.  Using a ' .
                    'PEAR_PackageFileManager-based script is an option');
                }
                if ($dep['channel'] != 'pear.php.net' && $dep['channel'] != 'pecl.php.net') {
                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
                    ' contains dependency on a non-standard channel package.  Using a ' .
                    'PEAR_PackageFileManager-based script is an option');
                }
                if (isset($dep['exclude'])) {
                    $this->ui->outputData('WARNING: exclude tags are ignored in conversion');
                }
                if (isset($dep['min'])) {
                    $pf->addPackageDep($dep['name'], $dep['min'], 'ge', 'yes');
                }
                if (isset($dep['max'])) {
                    $pf->addPackageDep($dep['name'], $dep['max'], 'le', 'yes');
                }
            }
        }
        if (isset($deps['optional']['extension'])) {
            if (!isset($deps['optional']['extension'][0])) {
                $deps['optional']['extension'] = array($deps['optional']['extension']);
            }
            foreach ($deps['optional']['extension'] as $dep) {
                if (isset($dep['exclude'])) {
                    $this->ui->outputData('WARNING: exclude tags are ignored in conversion');
                }
                if (isset($dep['min'])) {
                    $pf->addExtensionDep($dep['name'], $dep['min'], 'ge', 'yes');
                }
                if (isset($dep['max'])) {
                    $pf->addExtensionDep($dep['name'], $dep['max'], 'le', 'yes');
                }
            }
        }
        $contents = $pf2->getContents();
        $release = $pf2->getReleases();
        if (isset($releases[0])) {
            return $this->raiseError('Cannot safely process "' . $packagexml . '" contains ' 
            . 'multiple extsrcrelease/zendextsrcrelease tags.  Using a PEAR_PackageFileManager-based script ' .
            'or the convert command is an option');
        }
        if ($configoptions = $pf2->getConfigureOptions()) {
            foreach ($configoptions as $option) {
                $pf->addConfigureOption($option['name'], $option['prompt'],
                    isset($option['default']) ? $option['default'] : false);
            }
        }
        if (isset($release['filelist']['ignore'])) {
            return $this->raiseError('Cannot safely process "' . $packagexml . '" contains ' 
            . 'ignore tags.  Using a PEAR_PackageFileManager-based script or the convert' .
            ' command is an option');
        }
        if (isset($release['filelist']['install']) &&
              !isset($release['filelist']['install'][0])) {
            $release['filelist']['install'] = array($release['filelist']['install']);
        }
        if (isset($contents['dir']['attribs']['baseinstalldir'])) {
            $baseinstalldir = $contents['dir']['attribs']['baseinstalldir'];
        } else {
            $baseinstalldir = false;
        }
        if (!isset($contents['dir']['file'][0])) {
            $contents['dir']['file'] = array($contents['dir']['file']);
        }
        foreach ($contents['dir']['file'] as $file) {
            if ($baseinstalldir && !isset($file['attribs']['baseinstalldir'])) {
                $file['attribs']['baseinstalldir'] = $baseinstalldir;
            }
            $processFile = $file;
            unset($processFile['attribs']);
            if (count($processFile)) {
                foreach ($processFile as $name => $task) {
                    if ($name != $pf2->getTasksNs() . ':replace') {
                        return $this->raiseError('Cannot safely process "' . $packagexml .
                        '" contains tasks other than replace.  Using a ' .
                        'PEAR_PackageFileManager-based script is an option.');
                    }
                    $file['attribs']['replace'][] = $task;
                }
            }
            if (!in_array($file['attribs']['role'], PEAR_Common::getFileRoles())) {
                return $this->raiseError('Cannot safely convert "' . $packagexml .
                '", contains custom roles.  Using a PEAR_PackageFileManager-based script ' .
                'or the convert command is an option');
            }
            if (isset($release['filelist']['install'])) {
                foreach ($release['filelist']['install'] as $installas) {
                    if ($installas['attribs']['name'] == $file['attribs']['name']) {
                        $file['attribs']['install-as'] = $installas['attribs']['as'];
                    }
                }
            }
            $pf->addFile('/', $file['attribs']['name'], $file['attribs']);
        }
        if ($pf2->getChangeLog()) {
            $this->ui->outputData('WARNING: changelog is not translated to package.xml ' .
                '1.0, use PEAR_PackageFileManager-based script if you need changelog-' .
                'translation for package.xml 1.0');
        }
        $gen = &$pf->getDefaultGenerator();
        $gen->toPackageFile('.');
    }
}

?>
PEAR-1.7.2/PEAR/Command/Registry.xml100644   1750   1750        3376 11014312742  12135 <commands version="1.0">
 <list>
  <summary>List Installed Packages In The Default Channel</summary>
  <function>doList</function>
  <shortcut>l</shortcut>
  <options>
   <channel>
    <shortopt>c</shortopt>
    <doc>list installed packages from this channel</doc>
    <arg>CHAN</arg>
   </channel>
   <allchannels>
    <shortopt>a</shortopt>
    <doc>list installed packages from all channels</doc>
   </allchannels>
   <channelinfo>
    <shortopt>i</shortopt>
    <doc>output fully channel-aware data, even on failure</doc>
   </channelinfo>
  </options>
  <doc>&lt;package&gt;
If invoked without parameters, this command lists the PEAR packages
installed in your php_dir ({config php_dir}).  With a parameter, it
lists the files in a package.
</doc>
 </list>
 <list-files>
  <summary>List Files In Installed Package</summary>
  <function>doFileList</function>
  <shortcut>fl</shortcut>
  <options />
  <doc>&lt;package&gt;
List the files in an installed package.
</doc>
 </list-files>
 <shell-test>
  <summary>Shell Script Test</summary>
  <function>doShellTest</function>
  <shortcut>st</shortcut>
  <options />
  <doc>&lt;package&gt; [[relation] version]
Tests if a package is installed in the system. Will exit(1) if it is not.
   &lt;relation&gt;   The version comparison operator. One of:
                &lt;, lt, &lt;=, le, &gt;, gt, &gt;=, ge, ==, =, eq, !=, &lt;&gt;, ne
   &lt;version&gt;    The version to compare with
</doc>
 </shell-test>
 <info>
  <summary>Display information about a package</summary>
  <function>doInfo</function>
  <shortcut>in</shortcut>
  <options />
  <doc>&lt;package&gt;
Displays information about a package. The package argument may be a
local package file, an URL to a package file, or the name of an
installed package.</doc>
 </info>
</commands>PEAR-1.7.2/PEAR/Command/Registry.php100644   1750   1750      133067 11014312742  12165 <?php
/**
 * PEAR_Command_Registry (list, list-files, shell-test, info commands)
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Registry.php,v 1.81 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * base class
 */
require_once 'PEAR/Command/Common.php';

/**
 * PEAR commands for registry manipulation
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */
class PEAR_Command_Registry extends PEAR_Command_Common
{
    // {{{ properties

    var $commands = array(
        'list' => array(
            'summary' => 'List Installed Packages In The Default Channel',
            'function' => 'doList',
            'shortcut' => 'l',
            'options' => array(
                'channel' => array(
                    'shortopt' => 'c',
                    'doc' => 'list installed packages from this channel',
                    'arg' => 'CHAN',
                    ),
                'allchannels' => array(
                    'shortopt' => 'a',
                    'doc' => 'list installed packages from all channels',
                    ),
                'channelinfo' => array(
                    'shortopt' => 'i',
                    'doc' => 'output fully channel-aware data, even on failure',
                    ),
                ),
            'doc' => '<package>
If invoked without parameters, this command lists the PEAR packages
installed in your php_dir ({config php_dir}).  With a parameter, it
lists the files in a package.
',
            ),
        'list-files' => array(
            'summary' => 'List Files In Installed Package',
            'function' => 'doFileList',
            'shortcut' => 'fl',
            'options' => array(),
            'doc' => '<package>
List the files in an installed package.
'
            ),
        'shell-test' => array(
            'summary' => 'Shell Script Test',
            'function' => 'doShellTest',
            'shortcut' => 'st',
            'options' => array(),
            'doc' => '<package> [[relation] version]
Tests if a package is installed in the system. Will exit(1) if it is not.
   <relation>   The version comparison operator. One of:
                <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne
   <version>    The version to compare with
'),
        'info' => array(
            'summary'  => 'Display information about a package',
            'function' => 'doInfo',
            'shortcut' => 'in',
            'options'  => array(),
            'doc'      => '<package>
Displays information about a package. The package argument may be a
local package file, an URL to a package file, or the name of an
installed package.'
            )
        );

    // }}}
    // {{{ constructor

    /**
     * PEAR_Command_Registry constructor.
     *
     * @access public
     */
    function PEAR_Command_Registry(&$ui, &$config)
    {
        parent::PEAR_Command_Common($ui, $config);
    }

    // }}}

    // {{{ doList()

    function _sortinfo($a, $b)
    {
        $apackage = isset($a['package']) ? $a['package'] : $a['name'];
        $bpackage = isset($b['package']) ? $b['package'] : $b['name'];
        return strcmp($apackage, $bpackage);
    }

    function doList($command, $options, $params)
    {
        $reg = &$this->config->getRegistry();
        $channelinfo = isset($options['channelinfo']);
        if (isset($options['allchannels']) && !$channelinfo) {
            return $this->doListAll($command, array(), $params);
        }
        if (isset($options['allchannels']) && $channelinfo) {
            // allchannels with $channelinfo
            unset($options['allchannels']);
            $channels = $reg->getChannels();
            $errors = array();
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            foreach ($channels as $channel) {
                $options['channel'] = $channel->getName();
                $ret = $this->doList($command, $options, $params);

                if (PEAR::isError($ret)) {
                    $errors[] = $ret;
                }
            }
            PEAR::staticPopErrorHandling();
            if (count($errors)) {
                // for now, only give first error
                return PEAR::raiseError($errors[0]);
            }
            return true;
        }

        if (count($params) == 1) {
            return $this->doFileList($command, $options, $params);
        }
        if (isset($options['channel'])) {
            if ($reg->channelExists($options['channel'])) {
                $channel = $reg->channelName($options['channel']);
            } else {
                return $this->raiseError('Channel "' . $options['channel'] .'" does not exist');
            }
        } else {
            $channel = $this->config->get('default_channel');
        }
        $installed = $reg->packageInfo(null, null, $channel);
        usort($installed, array(&$this, '_sortinfo'));

        $data = array(
            'caption' => 'Installed packages, channel ' .
                $channel . ':',
            'border' => true,
            'headline' => array('Package', 'Version', 'State'),
            'channel' => $channel,
            );
        if ($channelinfo) {
            $data['headline'] = array('Channel', 'Package', 'Version', 'State');
        }

        if (count($installed) && !isset($data['data'])) {
            $data['data'] = array();
        }

        foreach ($installed as $package) {
            $pobj = $reg->getPackage(isset($package['package']) ?
                                        $package['package'] : $package['name'], $channel);
            if ($channelinfo) {
                $packageinfo = array($pobj->getChannel(), $pobj->getPackage(), $pobj->getVersion(),
                                    $pobj->getState() ? $pobj->getState() : null);
            } else {
                $packageinfo = array($pobj->getPackage(), $pobj->getVersion(),
                                    $pobj->getState() ? $pobj->getState() : null);
            }
            $data['data'][] = $packageinfo;
        }
        if (count($installed) == 0) {
            if (!$channelinfo) {
                $data = '(no packages installed from channel ' . $channel . ')';
            } else {
                $data = array(
                    'caption' => 'Installed packages, channel ' .
                        $channel . ':',
                    'border' => true,
                    'channel' => $channel,
                    'data' => '(no packages installed)',
                );
            }
        }
        $this->ui->outputData($data, $command);
        return true;
    }
    
    function doListAll($command, $options, $params)
    {
        // This duplicate code is deprecated over
        // list --channelinfo, which gives identical
        // output for list and list --allchannels.
        $reg = &$this->config->getRegistry();
        $installed = $reg->packageInfo(null, null, null);
        foreach ($installed as $channel => $packages) {
            usort($packages, array($this, '_sortinfo'));
            $data = array(
                'caption' => 'Installed packages, channel ' . $channel . ':',
                'border' => true,
                'headline' => array('Package', 'Version', 'State'),
                'channel' => $channel
                );
            foreach ($packages as $package) {
                $pobj = $reg->getPackage(isset($package['package']) ?
                                            $package['package'] : $package['name'], $channel);
                $data['data'][] = array($pobj->getPackage(), $pobj->getVersion(),
                                        $pobj->getState() ? $pobj->getState() : null);
            }
            if (count($packages)==0) {
                $data = array(
                    'caption' => 'Installed packages, channel ' . $channel . ':',
                    'border' => true,
                    'data' => array(array('(no packages installed)')),
                    'channel' => $channel
                    );
            }
            $this->ui->outputData($data, $command);
        }
        return true;
    }
    
    function doFileList($command, $options, $params)
    {
        if (count($params) != 1) {
            return $this->raiseError('list-files expects 1 parameter');
        }
        $reg = &$this->config->getRegistry();
        $fp = false;
        if (!is_dir($params[0]) && (file_exists($params[0]) || $fp = @fopen($params[0],
              'r'))) {
            if ($fp) {
                fclose($fp);
            }
            if (!class_exists('PEAR_PackageFile')) {
                require_once 'PEAR/PackageFile.php';
            }
            $pkg = &new PEAR_PackageFile($this->config, $this->_debug);
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            $info = &$pkg->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL);
            PEAR::staticPopErrorHandling();
            $headings = array('Package File', 'Install Path');
            $installed = false;
        } else {
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
            PEAR::staticPopErrorHandling();
            if (PEAR::isError($parsed)) {
                return $this->raiseError($parsed);
            }
            $info = &$reg->getPackage($parsed['package'], $parsed['channel']);
            $headings = array('Type', 'Install Path');
            $installed = true;
        }
        if (PEAR::isError($info)) {
            return $this->raiseError($info);
        }
        if ($info === null) {
            return $this->raiseError("`$params[0]' not installed");
        }
        $list = ($info->getPackagexmlVersion() == '1.0' || $installed) ?
            $info->getFilelist() : $info->getContents();
        if ($installed) {
            $caption = 'Installed Files For ' . $params[0];
        } else {
            $caption = 'Contents of ' . basename($params[0]);
        }
        $data = array(
            'caption' => $caption,
            'border' => true,
            'headline' => $headings);
        if ($info->getPackagexmlVersion() == '1.0' || $installed) {
            foreach ($list as $file => $att) {
                if ($installed) {
                    if (empty($att['installed_as'])) {
                        continue;
                    }
                    $data['data'][] = array($att['role'], $att['installed_as']);
                } else {
                    if (isset($att['baseinstalldir']) && !in_array($att['role'],
                          array('test', 'data', 'doc'))) {
                        $dest = $att['baseinstalldir'] . DIRECTORY_SEPARATOR .
                            $file;
                    } else {
                        $dest = $file;
                    }
                    switch ($att['role']) {
                        case 'test':
                        case 'data':
                        case 'doc':
                            $role = $att['role'];
                            if ($role == 'test') {
                                $role .= 's';
                            }
                            $dest = $this->config->get($role . '_dir') . DIRECTORY_SEPARATOR .
                                $info->getPackage() . DIRECTORY_SEPARATOR . $dest;
                            break;
                        case 'php':
                        default:
                            $dest = $this->config->get('php_dir') . DIRECTORY_SEPARATOR .
                                $dest;
                    }
                    $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
                    $dest = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
                                                    array(DIRECTORY_SEPARATOR,
                                                          DIRECTORY_SEPARATOR,
                                                          DIRECTORY_SEPARATOR),
                                                    $dest);
                    $file = preg_replace('!/+!', '/', $file);
                    $data['data'][] = array($file, $dest);
                }
            }
        } else { // package.xml 2.0, not installed
            if (!isset($list['dir']['file'][0])) {
                $list['dir']['file'] = array($list['dir']['file']);
            }
            foreach ($list['dir']['file'] as $att) {
                $att = $att['attribs'];
                $file = $att['name'];
                $role = &PEAR_Installer_Role::factory($info, $att['role'], $this->config);
                $role->setup($this, $info, $att, $file);
                if (!$role->isInstallable()) {
                    $dest = '(not installable)';
                } else {
                    $dest = $role->processInstallation($info, $att, $file, '');
                    if (PEAR::isError($dest)) {
                        $dest = '(Unknown role "' . $att['role'] . ')';
                    } else {
                        list(,, $dest) = $dest;
                    }
                }
                $data['data'][] = array($file, $dest);
            }
        }
        $this->ui->outputData($data, $command);
        return true;
    }

    // }}}
    // {{{ doShellTest()

    function doShellTest($command, $options, $params)
    {
        if (count($params) < 1) {
            return PEAR::raiseError('ERROR, usage: pear shell-test packagename [[relation] version]');
        }
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        $reg = &$this->config->getRegistry();
        $info = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
        if (PEAR::isError($info)) {
            exit(1); // invalid package name
        }
        $package = $info['package'];
        $channel = $info['channel'];
        // "pear shell-test Foo"
        if (!$reg->packageExists($package, $channel)) {
            if ($channel == 'pecl.php.net') {
                if ($reg->packageExists($package, 'pear.php.net')) {
                    $channel = 'pear.php.net'; // magically change channels for extensions
                }
            }
        }
        if (sizeof($params) == 1) {
            if (!$reg->packageExists($package, $channel)) {
                exit(1);
            }
            // "pear shell-test Foo 1.0"
        } elseif (sizeof($params) == 2) {
            $v = $reg->packageInfo($package, 'version', $channel);
            if (!$v || !version_compare("$v", "{$params[1]}", "ge")) {
                exit(1);
            }
            // "pear shell-test Foo ge 1.0"
        } elseif (sizeof($params) == 3) {
            $v = $reg->packageInfo($package, 'version', $channel);
            if (!$v || !version_compare("$v", "{$params[2]}", $params[1])) {
                exit(1);
            }
        } else {
            PEAR::staticPopErrorHandling();
            $this->raiseError("$command: expects 1 to 3 parameters");
            exit(1);
        }
    }

    // }}}
    // {{{ doInfo

    function doInfo($command, $options, $params)
    {
        if (count($params) != 1) {
            return $this->raiseError('pear info expects 1 parameter');
        }
        $info = $fp = false;
        $reg = &$this->config->getRegistry();
        if ((file_exists($params[0]) && is_file($params[0]) && !is_dir($params[0])) || $fp = @fopen($params[0], 'r')) {
            if ($fp) {
                fclose($fp);
            }
            if (!class_exists('PEAR_PackageFile')) {
                require_once 'PEAR/PackageFile.php';
            }
            $pkg = &new PEAR_PackageFile($this->config, $this->_debug);
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            $obj = &$pkg->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL);
            PEAR::staticPopErrorHandling();
            if (PEAR::isError($obj)) {
                $uinfo = $obj->getUserInfo();
                if (is_array($uinfo)) {
                    foreach ($uinfo as $message) {
                        if (is_array($message)) {
                            $message = $message['message'];
                        }
                        $this->ui->outputData($message);
                    }
                }
                return $this->raiseError($obj);
            }
            if ($obj->getPackagexmlVersion() == '1.0') {
                $info = $obj->toArray();
            } else {
                return $this->_doInfo2($command, $options, $params, $obj, false);
            }
        } else {
            $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
            if (PEAR::isError($parsed)) {
                return $this->raiseError($parsed);
            }
            $package = $parsed['package'];
            $channel = $parsed['channel'];
            $info = $reg->packageInfo($package, null, $channel);
            if (isset($info['old'])) {
                $obj = $reg->getPackage($package, $channel);
                return $this->_doInfo2($command, $options, $params, $obj, true);
            }
        }
        if (PEAR::isError($info)) {
            return $info;
        }
        if (empty($info)) {
            $this->raiseError("No information found for `$params[0]'");
            return;
        }
        unset($info['filelist']);
        unset($info['dirtree']);
        unset($info['changelog']);
        if (isset($info['xsdversion'])) {
            $info['package.xml version'] = $info['xsdversion'];
            unset($info['xsdversion']);
        }
        if (isset($info['packagerversion'])) {
            $info['packaged with PEAR version'] = $info['packagerversion'];
            unset($info['packagerversion']);
        }
        $keys = array_keys($info);
        $longtext = array('description', 'summary');
        foreach ($keys as $key) {
            if (is_array($info[$key])) {
                switch ($key) {
                    case 'maintainers': {
                        $i = 0;
                        $mstr = '';
                        foreach ($info[$key] as $m) {
                            if ($i++ > 0) {
                                $mstr .= "\n";
                            }
                            $mstr .= $m['name'] . " <";
                            if (isset($m['email'])) {
                                $mstr .= $m['email'];
                            } else {
                                $mstr .= $m['handle'] . '@php.net';
                            }
                            $mstr .= "> ($m[role])";
                        }
                        $info[$key] = $mstr;
                        break;
                    }
                    case 'release_deps': {
                        $i = 0;
                        $dstr = '';
                        foreach ($info[$key] as $d) {
                            if (isset($this->_deps_rel_trans[$d['rel']])) {
                                $rel = $this->_deps_rel_trans[$d['rel']];
                            } else {
                                $rel = $d['rel'];
                            }
                            if (isset($this->_deps_type_trans[$d['type']])) {
                                $type = ucfirst($this->_deps_type_trans[$d['type']]);
                            } else {
                                $type = $d['type'];
                            }
                            if (isset($d['name'])) {
                                $name = $d['name'] . ' ';
                            } else {
                                $name = '';
                            }
                            if (isset($d['version'])) {
                                $version = $d['version'] . ' ';
                            } else {
                                $version = '';
                            }
                            if (isset($d['optional']) && $d['optional'] == 'yes') {
                                $optional = ' (optional)';
                            } else {
                                $optional = '';
                            }
                            $dstr .= "$type $name$rel $version$optional\n";
                        }
                        $info[$key] = $dstr;
                        break;
                    }
                    case 'provides' : {
                        $debug = $this->config->get('verbose');
                        if ($debug < 2) {
                            $pstr = 'Classes: ';
                        } else {
                            $pstr = '';
                        }
                        $i = 0;
                        foreach ($info[$key] as $p) {
                            if ($debug < 2 && $p['type'] != "class") {
                                continue;
                            }
                            // Only print classes when verbosity mode is < 2
                            if ($debug < 2) {
                                if ($i++ > 0) {
                                    $pstr .= ", ";
                                }
                                $pstr .= $p['name'];
                            } else {
                                if ($i++ > 0) {
                                    $pstr .= "\n";
                                }
                                $pstr .= ucfirst($p['type']) . " " . $p['name'];
                                if (isset($p['explicit']) && $p['explicit'] == 1) {
                                    $pstr .= " (explicit)";
                                }
                            }
                        }
                        $info[$key] = $pstr;
                        break;
                    }
                    case 'configure_options' : {
                        foreach ($info[$key] as $i => $p) {
                            $info[$key][$i] = array_map(null, array_keys($p), array_values($p));
                            $info[$key][$i] = array_map(create_function('$a',
                                'return join(" = ",$a);'), $info[$key][$i]);
                            $info[$key][$i] = implode(', ', $info[$key][$i]);
                        }
                        $info[$key] = implode("\n", $info[$key]);
                        break;
                    }
                    default: {
                        $info[$key] = implode(", ", $info[$key]);
                        break;
                    }
                }
            }
            if ($key == '_lastmodified') {
                $hdate = date('Y-m-d', $info[$key]);
                unset($info[$key]);
                $info['Last Modified'] = $hdate;
            } elseif ($key == '_lastversion') {
                $info['Previous Installed Version'] = $info[$key] ? $info[$key] : '- None -';
                unset($info[$key]);
            } else {
                $info[$key] = trim($info[$key]);
                if (in_array($key, $longtext)) {
                    $info[$key] = preg_replace('/  +/', ' ', $info[$key]);
                }
            }
        }
        $caption = 'About ' . $info['package'] . '-' . $info['version'];
        $data = array(
            'caption' => $caption,
            'border' => true);
        foreach ($info as $key => $value) {
            $key = ucwords(trim(str_replace('_', ' ', $key)));
            $data['data'][] = array($key, $value);
        }
        $data['raw'] = $info;

        $this->ui->outputData($data, 'package-info');
    }

    // }}}

    /**
     * @access private
     */
    function _doInfo2($command, $options, $params, &$obj, $installed)
    {
        $reg = &$this->config->getRegistry();
        $caption = 'About ' . $obj->getChannel() . '/' .$obj->getPackage() . '-' .
            $obj->getVersion();
        $data = array(
            'caption' => $caption,
            'border' => true);
        switch ($obj->getPackageType()) {
            case 'php' :
                $release = 'PEAR-style PHP-based Package';
            break;
            case 'extsrc' :
                $release = 'PECL-style PHP extension (source code)';
            break;
            case 'zendextsrc' :
                $release = 'PECL-style Zend extension (source code)';
            break;
            case 'extbin' :
                $release = 'PECL-style PHP extension (binary)';
            break;
            case 'zendextbin' :
                $release = 'PECL-style Zend extension (binary)';
            break;
            case 'bundle' :
                $release = 'Package bundle (collection of packages)';
            break;
        }
        $extends = $obj->getExtends();
        $extends = $extends ?
            $obj->getPackage() . ' (extends ' . $extends . ')' : $obj->getPackage();
        if ($src = $obj->getSourcePackage()) {
            $extends .= ' (source package ' . $src['channel'] . '/' . $src['package'] . ')';
        }
        $info = array(
            'Release Type' => $release,
            'Name' => $extends,
            'Channel' => $obj->getChannel(),
            'Summary' => preg_replace('/  +/', ' ', $obj->getSummary()),
            'Description' => preg_replace('/  +/', ' ', $obj->getDescription()),
            );
        $info['Maintainers'] = '';
        foreach (array('lead', 'developer', 'contributor', 'helper') as $role) {
            $leads = $obj->{"get{$role}s"}();
            if (!$leads) {
                continue;
            }
            if (isset($leads['active'])) {
                $leads = array($leads);
            }
            foreach ($leads as $lead) {
                if (!empty($info['Maintainers'])) {
                    $info['Maintainers'] .= "\n";
                }
                $info['Maintainers'] .= $lead['name'] . ' <';
                $info['Maintainers'] .= $lead['email'] . "> ($role)";
            }
        }
        $info['Release Date'] = $obj->getDate();
        if ($time = $obj->getTime()) {
            $info['Release Date'] .= ' ' . $time;
        }
        $info['Release Version'] = $obj->getVersion() . ' (' . $obj->getState() . ')';
        $info['API Version'] = $obj->getVersion('api') . ' (' . $obj->getState('api') . ')';
        $info['License'] = $obj->getLicense();
        $uri = $obj->getLicenseLocation();
        if ($uri) {
            if (isset($uri['uri'])) {
                $info['License'] .= ' (' . $uri['uri'] . ')';
            } else {
                $extra = $obj->getInstalledLocation($info['filesource']);
                if ($extra) {
                    $info['License'] .= ' (' . $uri['filesource'] . ')';
                }
            }
        }
        $info['Release Notes'] = $obj->getNotes();
        if ($compat = $obj->getCompatible()) {
            if (!isset($compat[0])) {
                $compat = array($compat);
            }
            $info['Compatible with'] = '';
            foreach ($compat as $package) {
                $info['Compatible with'] .= $package['channel'] . '/' . $package['name'] .
                    "\nVersions >= " . $package['min'] . ', <= ' . $package['max'];
                if (isset($package['exclude'])) {
                    if (is_array($package['exclude'])) {
                        $package['exclude'] = implode(', ', $package['exclude']);
                    }
                    if (!isset($info['Not Compatible with'])) {
                        $info['Not Compatible with'] = '';
                    } else {
                        $info['Not Compatible with'] .= "\n";
                    }
                    $info['Not Compatible with'] .= $package['channel'] . '/' .
                        $package['name'] . "\nVersions " . $package['exclude'];
                }
            }
        }
        $usesrole = $obj->getUsesrole();
        if ($usesrole) {
            if (!isset($usesrole[0])) {
                $usesrole = array($usesrole);
            }
            foreach ($usesrole as $roledata) {
                if (isset($info['Uses Custom Roles'])) {
                    $info['Uses Custom Roles'] .= "\n";
                } else {
                    $info['Uses Custom Roles'] = '';
                }
                if (isset($roledata['package'])) {
                    $rolepackage = $reg->parsedPackageNameToString($roledata, true);
                } else {
                    $rolepackage = $roledata['uri'];
                }
                $info['Uses Custom Roles'] .= $roledata['role'] . ' (' . $rolepackage . ')';
            }
        }
        $usestask = $obj->getUsestask();
        if ($usestask) {
            if (!isset($usestask[0])) {
                $usestask = array($usestask);
            }
            foreach ($usestask as $taskdata) {
                if (isset($info['Uses Custom Tasks'])) {
                    $info['Uses Custom Tasks'] .= "\n";
                } else {
                    $info['Uses Custom Tasks'] = '';
                }
                if (isset($taskdata['package'])) {
                    $taskpackage = $reg->parsedPackageNameToString($taskdata, true);
                } else {
                    $taskpackage = $taskdata['uri'];
                }
                $info['Uses Custom Tasks'] .= $taskdata['task'] . ' (' . $taskpackage . ')';
            }
        }
        $deps = $obj->getDependencies();
        $info['Required Dependencies'] = 'PHP version ' . $deps['required']['php']['min'];
        if (isset($deps['required']['php']['max'])) {
            $info['Required Dependencies'] .= '-' . $deps['required']['php']['max'] . "\n";
        } else {
            $info['Required Dependencies'] .= "\n";
        }
        if (isset($deps['required']['php']['exclude'])) {
            if (!isset($info['Not Compatible with'])) {
                $info['Not Compatible with'] = '';
            } else {
                $info['Not Compatible with'] .= "\n";
            }
            if (is_array($deps['required']['php']['exclude'])) {
                $deps['required']['php']['exclude'] =
                    implode(', ', $deps['required']['php']['exclude']);
            }
            $info['Not Compatible with'] .= "PHP versions\n  " .
                $deps['required']['php']['exclude'];
        }
        $info['Required Dependencies'] .= 'PEAR installer version';
        if (isset($deps['required']['pearinstaller']['max'])) {
            $info['Required Dependencies'] .= 's ' .
                $deps['required']['pearinstaller']['min'] . '-' .
                $deps['required']['pearinstaller']['max'];
        } else {
            $info['Required Dependencies'] .= ' ' .
                $deps['required']['pearinstaller']['min'] . ' or newer';
        }
        if (isset($deps['required']['pearinstaller']['exclude'])) {
            if (!isset($info['Not Compatible with'])) {
                $info['Not Compatible with'] = '';
            } else {
                $info['Not Compatible with'] .= "\n";
            }
            if (is_array($deps['required']['pearinstaller']['exclude'])) {
                $deps['required']['pearinstaller']['exclude'] =
                    implode(', ', $deps['required']['pearinstaller']['exclude']);
            }
            $info['Not Compatible with'] .= "PEAR installer\n  Versions " .
                $deps['required']['pearinstaller']['exclude'];
        }
        foreach (array('Package', 'Extension') as $type) {
            $index = strtolower($type);
            if (isset($deps['required'][$index])) {
                if (isset($deps['required'][$index]['name'])) {
                    $deps['required'][$index] = array($deps['required'][$index]);
                }
                foreach ($deps['required'][$index] as $package) {
                    if (isset($package['conflicts'])) {
                        $infoindex = 'Not Compatible with';
                        if (!isset($info['Not Compatible with'])) {
                            $info['Not Compatible with'] = '';
                        } else {
                            $info['Not Compatible with'] .= "\n";
                        }
                    } else {
                        $infoindex = 'Required Dependencies';
                        $info[$infoindex] .= "\n";
                    }
                    if ($index == 'extension') {
                        $name = $package['name'];
                    } else {
                        if (isset($package['channel'])) {
                            $name = $package['channel'] . '/' . $package['name'];
                        } else {
                            $name = '__uri/' . $package['name'] . ' (static URI)';
                        }
                    }
                    $info[$infoindex] .= "$type $name";
                    if (isset($package['uri'])) {
                        $info[$infoindex] .= "\n  Download URI: $package[uri]";
                        continue;
                    }
                    if (isset($package['max']) && isset($package['min'])) {
                        $info[$infoindex] .= " \n  Versions " .
                            $package['min'] . '-' . $package['max'];
                    } elseif (isset($package['min'])) {
                        $info[$infoindex] .= " \n  Version " .
                            $package['min'] . ' or newer';
                    } elseif (isset($package['max'])) {
                        $info[$infoindex] .= " \n  Version " .
                            $package['max'] . ' or older';
                    }
                    if (isset($package['recommended'])) {
                        $info[$infoindex] .= "\n  Recommended version: $package[recommended]";
                    }
                    if (isset($package['exclude'])) {
                        if (!isset($info['Not Compatible with'])) {
                            $info['Not Compatible with'] = '';
                        } else {
                            $info['Not Compatible with'] .= "\n";
                        }
                        if (is_array($package['exclude'])) {
                            $package['exclude'] = implode(', ', $package['exclude']);
                        }
                        $package['package'] = $package['name']; // for parsedPackageNameToString
                         if (isset($package['conflicts'])) {
                            $info['Not Compatible with'] .= '=> except ';
                        }
                       $info['Not Compatible with'] .= 'Package ' .
                            $reg->parsedPackageNameToString($package, true);
                        $info['Not Compatible with'] .= "\n  Versions " . $package['exclude'];
                    }
                }
            }
        }
        if (isset($deps['required']['os'])) {
            if (isset($deps['required']['os']['name'])) {
                $dep['required']['os']['name'] = array($dep['required']['os']['name']);
            }
            foreach ($dep['required']['os'] as $os) {
                if (isset($os['conflicts']) && $os['conflicts'] == 'yes') {
                    if (!isset($info['Not Compatible with'])) {
                        $info['Not Compatible with'] = '';
                    } else {
                        $info['Not Compatible with'] .= "\n";
                    }
                    $info['Not Compatible with'] .= "$os[name] Operating System";
                } else {
                    $info['Required Dependencies'] .= "\n";
                    $info['Required Dependencies'] .= "$os[name] Operating System";
                }
            }
        }
        if (isset($deps['required']['arch'])) {
            if (isset($deps['required']['arch']['pattern'])) {
                $dep['required']['arch']['pattern'] = array($dep['required']['os']['pattern']);
            }
            foreach ($dep['required']['arch'] as $os) {
                if (isset($os['conflicts']) && $os['conflicts'] == 'yes') {
                    if (!isset($info['Not Compatible with'])) {
                        $info['Not Compatible with'] = '';
                    } else {
                        $info['Not Compatible with'] .= "\n";
                    }
                    $info['Not Compatible with'] .= "OS/Arch matching pattern '/$os[pattern]/'";
                } else {
                    $info['Required Dependencies'] .= "\n";
                    $info['Required Dependencies'] .= "OS/Arch matching pattern '/$os[pattern]/'";
                }
            }
        }
        if (isset($deps['optional'])) {
            foreach (array('Package', 'Extension') as $type) {
                $index = strtolower($type);
                if (isset($deps['optional'][$index])) {
                    if (isset($deps['optional'][$index]['name'])) {
                        $deps['optional'][$index] = array($deps['optional'][$index]);
                    }
                    foreach ($deps['optional'][$index] as $package) {
                        if (isset($package['conflicts']) && $package['conflicts'] == 'yes') {
                            $infoindex = 'Not Compatible with';
                            if (!isset($info['Not Compatible with'])) {
                                $info['Not Compatible with'] = '';
                            } else {
                                $info['Not Compatible with'] .= "\n";
                            }
                        } else {
                            $infoindex = 'Optional Dependencies';
                            if (!isset($info['Optional Dependencies'])) {
                                $info['Optional Dependencies'] = '';
                            } else {
                                $info['Optional Dependencies'] .= "\n";
                            }
                        }
                        if ($index == 'extension') {
                            $name = $package['name'];
                        } else {
                            if (isset($package['channel'])) {
                                $name = $package['channel'] . '/' . $package['name'];
                            } else {
                                $name = '__uri/' . $package['name'] . ' (static URI)';
                            }
                        }
                        $info[$infoindex] .= "$type $name";
                        if (isset($package['uri'])) {
                            $info[$infoindex] .= "\n  Download URI: $package[uri]";
                            continue;
                        }
                        if ($infoindex == 'Not Compatible with') {
                            // conflicts is only used to say that all versions conflict
                            continue;
                        }
                        if (isset($package['max']) && isset($package['min'])) {
                            $info[$infoindex] .= " \n  Versions " .
                                $package['min'] . '-' . $package['max'];
                        } elseif (isset($package['min'])) {
                            $info[$infoindex] .= " \n  Version " .
                                $package['min'] . ' or newer';
                        } elseif (isset($package['max'])) {
                            $info[$infoindex] .= " \n  Version " .
                                $package['min'] . ' or older';
                        }
                        if (isset($package['recommended'])) {
                            $info[$infoindex] .= "\n  Recommended version: $package[recommended]";
                        }
                        if (isset($package['exclude'])) {
                            if (!isset($info['Not Compatible with'])) {
                                $info['Not Compatible with'] = '';
                            } else {
                                $info['Not Compatible with'] .= "\n";
                            }
                            if (is_array($package['exclude'])) {
                                $package['exclude'] = implode(', ', $package['exclude']);
                            }
                            $info['Not Compatible with'] .= "Package $package\n  Versions " .
                                $package['exclude'];
                        }
                    }
                }
            }
        }
        if (isset($deps['group'])) {
            if (!isset($deps['group'][0])) {
                $deps['group'] = array($deps['group']);
            }
            foreach ($deps['group'] as $group) {
                $info['Dependency Group ' . $group['attribs']['name']] = $group['attribs']['hint'];
                $groupindex = $group['attribs']['name'] . ' Contents';
                $info[$groupindex] = '';
                foreach (array('Package', 'Extension') as $type) {
                    $index = strtolower($type);
                    if (isset($group[$index])) {
                        if (isset($group[$index]['name'])) {
                            $group[$index] = array($group[$index]);
                        }
                        foreach ($group[$index] as $package) {
                            if (!empty($info[$groupindex])) {
                                $info[$groupindex] .= "\n";
                            }
                            if ($index == 'extension') {
                                $name = $package['name'];
                            } else {
                                if (isset($package['channel'])) {
                                    $name = $package['channel'] . '/' . $package['name'];
                                } else {
                                    $name = '__uri/' . $package['name'] . ' (static URI)';
                                }
                            }
                            if (isset($package['uri'])) {
                                if (isset($package['conflicts']) && $package['conflicts'] == 'yes') {
                                    $info[$groupindex] .= "Not Compatible with $type $name";
                                } else {
                                    $info[$groupindex] .= "$type $name";
                                }
                                $info[$groupindex] .= "\n  Download URI: $package[uri]";
                                continue;
                            }
                            if (isset($package['conflicts']) && $package['conflicts'] == 'yes') {
                                $info[$groupindex] .= "Not Compatible with $type $name";
                                continue;
                            }
                            $info[$groupindex] .= "$type $name";
                            if (isset($package['max']) && isset($package['min'])) {
                                $info[$groupindex] .= " \n  Versions " .
                                    $package['min'] . '-' . $package['max'];
                            } elseif (isset($package['min'])) {
                                $info[$groupindex] .= " \n  Version " .
                                    $package['min'] . ' or newer';
                            } elseif (isset($package['max'])) {
                                $info[$groupindex] .= " \n  Version " .
                                    $package['min'] . ' or older';
                            }
                            if (isset($package['recommended'])) {
                                $info[$groupindex] .= "\n  Recommended version: $package[recommended]";
                            }
                            if (isset($package['exclude'])) {
                                if (!isset($info['Not Compatible with'])) {
                                    $info['Not Compatible with'] = '';
                                } else {
                                    $info[$groupindex] .= "Not Compatible with\n";
                                }
                                if (is_array($package['exclude'])) {
                                    $package['exclude'] = implode(', ', $package['exclude']);
                                }
                                $info[$groupindex] .= "  Package $package\n  Versions " .
                                    $package['exclude'];
                            }
                        }
                    }
                }
            }
        }
        if ($obj->getPackageType() == 'bundle') {
            $info['Bundled Packages'] = '';
            foreach ($obj->getBundledPackages() as $package) {
                if (!empty($info['Bundled Packages'])) {
                    $info['Bundled Packages'] .= "\n";
                }
                if (isset($package['uri'])) {
                    $info['Bundled Packages'] .= '__uri/' . $package['name'];
                    $info['Bundled Packages'] .= "\n  (URI: $package[uri]";
                } else {
                    $info['Bundled Packages'] .= $package['channel'] . '/' . $package['name'];
                }
            }
        }
        $info['package.xml version'] = '2.0';
        if ($installed) {
            if ($obj->getLastModified()) {
                $info['Last Modified'] = date('Y-m-d H:i', $obj->getLastModified());
            }
            $v = $obj->getLastInstalledVersion();
            $info['Previous Installed Version'] = $v ? $v : '- None -';
        }
        foreach ($info as $key => $value) {
            $data['data'][] = array($key, $value);
        }
        $data['raw'] = $obj->getArray(); // no validation needed

        $this->ui->outputData($data, 'package-info');
    }
}

?>
PEAR-1.7.2/PEAR/Command/Remote.xml100644   1750   1750        6344 11014312742  11556 <commands version="1.0">
 <remote-info>
  <summary>Information About Remote Packages</summary>
  <function>doRemoteInfo</function>
  <shortcut>ri</shortcut>
  <options />
  <doc>&lt;package&gt;
Get details on a package from the server.</doc>
 </remote-info>
 <list-upgrades>
  <summary>List Available Upgrades</summary>
  <function>doListUpgrades</function>
  <shortcut>lu</shortcut>
  <options />
  <doc>[preferred_state]
List releases on the server of packages you have installed where
a newer version is available with the same release state (stable etc.)
or the state passed as the second parameter.</doc>
 </list-upgrades>
 <remote-list>
  <summary>List Remote Packages</summary>
  <function>doRemoteList</function>
  <shortcut>rl</shortcut>
  <options>
   <channel>
    <shortopt>c</shortopt>
    <doc>specify a channel other than the default channel</doc>
    <arg>CHAN</arg>
   </channel>
   <channelinfo>
    <shortopt>i</shortopt>
    <doc>output fully channel-aware data, even on failure</doc>
   </channelinfo>
  </options>
  <doc>
Lists the packages available on the configured server along with the
latest stable release of each package.</doc>
 </remote-list>
 <search>
  <summary>Search remote package database</summary>
  <function>doSearch</function>
  <shortcut>sp</shortcut>
  <options>
   <channel>
    <shortopt>c</shortopt>
    <doc>specify a channel other than the default channel</doc>
    <arg>CHAN</arg>
   </channel>
   <allchannels>
    <shortopt>a</shortopt>
    <doc>search packages from all known channels</doc>
   </allchannels>
   <channelinfo>
    <shortopt>i</shortopt>
    <doc>output fully channel-aware data, even on failure</doc>
   </channelinfo>
  </options>
  <doc>[packagename] [packageinfo]
Lists all packages which match the search parameters.  The first
parameter is a fragment of a packagename.  The default channel
will be used unless explicitly overridden.  The second parameter
will be used to match any portion of the summary/description</doc>
 </search>
 <list-all>
  <summary>List All Packages</summary>
  <function>doListAll</function>
  <shortcut>la</shortcut>
  <options>
   <channel>
    <shortopt>c</shortopt>
    <doc>specify a channel other than the default channel</doc>
    <arg>CHAN</arg>
   </channel>
   <channelinfo>
    <shortopt>i</shortopt>
    <doc>output fully channel-aware data, even on failure</doc>
   </channelinfo>
  </options>
  <doc>
Lists the packages available on the configured server along with the
latest stable release of each package.</doc>
 </list-all>
 <download>
  <summary>Download Package</summary>
  <function>doDownload</function>
  <shortcut>d</shortcut>
  <options>
   <nocompress>
    <shortopt>Z</shortopt>
    <doc>download an uncompressed (.tar) file</doc>
   </nocompress>
  </options>
  <doc>&lt;package&gt;...
Download package tarballs.  The files will be named as suggested by the
server, for example if you download the DB package and the latest stable
version of DB is 1.6.5, the downloaded file will be DB-1.6.5.tgz.</doc>
 </download>
 <clear-cache>
  <summary>Clear Web Services Cache</summary>
  <function>doClearCache</function>
  <shortcut>cc</shortcut>
  <options />
  <doc>
Clear the XML-RPC/REST cache.  See also the cache_ttl configuration
parameter.
</doc>
 </clear-cache>
</commands>PEAR-1.7.2/PEAR/Command/Remote.php100644   1750   1750       76234 11014312742  11572 <?php
/**
 * PEAR_Command_Remote (remote-info, list-upgrades, remote-list, search, list-all, download,
 * clear-cache commands)
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Remote.php,v 1.107 2008/04/11 01:16:40 dufuz Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * base class
 */
require_once 'PEAR/Command/Common.php';
require_once 'PEAR/REST.php';

/**
 * PEAR commands for remote server querying
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */
class PEAR_Command_Remote extends PEAR_Command_Common
{
    // {{{ command definitions

    var $commands = array(
        'remote-info' => array(
            'summary' => 'Information About Remote Packages',
            'function' => 'doRemoteInfo',
            'shortcut' => 'ri',
            'options' => array(),
            'doc' => '<package>
Get details on a package from the server.',
            ),
        'list-upgrades' => array(
            'summary' => 'List Available Upgrades',
            'function' => 'doListUpgrades',
            'shortcut' => 'lu',
            'options' => array(
                'channelinfo' => array(
                    'shortopt' => 'i',
                    'doc' => 'output fully channel-aware data, even on failure',
                    ),
            ),
            'doc' => '[preferred_state]
List releases on the server of packages you have installed where
a newer version is available with the same release state (stable etc.)
or the state passed as the second parameter.'
            ),
        'remote-list' => array(
            'summary' => 'List Remote Packages',
            'function' => 'doRemoteList',
            'shortcut' => 'rl',
            'options' => array(
                'channel' =>
                    array(
                    'shortopt' => 'c',
                    'doc' => 'specify a channel other than the default channel',
                    'arg' => 'CHAN',
                    )
                ),
            'doc' => '
Lists the packages available on the configured server along with the
latest stable release of each package.',
            ),
        'search' => array(
            'summary' => 'Search remote package database',
            'function' => 'doSearch',
            'shortcut' => 'sp',
            'options' => array(
                'channel' =>
                    array(
                    'shortopt' => 'c',
                    'doc' => 'specify a channel other than the default channel',
                    'arg' => 'CHAN',
                    ),
                'allchannels' => array(
                    'shortopt' => 'a',
                    'doc' => 'search packages from all known channels',
                    ),
                'channelinfo' => array(
                    'shortopt' => 'i',
                    'doc' => 'output fully channel-aware data, even on failure',
                    ),
                ),
            'doc' => '[packagename] [packageinfo]
Lists all packages which match the search parameters.  The first
parameter is a fragment of a packagename.  The default channel
will be used unless explicitly overridden.  The second parameter
will be used to match any portion of the summary/description',
            ),
        'list-all' => array(
            'summary' => 'List All Packages',
            'function' => 'doListAll',
            'shortcut' => 'la',
            'options' => array(
                'channel' =>
                    array(
                    'shortopt' => 'c',
                    'doc' => 'specify a channel other than the default channel',
                    'arg' => 'CHAN',
                    ),
                'channelinfo' => array(
                    'shortopt' => 'i',
                    'doc' => 'output fully channel-aware data, even on failure',
                    ),
                ),
            'doc' => '
Lists the packages available on the configured server along with the
latest stable release of each package.',
            ),
        'download' => array(
            'summary' => 'Download Package',
            'function' => 'doDownload',
            'shortcut' => 'd',
            'options' => array(
                'nocompress' => array(
                    'shortopt' => 'Z',
                    'doc' => 'download an uncompressed (.tar) file',
                    ),
                ),
            'doc' => '<package>...
Download package tarballs.  The files will be named as suggested by the
server, for example if you download the DB package and the latest stable
version of DB is 1.6.5, the downloaded file will be DB-1.6.5.tgz.',
            ),
        'clear-cache' => array(
            'summary' => 'Clear Web Services Cache',
            'function' => 'doClearCache',
            'shortcut' => 'cc',
            'options' => array(),
            'doc' => '
Clear the XML-RPC/REST cache.  See also the cache_ttl configuration
parameter.
',
            ),
        );

    // }}}
    // {{{ constructor

    /**
     * PEAR_Command_Remote constructor.
     *
     * @access public
     */
    function PEAR_Command_Remote(&$ui, &$config)
    {
        parent::PEAR_Command_Common($ui, $config);
    }

    // }}}

    function _checkChannelForStatus($channel, $chan)
    {
        if (PEAR::isError($chan)) {
            $this->raiseError($chan);
        }
        if (!is_a($chan, 'PEAR_ChannelFile')) {
            return $this->raiseError('Internal corruption error: invalid channel "' .
                $channel . '"');
        }
        $rest = new PEAR_REST($this->config);
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        $mirror = $this->config->get('preferred_mirror', null,
                                     $channel);
        $a = $rest->downloadHttp('http://' . $channel .
            '/channel.xml', $chan->lastModified());
        PEAR::staticPopErrorHandling();
        if (!PEAR::isError($a) && $a) {
            $this->ui->outputData('WARNING: channel "' . $channel . '" has ' .
                'updated its protocols, use "channel-update ' . $channel .
                '" to update');
        }
    }

    // {{{ doRemoteInfo()

    function doRemoteInfo($command, $options, $params)
    {
        if (sizeof($params) != 1) {
            return $this->raiseError("$command expects one param: the remote package name");
        }
        $savechannel = $channel = $this->config->get('default_channel');
        $reg = &$this->config->getRegistry();
        $package = $params[0];
        $parsed = $reg->parsePackageName($package, $channel);
        if (PEAR::isError($parsed)) {
            return $this->raiseError('Invalid package name "' . $package . '"');
        }

        $channel = $parsed['channel'];
        $this->config->set('default_channel', $channel);
        $chan = $reg->getChannel($channel);
        if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
            return $e;
        }
        if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
              $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
            $rest = &$this->config->getREST('1.0', array());
            $info = $rest->packageInfo($base, $parsed['package'], $channel);
        } else {
            $r = &$this->config->getRemote();
            $info = $r->call('package.info', $parsed['package']);
        }
        if (PEAR::isError($info)) {
            $this->config->set('default_channel', $savechannel);
            return $this->raiseError($info);
        }
        if (!isset($info['name'])) {
            return $this->raiseError('No remote package "' . $package . '" was found');
        }

        $installed = $reg->packageInfo($info['name'], null, $channel);
        $info['installed'] = $installed['version'] ? $installed['version'] : '- no -';
        if (is_array($info['installed'])) {
            $info['installed'] = $info['installed']['release'];
        }

        $this->ui->outputData($info, $command);
        $this->config->set('default_channel', $savechannel);

        return true;
    }

    // }}}
    // {{{ doRemoteList()

    function doRemoteList($command, $options, $params)
    {
        $savechannel = $channel = $this->config->get('default_channel');
        $reg = &$this->config->getRegistry();
        if (isset($options['channel'])) {
            $channel = $options['channel'];
            if ($reg->channelExists($channel)) {
                $this->config->set('default_channel', $channel);
            } else {
                return $this->raiseError('Channel "' . $channel . '" does not exist');
            }
        }
        $chan = $reg->getChannel($channel);
        if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
            return $e;
        }
        $list_options = false;
        if ($this->config->get('preferred_state') == 'stable') {
            $list_options = true;
        }
        if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
              $base = $chan->getBaseURL('REST1.1', $this->config->get('preferred_mirror'))) {
            // use faster list-all if available
            $rest = &$this->config->getREST('1.1', array());
            $available = $rest->listAll($base, $list_options, true, false, false, $chan->getName());
        } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) &&
              $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
            $rest = &$this->config->getREST('1.0', array());
            $available = $rest->listAll($base, $list_options, true, false, false, $chan->getName());
        } else {
            $r = &$this->config->getRemote();
            if ($channel == 'pear.php.net') {
                // hack because of poor pearweb design
                $available = $r->call('package.listAll', true, $list_options, false);
            } else {
                $available = $r->call('package.listAll', true, $list_options);
            }
        }
        if (PEAR::isError($available)) {
            $this->config->set('default_channel', $savechannel);
            return $this->raiseError($available);
        }
        $i = $j = 0;
        $data = array(
            'caption' => 'Channel ' . $channel . ' Available packages:',
            'border' => true,
            'headline' => array('Package', 'Version'),
            'channel' => $channel
            );
        if (count($available)==0) {
            $data = '(no packages available yet)';
        } else {
            foreach ($available as $name => $info) {
                $data['data'][] = array($name, (isset($info['stable']) && $info['stable'])
                    ? $info['stable'] : '-n/a-');
            }
        }
        $this->ui->outputData($data, $command);
        $this->config->set('default_channel', $savechannel);
        return true;
    }

    // }}}
    // {{{ doListAll()

    function doListAll($command, $options, $params)
    {
        $savechannel = $channel = $this->config->get('default_channel');
        $reg = &$this->config->getRegistry();
        if (isset($options['channel'])) {
            $channel = $options['channel'];
            if ($reg->channelExists($channel)) {
                $this->config->set('default_channel', $channel);
            } else {
                return $this->raiseError("Channel \"$channel\" does not exist");
            }
        }
        $list_options = false;
        if ($this->config->get('preferred_state') == 'stable') {
            $list_options = true;
        }
        $chan = $reg->getChannel($channel);
        if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
            return $e;
        }
        if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
              $base = $chan->getBaseURL('REST1.1', $this->config->get('preferred_mirror'))) {
            // use faster list-all if available
            $rest = &$this->config->getREST('1.1', array());
            $available = $rest->listAll($base, $list_options, false, false, false, $chan->getName());
        } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) &&
              $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
            $rest = &$this->config->getREST('1.0', array());
            $available = $rest->listAll($base, $list_options, false, false, false, $chan->getName());
        } else {
            $r = &$this->config->getRemote();
            if ($channel == 'pear.php.net') {
                // hack because of poor pearweb design
                $available = $r->call('package.listAll', true, $list_options, false);
            } else {
                $available = $r->call('package.listAll', true, $list_options);
            }
        }
        if (PEAR::isError($available)) {
            $this->config->set('default_channel', $savechannel);
            return $this->raiseError('The package list could not be fetched from the remote server. Please try again. (Debug info: "' . $available->getMessage() . '")');
        }
        $data = array(
            'caption' => 'All packages [Channel ' . $channel . ']:',
            'border' => true,
            'headline' => array('Package', 'Latest', 'Local'),
            'channel' => $channel,
            );
        if (isset($options['channelinfo'])) {
            // add full channelinfo
            $data['caption'] = 'Channel ' . $channel . ' All packages:';
            $data['headline'] = array('Channel', 'Package', 'Latest', 'Local',
                'Description', 'Dependencies');
        }
        $local_pkgs = $reg->listPackages($channel);

        foreach ($available as $name => $info) {
            $installed = $reg->packageInfo($name, null, $channel);
            if (is_array($installed['version'])) {
                $installed['version'] = $installed['version']['release'];
            }
            $desc = $info['summary'];
            if (isset($params[$name])) {
                $desc .= "\n\n".$info['description'];
            }
            if (isset($options['mode']))
            {
                if ($options['mode'] == 'installed' && !isset($installed['version'])) {
                    continue;
                }
                if ($options['mode'] == 'notinstalled' && isset($installed['version'])) {
                    continue;
                }
                if ($options['mode'] == 'upgrades'
                      && (!isset($installed['version']) || version_compare($installed['version'],
                      $info['stable'], '>='))) {
                    continue;
                }
            }
            $pos = array_search(strtolower($name), $local_pkgs);
            if ($pos !== false) {
                unset($local_pkgs[$pos]);
            }

            if (isset($info['stable']) && !$info['stable']) {
                $info['stable'] = null;
            }

            if (isset($options['channelinfo'])) {
                // add full channelinfo
                if ($info['stable'] === $info['unstable']) {
                    $state = $info['state'];
                } else {
                    $state = 'stable';
                }
                $latest = $info['stable'].' ('.$state.')';
                $local = '';
                if (isset($installed['version'])) {
                    $inst_state = $reg->packageInfo($name, 'release_state', $channel);
                    $local = $installed['version'].' ('.$inst_state.')';
                }

                $packageinfo = array(
                    $channel,
                    $name,
                    $latest,
                    $local,
                    isset($desc) ? $desc : null,
                    isset($info['deps']) ? $info['deps'] : null,
                );
            } else {
                $packageinfo = array(
                    $reg->channelAlias($channel) . '/' . $name,
                    isset($info['stable']) ? $info['stable'] : null,
                    isset($installed['version']) ? $installed['version'] : null,
                    isset($desc) ? $desc : null,
                    isset($info['deps']) ? $info['deps'] : null,
                );
            }
            $data['data'][$info['category']][] = $packageinfo;
        }

        if (isset($options['mode']) && in_array($options['mode'], array('notinstalled', 'upgrades'))) {
            $this->config->set('default_channel', $savechannel);
            $this->ui->outputData($data, $command);
            return true;
        }
        foreach ($local_pkgs as $name) {
            $info = &$reg->getPackage($name, $channel);
            $data['data']['Local'][] = array(
                $reg->channelAlias($channel) . '/' . $info->getPackage(),
                '',
                $info->getVersion(),
                $info->getSummary(),
                $info->getDeps()
                );
        }

        $this->config->set('default_channel', $savechannel);
        $this->ui->outputData($data, $command);
        return true;
    }

    // }}}
    // {{{ doSearch()

    function doSearch($command, $options, $params)
    {
        if ((!isset($params[0]) || empty($params[0]))
            && (!isset($params[1]) || empty($params[1])))
        {
            return $this->raiseError('no valid search string supplied');
        };

        $channelinfo = isset($options['channelinfo']);
        $reg = &$this->config->getRegistry();
        if (isset($options['allchannels'])) {
            // search all channels
            unset($options['allchannels']);
            $channels = $reg->getChannels();
            $errors = array();
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            foreach ($channels as $channel) {
                if ($channel->getName() != '__uri') {
                    $options['channel'] = $channel->getName();
                    $ret = $this->doSearch($command, $options, $params);
                    if (PEAR::isError($ret)) {
                        $errors[] = $ret;
                    }
                }
            }
            PEAR::staticPopErrorHandling();
            if (count($errors) !== 0) {
                // for now, only give first error
                return PEAR::raiseError($errors[0]);
            }

            return true;
        }

        $savechannel = $channel = $this->config->get('default_channel');
        $package = $params[0];
        $summary = isset($params[1]) ? $params[1] : false;
        if (isset($options['channel'])) {
            $reg = &$this->config->getRegistry();
            $channel = $options['channel'];
            if ($reg->channelExists($channel)) {
                $this->config->set('default_channel', $channel);
            } else {
                return $this->raiseError('Channel "' . $channel . '" does not exist');
            }
        }
        $chan = $reg->getChannel($channel);
        if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
            return $e;
        }
        if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
              $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
            $rest = &$this->config->getREST('1.0', array());
            $available = $rest->listAll($base, false, false, $package, $summary, $chan->getName());
        } else {
            $r = &$this->config->getRemote();
            $available = $r->call('package.search', $package, $summary, true,
                $this->config->get('preferred_state') == 'stable', true);
        }
        if (PEAR::isError($available)) {
            $this->config->set('default_channel', $savechannel);
            return $this->raiseError($available);
        }
        if (!$available && !$channelinfo) {
            // clean exit when not found, no error !
            $data = 'no packages found that match pattern "' . $package . '", for channel '.$channel.'.';
            $this->ui->outputData($data);
            $this->config->set('default_channel', $channel);
            return true;
        }
        if ($channelinfo) {
            $data = array(
                'caption' => 'Matched packages, channel ' . $channel . ':',
                'border' => true,
                'headline' => array('Channel', 'Package', 'Stable/(Latest)', 'Local'),
                'channel' => $channel
                );
        } else {
            $data = array(
                'caption' => 'Matched packages, channel ' . $channel . ':',
                'border' => true,
                'headline' => array('Package', 'Stable/(Latest)', 'Local'),
                'channel' => $channel
                );
        }

        if (!$available && $channelinfo) {
            unset($data['headline']);
            $data['data'] = 'No packages found that match pattern "' . $package . '".';
            $available = array();
        }
        foreach ($available as $name => $info) {
            $installed = $reg->packageInfo($name, null, $channel);
            $desc = $info['summary'];
            if (isset($params[$name]))
                $desc .= "\n\n".$info['description'];

            if (!isset($info['stable']) || !$info['stable']) {
                $version_remote = 'none';
            } else {
                if ($info['unstable']) {
                    $version_remote = $info['unstable'];
                } else {
                    $version_remote = $info['stable'];
                }
                $version_remote .= ' ('.$info['state'].')';
            }
            $version = is_array($installed['version']) ? $installed['version']['release'] :
                $installed['version'];
            if ($channelinfo) {
                $packageinfo = array(
                    $channel,
                    $name,
                    $version_remote,
                    $version,
                    $desc,
                );
            } else {
                $packageinfo = array(
                    $name,
                    $version_remote,
                    $version,
                    $desc,
                );
            }
            $data['data'][$info['category']][] = $packageinfo;
        }
        $this->ui->outputData($data, $command);
        $this->config->set('default_channel', $channel);
        return true;
    }

    // }}}
    function &getDownloader($options)
    {
        if (!class_exists('PEAR_Downloader')) {
            require_once 'PEAR/Downloader.php';
        }
        $a = &new PEAR_Downloader($this->ui, $options, $this->config);
        return $a;
    }
    // {{{ doDownload()

    function doDownload($command, $options, $params)
    {
        // make certain that dependencies are ignored
        $options['downloadonly'] = 1;

        // eliminate error messages for preferred_state-related errors
        /* TODO: Should be an option, but until now download does respect
           prefered state */
        /* $options['ignorepreferred_state'] = 1; */
        // eliminate error messages for preferred_state-related errors

        $downloader = &$this->getDownloader($options);
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        $e = $downloader->setDownloadDir(getcwd());
        PEAR::staticPopErrorHandling();
        if (PEAR::isError($e)) {
            return $this->raiseError('Current directory is not writeable, cannot download');
        }
        $errors = array();
        $downloaded = array();
        $err = $downloader->download($params);
        if (PEAR::isError($err)) {
            return $err;
        }
        $errors = $downloader->getErrorMsgs();
        if (count($errors)) {
            foreach ($errors as $error) {
                $this->ui->outputData($error);
            }
            return $this->raiseError("$command failed");
        }
        $downloaded = $downloader->getDownloadedPackages();
        foreach ($downloaded as $pkg) {
            $this->ui->outputData("File $pkg[file] downloaded", $command);
        }
        return true;
    }

    function downloadCallback($msg, $params = null)
    {
        if ($msg == 'done') {
            $this->bytes_downloaded = $params;
        }
    }

    // }}}
    // {{{ doListUpgrades()

    function doListUpgrades($command, $options, $params)
    {
        require_once 'PEAR/Common.php';
        if (isset($params[0]) && !is_array(PEAR_Common::betterStates($params[0]))) {
            return $this->raiseError($params[0] . ' is not a valid state (stable/beta/alpha/devel/etc.) try "pear help list-upgrades"');
        }
        $savechannel = $channel = $this->config->get('default_channel');
        $reg = &$this->config->getRegistry();
        foreach ($reg->listChannels() as $channel) {
            $inst = array_flip($reg->listPackages($channel));
            if (!count($inst)) {
                continue;
            }
            if ($channel == '__uri') {
                continue;
            }
            $this->config->set('default_channel', $channel);
            if (empty($params[0])) {
                $state = $this->config->get('preferred_state');
            } else {
                $state = $params[0];
            }
            $caption = $channel . ' Available Upgrades';
            $chan = $reg->getChannel($channel);
            if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
                return $e;
            }
            if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
                  $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
                $rest = &$this->config->getREST('1.0', array());
                if (empty($state) || $state == 'any') {
                    $state = false;
                } else {
                    $caption .= ' (' . implode(', ', PEAR_Common::betterStates($state, true)) . ')';
                }
                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
                $latest = $rest->listLatestUpgrades($base, $state, $inst, $channel, $reg);
                PEAR::staticPopErrorHandling();
            } else {
                $remote = &$this->config->getRemote();
                $remote->pushErrorHandling(PEAR_ERROR_RETURN);
                if (empty($state) || $state == 'any') {
                    $latest = $remote->call("package.listLatestReleases");
                } else {
                    $latest = $remote->call("package.listLatestReleases", $state);
                    $caption .= ' (' . implode(', ', PEAR_Common::betterStates($state, true)) . ')';
                }
                $remote->popErrorHandling();
            }
            if (PEAR::isError($latest)) {
                $this->ui->outputData($latest->getMessage());
                continue;
            }
            $caption .= ':';
            if (PEAR::isError($latest)) {
                $this->config->set('default_channel', $savechannel);
                return $latest;
            }
            $data = array(
                'caption' => $caption,
                'border' => 1,
                'headline' => array('Channel', 'Package', 'Local', 'Remote', 'Size'),
                'channel' => $channel
                );
            foreach ((array)$latest as $pkg => $info) {
                $package = strtolower($pkg);
                if (!isset($inst[$package])) {
                    // skip packages we don't have installed
                    continue;
                }
                extract($info);
                $inst_version = $reg->packageInfo($package, 'version', $channel);
                $inst_state   = $reg->packageInfo($package, 'release_state', $channel);
                if (version_compare("$version", "$inst_version", "le")) {
                    // installed version is up-to-date
                    continue;
                }
                if ($filesize >= 20480) {
                    $filesize += 1024 - ($filesize % 1024);
                    $fs = sprintf("%dkB", $filesize / 1024);
                } elseif ($filesize > 0) {
                    $filesize += 103 - ($filesize % 103);
                    $fs = sprintf("%.1fkB", $filesize / 1024.0);
                } else {
                    $fs = "  -"; // XXX center instead
                }
                $data['data'][] = array($channel, $pkg, "$inst_version ($inst_state)", "$version ($state)", $fs);
            }
            if (isset($options['channelinfo'])) {
                if (empty($data['data'])) {
                    unset($data['headline']);
                    if (count($inst) == 0) {
                        $data['data'] = '(no packages installed)';
                    } else {
                        $data['data'] = '(no upgrades available)';
                    }
                }
                $this->ui->outputData($data, $command);
            } else {
                if (empty($data['data'])) {
                    $this->ui->outputData('Channel ' . $channel . ': No upgrades available');
                } else {
                    $this->ui->outputData($data, $command);
                }
            }
        }
        $this->config->set('default_channel', $savechannel);
        return true;
    }

    // }}}
    // {{{ doClearCache()

    function doClearCache($command, $options, $params)
    {
        $cache_dir = $this->config->get('cache_dir');
        $verbose = $this->config->get('verbose');
        $output = '';
        if (!file_exists($cache_dir) || !is_dir($cache_dir)) {
            return $this->raiseError("$cache_dir does not exist or is not a directory");
        }
        if (!($dp = @opendir($cache_dir))) {
            return $this->raiseError("opendir($cache_dir) failed: $php_errormsg");
        }
        if ($verbose >= 1) {
            $output .= "reading directory $cache_dir\n";
        }
        $num = 0;
        while ($ent = readdir($dp)) {
            if (preg_match('/^xmlrpc_cache_[a-z0-9]{32}\\z/', $ent) ||
                  preg_match('/rest.cache(file|id)\\z/', $ent)) {
                $path = $cache_dir . DIRECTORY_SEPARATOR . $ent;
                if (file_exists($path)) {
                    $ok = @unlink($path);
                } else {
                    $ok = false;
                    $php_errormsg = '';
                }
                if ($ok) {
                    if ($verbose >= 2) {
                        $output .= "deleted $path\n";
                    }
                    $num++;
                } elseif ($verbose >= 1) {
                    $output .= "failed to delete $path $php_errormsg\n";
                }
            }
        }
        closedir($dp);
        if ($verbose >= 1) {
            $output .= "$num cache entries cleared\n";
        }
        $this->ui->outputData(rtrim($output), $command);
        return $num;
    }

    // }}}
}

?>
PEAR-1.7.2/PEAR/Command/Test.xml100644   1750   1750        3152 11014312742  11234 <commands version="1.0">
 <run-tests>
  <summary>Run Regression Tests</summary>
  <function>doRunTests</function>
  <shortcut>rt</shortcut>
  <options>
   <recur>
    <shortopt>r</shortopt>
    <doc>Run tests in child directories, recursively.  4 dirs deep maximum</doc>
   </recur>
   <ini>
    <shortopt>i</shortopt>
    <doc>actual string of settings to pass to php in format &quot; -d setting=blah&quot;</doc>
    <arg>SETTINGS</arg>
   </ini>
   <realtimelog>
    <shortopt>l</shortopt>
    <doc>Log test runs/results as they are run</doc>
   </realtimelog>
   <quiet>
    <shortopt>q</shortopt>
    <doc>Only display detail for failed tests</doc>
   </quiet>
   <simple>
    <shortopt>s</shortopt>
    <doc>Display simple output for all tests</doc>
   </simple>
   <package>
    <shortopt>p</shortopt>
    <doc>Treat parameters as installed packages from which to run tests</doc>
   </package>
   <phpunit>
    <shortopt>u</shortopt>
    <doc>Search parameters for AllTests.php, and use that to run phpunit-based tests.
If none is found, all .phpt tests will be tried instead.</doc>
   </phpunit>
   <tapoutput>
    <shortopt>t</shortopt>
    <doc>Output run-tests.log in TAP-compliant format</doc>
   </tapoutput>
   <cgi>
    <shortopt>c</shortopt>
    <doc>CGI php executable (needed for tests with POST/GET section)</doc>
    <arg>PHPCGI</arg>
   </cgi>
   <coverage>
    <shortopt>x</shortopt>
    <doc>Generate a code coverage report (requires Xdebug 2.0.0+)</doc>
   </coverage>
  </options>
  <doc>[testfile|dir ...]
Run regression tests with PHP&apos;s regression testing script (run-tests.php).</doc>
 </run-tests>
</commands>PEAR-1.7.2/PEAR/Command/Test.php100644   1750   1750       30361 11014312742  11245 <?php
/**
 * PEAR_Command_Test (run-tests)
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Martin Jansen <mj@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Test.php,v 1.27 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * base class
 */
require_once 'PEAR/Command/Common.php';

/**
 * PEAR commands for login/logout
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Martin Jansen <mj@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */

class PEAR_Command_Test extends PEAR_Command_Common
{
    // {{{ properties

    var $commands = array(
        'run-tests' => array(
            'summary' => 'Run Regression Tests',
            'function' => 'doRunTests',
            'shortcut' => 'rt',
            'options' => array(
                'recur' => array(
                    'shortopt' => 'r',
                    'doc' => 'Run tests in child directories, recursively.  4 dirs deep maximum',
                ),
                'ini' => array(
                    'shortopt' => 'i',
                    'doc' => 'actual string of settings to pass to php in format " -d setting=blah"',
                    'arg' => 'SETTINGS'
                ),
                'realtimelog' => array(
                    'shortopt' => 'l',
                    'doc' => 'Log test runs/results as they are run',
                ),
                'quiet' => array(
                    'shortopt' => 'q',
                    'doc' => 'Only display detail for failed tests',
                ),
                'simple' => array(
                    'shortopt' => 's',
                    'doc' => 'Display simple output for all tests',
                ),
                'package' => array(
                    'shortopt' => 'p',
                    'doc' => 'Treat parameters as installed packages from which to run tests',
                ),
                'phpunit' => array(
                    'shortopt' => 'u',
                    'doc' => 'Search parameters for AllTests.php, and use that to run phpunit-based tests
If none is found, all .phpt tests will be tried instead.',
                ),
                'tapoutput' => array(
                    'shortopt' => 't',
                    'doc' => 'Output run-tests.log in TAP-compliant format',
                ),
                'cgi' => array(
                    'shortopt' => 'c',
                    'doc' => 'CGI php executable (needed for tests with POST/GET section)',
                    'arg' => 'PHPCGI',
                ),
                'coverage' => array(
                    'shortopt' => 'x',
                    'doc'      => 'Generate a code coverage report (requires Xdebug 2.0.0+)',
                ),
            ),
            'doc' => '[testfile|dir ...]
Run regression tests with PHP\'s regression testing script (run-tests.php).',
            ),
        );

    var $output;

    // }}}
    // {{{ constructor

    /**
     * PEAR_Command_Test constructor.
     *
     * @access public
     */
    function PEAR_Command_Test(&$ui, &$config)
    {
        parent::PEAR_Command_Common($ui, $config);
    }

    // }}}
    // {{{ doRunTests()

    function doRunTests($command, $options, $params)
    {
        if (isset($options['phpunit']) && isset($options['tapoutput'])) {
            return $this->raiseError('ERROR: cannot use both --phpunit and --tapoutput at the same time');
        }
        require_once 'PEAR/Common.php';
        require_once 'System.php';
        $log = new PEAR_Common;
        $log->ui = &$this->ui; // slightly hacky, but it will work
        $tests = array();
        $depth = isset($options['recur']) ? 4 : 1;

        if (!count($params)) {
            $params[] = '.';
        }
        if (isset($options['package'])) {
            $oldparams = $params;
            $params = array();
            $reg = &$this->config->getRegistry();
            foreach ($oldparams as $param) {
                $pname = $reg->parsePackageName($param, $this->config->get('default_channel'));
                if (PEAR::isError($pname)) {
                    return $this->raiseError($pname);
                }

                $package = &$reg->getPackage($pname['package'], $pname['channel']);
                if (!$package) {
                    return PEAR::raiseError('Unknown package "' .
                        $reg->parsedPackageNameToString($pname) . '"');
                }

                $filelist = $package->getFilelist();
                foreach ($filelist as $name => $atts) {
                    if (isset($atts['role']) && $atts['role'] != 'test') {
                        continue;
                    }

                    if (isset($options['phpunit']) && preg_match('/AllTests\.php\\z/i', $name)) {
                        $params[] = $atts['installed_as'];
                        continue;
                    } elseif (!preg_match('/\.phpt\\z/', $name)) {
                        continue;
                    }
                    $params[] = $atts['installed_as'];
                }
            }
        }

        foreach ($params as $p) {
            if (is_dir($p)) {
                if (isset($options['phpunit'])) {
                    $dir = System::find(array($p, '-type', 'f',
                                                '-maxdepth', $depth,
                                                '-name', 'AllTests.php'));
                    if (count($dir)) {
                        foreach ($dir as $p) {
                            $p = realpath($p);
                            if (!count($tests) ||
                                  (count($tests) && strlen($p) < strlen($tests[0]))) {
                                // this is in a higher-level directory, use this one instead.
                                $tests = array($p);
                            }
                        }
                    }
                    continue;
                }
                $dir = System::find(array($p, '-type', 'f',
                                            '-maxdepth', $depth,
                                            '-name', '*.phpt'));
                $tests = array_merge($tests, $dir);
            } else {
                if (isset($options['phpunit'])) {
                    if (preg_match('/AllTests\.php\\z/i', $p)) {
                        $p = realpath($p);
                        if (!count($tests) ||
                              (count($tests) && strlen($p) < strlen($tests[0]))) {
                            // this is in a higher-level directory, use this one instead.
                            $tests = array($p);
                        }
                    }
                    continue;
                }

                if (file_exists($p) && preg_match('/\.phpt$/', $p)) {
                    $tests[] = $p;
                    continue;
                }

                if (!preg_match('/\.phpt\\z/', $p)) {
                    $p .= '.phpt';
                }
                $dir = System::find(array(dirname($p), '-type', 'f',
                                            '-maxdepth', $depth,
                                            '-name', $p));
                $tests = array_merge($tests, $dir);
            }
        }

        $ini_settings = '';
        if (isset($options['ini'])) {
            $ini_settings .= $options['ini'];
        }

        if (isset($_ENV['TEST_PHP_INCLUDE_PATH'])) {
            $ini_settings .= " -d include_path={$_ENV['TEST_PHP_INCLUDE_PATH']}";
        }

        if ($ini_settings) {
            $this->ui->outputData('Using INI settings: "' . $ini_settings . '"');
        }
        $skipped = $passed = $failed = array();
        $tests_count = count($tests);
        $this->ui->outputData('Running ' . $tests_count . ' tests', $command);
        $start = time();
        if (isset($options['realtimelog']) && file_exists('run-tests.log')) {
            unlink('run-tests.log');
        }

        if (isset($options['tapoutput'])) {
            $tap = '1..' . $tests_count . "\n";
        }

        require_once 'PEAR/RunTest.php';
        $run = new PEAR_RunTest($log, $options);
        $run->tests_count = $tests_count;

        if (isset($options['coverage']) && extension_loaded('xdebug')){
            $run->xdebug_loaded = true;
        } else {
            $run->xdebug_loaded = false;
        }

        $j = $i = 1;
        foreach ($tests as $t) {
            if (isset($options['realtimelog'])) {
                $fp = @fopen('run-tests.log', 'a');
                if ($fp) {
                    fwrite($fp, "Running test [$i / $tests_count] $t...");
                    fclose($fp);
                }
            }
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            if (isset($options['phpunit'])) {
                $result = $run->runPHPUnit($t, $ini_settings);
            } else {
                $result = $run->run($t, $ini_settings, $j);
            }
            PEAR::staticPopErrorHandling();
            if (PEAR::isError($result)) {
                $this->ui->log($result->getMessage());
                continue;
            }

            if (isset($options['tapoutput'])) {
                $tap .= $result[0] . ' ' . $i . $result[1] . "\n";
                continue;
            }

            if (isset($options['realtimelog'])) {
                $fp = @fopen('run-tests.log', 'a');
                if ($fp) {
                    fwrite($fp, "$result\n");
                    fclose($fp);
                }
            }
            
            if ($result == 'FAILED') {
            	$failed[] = $t;
            }
            if ($result == 'PASSED') {
            	$passed[] = $t;
            }
            if ($result == 'SKIPPED') {
            	$skipped[] = $t;
            }
            
            $j++;
        }

        $total = date('i:s', time() - $start);
        if (isset($options['tapoutput'])) {
            $fp = @fopen('run-tests.log', 'w');
            if ($fp) {
                fwrite($fp, $tap, strlen($tap));
                fclose($fp);
                $this->ui->outputData('wrote TAP-format log to "' .realpath('run-tests.log') .
                    '"', $command);
            }
        } else {
            if (count($failed)) {
                $output = "TOTAL TIME: $total\n";
                $output .= count($passed) . " PASSED TESTS\n";
                $output .= count($skipped) . " SKIPPED TESTS\n";
        		$output .= count($failed) . " FAILED TESTS:\n";
            	foreach ($failed as $failure) {
            		$output .= $failure . "\n";
            	}
                
                $mode = isset($options['realtimelog']) ? 'a' : 'w';
                $fp   = @fopen('run-tests.log', $mode);

                if ($fp) {
                    fwrite($fp, $output, strlen($output));
                    fclose($fp);
                    $this->ui->outputData('wrote log to "' . realpath('run-tests.log') . '"', $command);
                }
            } elseif (file_exists('run-tests.log') && !is_dir('run-tests.log')) {
                @unlink('run-tests.log');
            }
        }
        $this->ui->outputData('TOTAL TIME: ' . $total);
        $this->ui->outputData(count($passed) . ' PASSED TESTS', $command);
        $this->ui->outputData(count($skipped) . ' SKIPPED TESTS', $command);
        if (count($failed)) {
    		$this->ui->outputData(count($failed) . ' FAILED TESTS:', $command);
        	foreach ($failed as $failure) {
        		$this->ui->outputData($failure, $command);
        	}
        }

        return true;
    }
    // }}}
}PEAR-1.7.2/PEAR/Downloader/Package.php100644   1750   1750      224107 11014312742  12424 <?php
/**
 * PEAR_Downloader_Package
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Package.php,v 1.113 2008/03/29 14:18:36 dufuz Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * Error code when parameter initialization fails because no releases
 * exist within preferred_state, but releases do exist
 */
define('PEAR_DOWNLOADER_PACKAGE_STATE', -1003);
/**
 * Error code when parameter initialization fails because no releases
 * exist that will work with the existing PHP version
 */
define('PEAR_DOWNLOADER_PACKAGE_PHPVERSION', -1004);
/**
 * Coordinates download parameters and manages their dependencies
 * prior to downloading them.
 *
 * Input can come from three sources:
 *
 * - local files (archives or package.xml)
 * - remote files (downloadable urls)
 * - abstract package names
 *
 * The first two elements are handled cleanly by PEAR_PackageFile, but the third requires
 * accessing pearweb's xml-rpc interface to determine necessary dependencies, and the
 * format returned of dependencies is slightly different from that used in package.xml.
 *
 * This class hides the differences between these elements, and makes automatic
 * dependency resolution a piece of cake.  It also manages conflicts when
 * two classes depend on incompatible dependencies, or differing versions of the same
 * package dependency.  In addition, download will not be attempted if the php version is
 * not supported, PEAR installer version is not supported, or non-PECL extensions are not
 * installed.
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Downloader_Package
{
    /**
     * @var PEAR_Downloader
     */
    var $_downloader;
    /**
     * @var PEAR_Config
     */
    var $_config;
    /**
     * @var PEAR_Registry
     */
    var $_registry;
    /**
     * Used to implement packagingroot properly
     * @var PEAR_Registry
     */
    var $_installRegistry;
    /**
     * @var PEAR_PackageFile_v1|PEAR_PackageFile|v2
     */
    var $_packagefile;
    /**
     * @var array
     */
    var $_parsedname;
    /**
     * @var array
     */
    var $_downloadURL;
    /**
     * @var array
     */
    var $_downloadDeps = array();
    /**
     * @var boolean
     */
    var $_valid = false;
    /**
     * @var boolean
     */
    var $_analyzed = false;
    /**
     * if this or a parent package was invoked with Package-state, this is set to the
     * state variable.
     *
     * This allows temporary reassignment of preferred_state for a parent package and all of
     * its dependencies.
     * @var string|false
     */
    var $_explicitState = false;
    /**
     * If this package is invoked with Package#group, this variable will be true
     */
    var $_explicitGroup = false;
    /**
     * Package type local|url|xmlrpc
     * @var string
     */
    var $_type;
    /**
     * Contents of package.xml, if downloaded from a remote channel
     * @var string|false
     * @access private
     */
    var $_rawpackagefile;
    /**
     * @var boolean
     * @access private
     */
    var $_validated = false;

    /**
     * @param PEAR_Downloader
     */
    function PEAR_Downloader_Package(&$downloader)
    {
        $this->_downloader = &$downloader;
        $this->_config = &$this->_downloader->config;
        $this->_registry = &$this->_config->getRegistry();
        $options = $downloader->getOptions();
        if (isset($options['packagingroot'])) {
            $this->_config->setInstallRoot($options['packagingroot']);
            $this->_installRegistry = &$this->_config->getRegistry();
            $this->_config->setInstallRoot(false);
        } else {
            $this->_installRegistry = &$this->_registry;
        }
        $this->_valid = $this->_analyzed = false;
    }

    /**
     * Parse the input and determine whether this is a local file, a remote uri, or an
     * abstract package name.
     *
     * This is the heart of the PEAR_Downloader_Package(), and is used in
     * {@link PEAR_Downloader::download()}
     * @param string
     * @return bool|PEAR_Error
     */
    function initialize($param)
    {
        $origErr = $this->_fromFile($param);
        if (!$this->_valid) {
            $options = $this->_downloader->getOptions();
            if (isset($options['offline'])) {
                if (PEAR::isError($origErr)) {
                    if (!isset($options['soft'])) {
                        $this->_downloader->log(0, $origErr->getMessage());
                    }
                }
                return PEAR::raiseError('Cannot download non-local package "' . $param . '"');
            }
            $err = $this->_fromUrl($param);
            if (PEAR::isError($err) || !$this->_valid) {
                if ($this->_type == 'url') {
                    if (PEAR::isError($err)) {
                        if (!isset($options['soft'])) {
                            $this->_downloader->log(0, $err->getMessage());
                        }
                    }
                    return PEAR::raiseError("Invalid or missing remote package file");
                }
                $err = $this->_fromString($param);
                if (PEAR::isError($err) || !$this->_valid) {
                    if (PEAR::isError($err) &&
                          $err->getCode() == PEAR_DOWNLOADER_PACKAGE_STATE) {
                        return false; // instruct the downloader to silently skip
                    }
                    if (isset($this->_type) && $this->_type == 'local' &&
                          PEAR::isError($origErr)) {
                        if (is_array($origErr->getUserInfo())) {
                            foreach ($origErr->getUserInfo() as $err) {
                                if (is_array($err)) {
                                    $err = $err['message'];
                                }
                                if (!isset($options['soft'])) {
                                    $this->_downloader->log(0, $err);
                                }
                            }
                        }
                        if (!isset($options['soft'])) {
                            $this->_downloader->log(0, $origErr->getMessage());
                        }
                        if (is_array($param)) {
                            $param = $this->_registry->parsedPackageNameToString($param,
                                true);
                        }
                        return PEAR::raiseError(
                            "Cannot initialize '$param', invalid or missing package file");
                    }
                    if (PEAR::isError($err)) {
                        if (!isset($options['soft'])) {
                            $this->_downloader->log(0, $err->getMessage());
                        }
                    }
                    if (is_array($param)) {
                        $param = $this->_registry->parsedPackageNameToString($param, true);
                    }
                    return PEAR::raiseError(
                        "Cannot initialize '$param', invalid or missing package file");
                }
            }
        }
        return true;
    }

    /**
     * Retrieve any non-local packages
     * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|PEAR_Error
     */
    function &download()
    {
        if (isset($this->_packagefile)) {
            return $this->_packagefile;
        }
        if (isset($this->_downloadURL['url'])) {
            $this->_isvalid = false;
            $info = $this->getParsedPackage();
            foreach ($info as $i => $p) {
                $info[$i] = strtolower($p);
            }
            $err = $this->_fromUrl($this->_downloadURL['url'],
                $this->_registry->parsedPackageNameToString($this->_parsedname, true));
            $newinfo = $this->getParsedPackage();
            foreach ($newinfo as $i => $p) {
                $newinfo[$i] = strtolower($p);
            }
            if ($info != $newinfo) {
                do {
                    if ($info['package'] == 'pecl.php.net' && $newinfo['package'] == 'pear.php.net') {
                        $info['package'] = 'pear.php.net';
                        if ($info == $newinfo) {
                            // skip the channel check if a pecl package says it's a PEAR package
                            break;
                        }
                    }
                    return PEAR::raiseError('CRITICAL ERROR: We are ' .
                        $this->_registry->parsedPackageNameToString($info) . ', but the file ' .
                        'downloaded claims to be ' .
                        $this->_registry->parsedPackageNameToString($this->getParsedPackage()));
                } while (false);
            }
            if (PEAR::isError($err) || !$this->_valid) {
                return $err;
            }
        }
        $this->_type = 'local';
        return $this->_packagefile;
    }

    function &getPackageFile()
    {
        return $this->_packagefile;
    }

    function &getDownloader()
    {
        return $this->_downloader;
    }

    function getType()
    {
        return $this->_type;
    }

    /**
     * Like {@link initialize()}, but operates on a dependency
     */
    function fromDepURL($dep)
    {
        $this->_downloadURL = $dep;
        if (isset($dep['uri'])) {
            $options = $this->_downloader->getOptions();
            if (!extension_loaded("zlib") || isset($options['nocompress'])) {
                $ext = '.tar';
            } else {
                $ext = '.tgz';
            }
            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
            $err = $this->_fromUrl($dep['uri'] . $ext);
            PEAR::popErrorHandling();
            if (PEAR::isError($err)) {
                if (!isset($options['soft'])) {
                    $this->_downloader->log(0, $err->getMessage());
                }
                return PEAR::raiseError('Invalid uri dependency "' . $dep['uri'] . $ext . '", ' .
                    'cannot download');
            }
        } else {
            $this->_parsedname =
                array(
                    'package' => $dep['info']->getPackage(),
                    'channel' => $dep['info']->getChannel(),
                    'version' => $dep['version']
                );
            if (!isset($dep['nodefault'])) {
                $this->_parsedname['group'] = 'default'; // download the default dependency group
                $this->_explicitGroup = false;
            }
            $this->_rawpackagefile = $dep['raw'];
        }
    }

    function detectDependencies($params)
    {
        $options = $this->_downloader->getOptions();
        if (isset($options['downloadonly'])) {
            return;
        }
        if (isset($options['offline'])) {
            $this->_downloader->log(3, 'Skipping dependency download check, --offline specified');
            return;
        }
        $pname = $this->getParsedPackage();
        if (!$pname) {
            return;
        }
        $deps = $this->getDeps();
        if (!$deps) {
            return;
        }
        if (isset($deps['required'])) { // package.xml 2.0
            return $this->_detect2($deps, $pname, $options, $params);
        } else {
            return $this->_detect1($deps, $pname, $options, $params);
        }
    }

    function setValidated()
    {
        $this->_validated = true;
    }

    function alreadyValidated()
    {
        return $this->_validated;
    }

    /**
     * Remove packages to be downloaded that are already installed
     * @param array of PEAR_Downloader_Package objects
     * @static
     */
    function removeInstalled(&$params)
    {
        if (!isset($params[0])) {
            return;
        }
        $options = $params[0]->_downloader->getOptions();
        if (!isset($options['downloadonly'])) {
            foreach ($params as $i => $param) {
                // remove self if already installed with this version
                // this does not need any pecl magic - we only remove exact matches
                if ($param->_installRegistry->packageExists($param->getPackage(), $param->getChannel())) {
                    if (version_compare($param->_installRegistry->packageInfo($param->getPackage(), 'version',
                          $param->getChannel()), $param->getVersion(), '==')) {
                        if (!isset($options['force'])) {
                            $info = $param->getParsedPackage();
                            unset($info['version']);
                            unset($info['state']);
                            if (!isset($options['soft'])) {
                                $param->_downloader->log(1, 'Skipping package "' .
                                    $param->getShortName() .
                                    '", already installed as version ' .
                                    $param->_installRegistry->packageInfo($param->getPackage(),
                                        'version', $param->getChannel()));
                            }
                            $params[$i] = false;
                        }
                    } elseif (!isset($options['force']) && !isset($options['upgrade']) &&
                          !isset($options['soft'])) {
                        $info = $param->getParsedPackage();
                        $param->_downloader->log(1, 'Skipping package "' .
                            $param->getShortName() .
                            '", already installed as version ' .
                            $param->_installRegistry->packageInfo($param->getPackage(), 'version',
                                $param->getChannel()));
                        $params[$i] = false;
                    }
                }
            }
        }
        PEAR_Downloader_Package::removeDuplicates($params);
    }

    function _detect2($deps, $pname, $options, $params)
    {
        $this->_downloadDeps = array();
        $groupnotfound = false;
        foreach (array('package', 'subpackage') as $packagetype) {
            // get required dependency group
            if (isset($deps['required'][$packagetype])) {
                if (isset($deps['required'][$packagetype][0])) {
                    foreach ($deps['required'][$packagetype] as $dep) {
                        if (isset($dep['conflicts'])) {
                            // skip any package that this package conflicts with
                            continue;
                        }
                        $ret = $this->_detect2Dep($dep, $pname, 'required', $params);
                        if (is_array($ret)) {
                            $this->_downloadDeps[] = $ret;
                        }
                    }
                } else {
                    $dep = $deps['required'][$packagetype];
                    if (!isset($dep['conflicts'])) {
                        // skip any package that this package conflicts with
                        $ret = $this->_detect2Dep($dep, $pname, 'required', $params);
                        if (is_array($ret)) {
                            $this->_downloadDeps[] = $ret;
                        }
                    }
                }
            }
            // get optional dependency group, if any
            if (isset($deps['optional'][$packagetype])) {
                $skipnames = array();
                if (!isset($deps['optional'][$packagetype][0])) {
                    $deps['optional'][$packagetype] = array($deps['optional'][$packagetype]);
                }
                foreach ($deps['optional'][$packagetype] as $dep) {
                    $skip = false;
                    if (!isset($options['alldeps'])) {
                        $dep['package'] = $dep['name'];
                        if (!isset($options['soft'])) {
                            $this->_downloader->log(3, 'Notice: package "' .
                              $this->_registry->parsedPackageNameToString($this->getParsedPackage(),
                                    true) . '" optional dependency "' .
                                $this->_registry->parsedPackageNameToString(array('package' =>
                                    $dep['name'], 'channel' => 'pear.php.net'), true) .
                                '" will not be automatically downloaded');
                        }
                        $skipnames[] = $this->_registry->parsedPackageNameToString($dep, true);
                        $skip = true;
                        unset($dep['package']);
                    }
                    if (!($ret = $this->_detect2Dep($dep, $pname, 'optional', $params))) {
                        $dep['package'] = $dep['name'];
                        $skip = count($skipnames) ?
                            $skipnames[count($skipnames) - 1] : '';
                        if ($skip ==
                              $this->_registry->parsedPackageNameToString($dep, true)) {
                            array_pop($skipnames);
                        }
                    }
                    if (!$skip && is_array($ret)) {
                        $this->_downloadDeps[] = $ret;
                    }
                }
                if (count($skipnames)) {
                    if (!isset($options['soft'])) {
                        $this->_downloader->log(1, 'Did not download optional dependencies: ' .
                            implode(', ', $skipnames) .
                            ', use --alldeps to download automatically');
                    }
                }
            }
            // get requested dependency group, if any
            $groupname = $this->getGroup();
            $explicit = $this->_explicitGroup;
            if (!$groupname) {
                if ($this->canDefault()) {
                    $groupname = 'default'; // try the default dependency group
                } else {
                    continue;
                }
            }
            if ($groupnotfound) {
                continue;
            }
            if (isset($deps['group'])) {
                if (isset($deps['group']['attribs'])) {
                    if (strtolower($deps['group']['attribs']['name']) == strtolower($groupname)) {
                        $group = $deps['group'];
                    } elseif ($explicit) {
                        if (!isset($options['soft'])) {
                            $this->_downloader->log(0, 'Warning: package "' .
                                $this->_registry->parsedPackageNameToString($pname, true) .
                                '" has no dependency ' . 'group named "' . $groupname . '"');
                        }
                        $groupnotfound = true;
                        continue;
                    }
                } else {
                    $found = false;
                    foreach ($deps['group'] as $group) {
                        if (strtolower($group['attribs']['name']) == strtolower($groupname)) {
                            $found = true;
                            break;
                        }
                    }
                    if (!$found) {
                        if ($explicit) {
                            if (!isset($options['soft'])) {
                                $this->_downloader->log(0, 'Warning: package "' .
                                    $this->_registry->parsedPackageNameToString($pname, true) .
                                    '" has no dependency ' . 'group named "' . $groupname . '"');
                            }
                        }
                        $groupnotfound = true;
                        continue;
                    }
                }
            }
            if (isset($group)) {
                if (isset($group[$packagetype])) {
                    if (isset($group[$packagetype][0])) {
                        foreach ($group[$packagetype] as $dep) {
                            $ret = $this->_detect2Dep($dep, $pname, 'dependency group "' .
                                $group['attribs']['name'] . '"', $params);
                            if (is_array($ret)) {
                                $this->_downloadDeps[] = $ret;
                            }
                        }
                    } else {
                        $ret = $this->_detect2Dep($group[$packagetype], $pname,
                            'dependency group "' .
                            $group['attribs']['name'] . '"', $params);
                        if (is_array($ret)) {
                            $this->_downloadDeps[] = $ret;
                        }
                    }
                }
            }
        }
    }

    function _detect2Dep($dep, $pname, $group, $params)
    {
        if (isset($dep['conflicts'])) {
            return true;
        }
        $options = $this->_downloader->getOptions();
        if (isset($dep['uri'])) {
            return array('uri' => $dep['uri'], 'dep' => $dep);;
        }
        $testdep = $dep;
        $testdep['package'] = $dep['name'];
        if (PEAR_Downloader_Package::willDownload($testdep, $params)) {
            $dep['package'] = $dep['name'];
            if (!isset($options['soft'])) {
                $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group .
                    ' dependency "' .
                    $this->_registry->parsedPackageNameToString($dep, true) .
                    '", will be installed');
            }
            return false;
        }
        $options = $this->_downloader->getOptions();
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        if ($this->_explicitState) {
            $pname['state'] = $this->_explicitState;
        }
        $url =
            $this->_downloader->_getDepPackageDownloadUrl($dep, $pname);
        if (PEAR::isError($url)) {
            PEAR::popErrorHandling();
            return $url;
        }
        $dep['package'] = $dep['name'];
        $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params, $group == 'optional' &&
            !isset($options['alldeps']), true);
        PEAR::popErrorHandling();
        if (PEAR::isError($ret)) {
            if (!isset($options['soft'])) {
                $this->_downloader->log(0, $ret->getMessage());
            }
            return false;
        } else {
            // check to see if a dep is already installed and is the same or newer
            if (!isset($dep['min']) && !isset($dep['max']) && !isset($dep['recommended'])) {
                $oper = 'has';
            } else {
                $oper = 'gt';
            }
            // do not try to move this before getDepPackageDownloadURL
            // we can't determine whether upgrade is necessary until we know what
            // version would be downloaded
            if (!isset($options['force']) && $this->isInstalled($ret, $oper)) {
                $version = $this->_installRegistry->packageInfo($dep['name'], 'version',
                    $dep['channel']);
                $dep['package'] = $dep['name'];
                if (!isset($options['soft'])) {
                    $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group .
                        ' dependency "' .
                    $this->_registry->parsedPackageNameToString($dep, true) .
                        '" version ' . $url['version'] . ', already installed as version ' .
                        $version);
                }
                return false;
            }
        }
        if (isset($dep['nodefault'])) {
            $ret['nodefault'] = true;
        }
        return $ret;
    }

    function _detect1($deps, $pname, $options, $params)
    {
        $this->_downloadDeps = array();
        $skipnames = array();
        foreach ($deps as $dep) {
            $nodownload = false;
            if ($dep['type'] == 'pkg') {
                $dep['channel'] = 'pear.php.net';
                $dep['package'] = $dep['name'];
                switch ($dep['rel']) {
                    case 'not' :
                        continue 2;
                    case 'ge' :
                    case 'eq' :
                    case 'gt' :
                    case 'has' :
                        $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
                            'required' :
                            'optional';
                        if (PEAR_Downloader_Package::willDownload($dep, $params)) {
                            $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group
                                . ' dependency "' .
                                $this->_registry->parsedPackageNameToString($dep, true) .
                                '", will be installed');
                            continue 2;
                        }
                        $fakedp = new PEAR_PackageFile_v1;
                        $fakedp->setPackage($dep['name']);
                        // skip internet check if we are not upgrading (bug #5810)
                        if (!isset($options['upgrade']) && $this->isInstalled(
                              $fakedp, $dep['rel'])) {
                            $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group
                                . ' dependency "' .
                                $this->_registry->parsedPackageNameToString($dep, true) .
                                '", is already installed');
                            continue 2;
                        }
                }
                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                if ($this->_explicitState) {
                    $pname['state'] = $this->_explicitState;
                }
                $url =
                    $this->_downloader->_getDepPackageDownloadUrl($dep, $pname);
                $chan = 'pear.php.net';
                if (PEAR::isError($url)) {
                    // check to see if this is a pecl package that has jumped
                    // from pear.php.net to pecl.php.net channel
                    if (!class_exists('PEAR_Dependency2')) {
                        require_once 'PEAR/Dependency2.php';
                    }
                    $newdep = PEAR_Dependency2::normalizeDep($dep);
                    $newdep = $newdep[0];
                    $newdep['channel'] = 'pecl.php.net';
                    $chan = 'pecl.php.net';
                    $url =
                        $this->_downloader->_getDepPackageDownloadUrl($newdep, $pname);
                    $obj = &$this->_installRegistry->getPackage($dep['name']);
                    if (PEAR::isError($url)) {
                        PEAR::popErrorHandling();
                        if ($obj !== null && $this->isInstalled($obj, $dep['rel'])) {
                            $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
                                'required' :
                                'optional';
                            $dep['package'] = $dep['name'];
                            if (!isset($options['soft'])) {
                                $this->_downloader->log(3, $this->getShortName() .
                                    ': Skipping ' . $group . ' dependency "' .
                                    $this->_registry->parsedPackageNameToString($dep, true) .
                                    '", already installed as version ' . $obj->getVersion());
                            }
                            $skip = count($skipnames) ?
                                $skipnames[count($skipnames) - 1] : '';
                            if ($skip ==
                                  $this->_registry->parsedPackageNameToString($dep, true)) {
                                array_pop($skipnames);
                            }
                            continue;
                        } else {
                            if (isset($dep['optional']) && $dep['optional'] == 'yes') {
                                $this->_downloader->log(2, $this->getShortName() .
                                    ': Skipping optional dependency "' .
                                    $this->_registry->parsedPackageNameToString($dep, true) .
                                    '", no releases exist');
                                continue;
                            } else {
                                return $url;
                            }
                        }
                    }
                }
                PEAR::popErrorHandling();
                if (!isset($options['alldeps'])) {
                    if (isset($dep['optional']) && $dep['optional'] == 'yes') {
                        if (!isset($options['soft'])) {
                            $this->_downloader->log(3, 'Notice: package "' .
                                $this->getShortName() .
                                '" optional dependency "' .
                                $this->_registry->parsedPackageNameToString(
                                    array('channel' => $chan, 'package' =>
                                    $dep['name']), true) .
                                '" will not be automatically downloaded');
                        }
                        $skipnames[] = $this->_registry->parsedPackageNameToString(
                                array('channel' => $chan, 'package' =>
                                $dep['name']), true);
                        $nodownload = true;
                    }
                }
                if (!isset($options['alldeps']) && !isset($options['onlyreqdeps'])) {
                    if (!isset($dep['optional']) || $dep['optional'] == 'no') {
                        if (!isset($options['soft'])) {
                            $this->_downloader->log(3, 'Notice: package "' .
                                $this->getShortName() .
                                '" required dependency "' .
                                $this->_registry->parsedPackageNameToString(
                                    array('channel' => $chan, 'package' =>
                                    $dep['name']), true) .
                                '" will not be automatically downloaded');
                        }
                        $skipnames[] = $this->_registry->parsedPackageNameToString(
                                array('channel' => $chan, 'package' =>
                                $dep['name']), true);
                        $nodownload = true;
                    }
                }
                // check to see if a dep is already installed
                // do not try to move this before getDepPackageDownloadURL
                // we can't determine whether upgrade is necessary until we know what
                // version would be downloaded
                if (!isset($options['force']) && $this->isInstalled(
                        $url, $dep['rel'])) {
                    $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
                        'required' :
                        'optional';
                    $dep['package'] = $dep['name'];
                    if (isset($newdep)) {
                        $version = $this->_installRegistry->packageInfo($newdep['name'], 'version',
                            $newdep['channel']);
                    } else {
                        $version = $this->_installRegistry->packageInfo($dep['name'], 'version');
                    }
                    $dep['version'] = $url['version'];
                    if (!isset($options['soft'])) {
                        $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group .
                            ' dependency "' .
                            $this->_registry->parsedPackageNameToString($dep, true) .
                            '", already installed as version ' . $version);
                    }
                    $skip = count($skipnames) ?
                        $skipnames[count($skipnames) - 1] : '';
                    if ($skip ==
                          $this->_registry->parsedPackageNameToString($dep, true)) {
                        array_pop($skipnames);
                    }
                    continue;
                }
                if ($nodownload) {
                    continue;
                }
                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                if (isset($newdep)) {
                    $dep = $newdep;
                }
                $dep['package'] = $dep['name'];
                $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params,
                    isset($dep['optional']) && $dep['optional'] == 'yes' &&
                    !isset($options['alldeps']), true);
                PEAR::popErrorHandling();
                if (PEAR::isError($ret)) {
                    if (!isset($options['soft'])) {
                        $this->_downloader->log(0, $ret->getMessage());
                    }
                    continue;
                }
                $this->_downloadDeps[] = $ret;
            }
        }
        if (count($skipnames)) {
            if (!isset($options['soft'])) {
                $this->_downloader->log(1, 'Did not download dependencies: ' .
                    implode(', ', $skipnames) .
                    ', use --alldeps or --onlyreqdeps to download automatically');
            }
        }
    }

    function setDownloadURL($pkg)
    {
        $this->_downloadURL = $pkg;
    }

    /**
     * Set the package.xml object for this downloaded package
     *
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 $pkg
     */
    function setPackageFile(&$pkg)
    {
        $this->_packagefile = &$pkg;
    }

    function getShortName()
    {
        return $this->_registry->parsedPackageNameToString(array('channel' => $this->getChannel(),
            'package' => $this->getPackage()), true);
    }

    function getParsedPackage()
    {
        if (isset($this->_packagefile) || isset($this->_parsedname)) {
            return array('channel' => $this->getChannel(),
                'package' => $this->getPackage(),
                'version' => $this->getVersion());
        }
        return false;
    }

    function getDownloadURL()
    {
        return $this->_downloadURL;
    }

    function canDefault()
    {
        if (isset($this->_downloadURL)) {
            if (isset($this->_downloadURL['nodefault'])) {
                return false;
            }
        }
        return true;
    }

    function getPackage()
    {
        if (isset($this->_packagefile)) {
            return $this->_packagefile->getPackage();
        } elseif (isset($this->_downloadURL['info'])) {
            return $this->_downloadURL['info']->getPackage();
        } else {
            return false;
        }
    }

    /**
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     */
    function isSubpackage(&$pf)
    {
        if (isset($this->_packagefile)) {
            return $this->_packagefile->isSubpackage($pf);
        } elseif (isset($this->_downloadURL['info'])) {
            return $this->_downloadURL['info']->isSubpackage($pf);
        } else {
            return false;
        }
    }

    function getPackageType()
    {
        if (isset($this->_packagefile)) {
            return $this->_packagefile->getPackageType();
        } elseif (isset($this->_downloadURL['info'])) {
            return $this->_downloadURL['info']->getPackageType();
        } else {
            return false;
        }
    }

    function isBundle()
    {
        if (isset($this->_packagefile)) {
            return $this->_packagefile->getPackageType() == 'bundle';
        } else {
            return false;
        }
    }

    function getPackageXmlVersion()
    {
        if (isset($this->_packagefile)) {
            return $this->_packagefile->getPackagexmlVersion();
        } elseif (isset($this->_downloadURL['info'])) {
            return $this->_downloadURL['info']->getPackagexmlVersion();
        } else {
            return '1.0';
        }
    }

    function getChannel()
    {
        if (isset($this->_packagefile)) {
            return $this->_packagefile->getChannel();
        } elseif (isset($this->_downloadURL['info'])) {
            return $this->_downloadURL['info']->getChannel();
        } else {
            return false;
        }
    }

    function getURI()
    {
        if (isset($this->_packagefile)) {
            return $this->_packagefile->getURI();
        } elseif (isset($this->_downloadURL['info'])) {
            return $this->_downloadURL['info']->getURI();
        } else {
            return false;
        }
    }

    function getVersion()
    {
        if (isset($this->_packagefile)) {
            return $this->_packagefile->getVersion();
        } elseif (isset($this->_downloadURL['version'])) {
            return $this->_downloadURL['version'];
        } else {
            return false;
        }
    }

    function isCompatible($pf)
    {
        if (isset($this->_packagefile)) {
            return $this->_packagefile->isCompatible($pf);
        } elseif (isset($this->_downloadURL['info'])) {
            return $this->_downloadURL['info']->isCompatible($pf);
        } else {
            return true;
        }
    }

    function setGroup($group)
    {
        $this->_parsedname['group'] = $group;
    }

    function getGroup()
    {
        if (isset($this->_parsedname['group'])) {
            return $this->_parsedname['group'];
        } else {
            return '';
        }
    }

    function isExtension($name)
    {
        if (isset($this->_packagefile)) {
            return $this->_packagefile->isExtension($name);
        } elseif (isset($this->_downloadURL['info'])) {
			if ($this->_downloadURL['info']->getPackagexmlVersion() == '2.0') {
				return $this->_downloadURL['info']->getProvidesExtension() == $name;
			} else {
				return false;
			}
        } else {
            return false;
        }
    }

    function getDeps()
    {
        if (isset($this->_packagefile)) {
            $ver = $this->_packagefile->getPackagexmlVersion();
            if (version_compare($ver, '2.0', '>=')) {
                return $this->_packagefile->getDeps(true);
            } else {
                return $this->_packagefile->getDeps();
            }
        } elseif (isset($this->_downloadURL['info'])) {
            $ver = $this->_downloadURL['info']->getPackagexmlVersion();
            if (version_compare($ver, '2.0', '>=')) {
                return $this->_downloadURL['info']->getDeps(true);
            } else {
                return $this->_downloadURL['info']->getDeps();
            }
        } else {
            return array();
        }
    }

    /**
     * @param array Parsed array from {@link PEAR_Registry::parsePackageName()} or a dependency
     *                     returned from getDepDownloadURL()
     */
    function isEqual($param)
    {
        if (is_object($param)) {
            $channel = $param->getChannel();
            $package = $param->getPackage();
            if ($param->getURI()) {
                $param = array(
                    'channel' => $param->getChannel(),
                    'package' => $param->getPackage(),
                    'version' => $param->getVersion(),
                    'uri' => $param->getURI(),
                );
            } else {
                $param = array(
                    'channel' => $param->getChannel(),
                    'package' => $param->getPackage(),
                    'version' => $param->getVersion(),
                );
            }
        } else {
            if (isset($param['uri'])) {
                if ($this->getChannel() != '__uri') {
                    return false;
                }
                return $param['uri'] == $this->getURI();
            }
            $package = isset($param['package']) ? $param['package'] :
                $param['info']->getPackage();
            $channel = isset($param['channel']) ? $param['channel'] :
                $param['info']->getChannel();
            if (isset($param['rel'])) {
                if (!class_exists('PEAR_Dependency2')) {
                    require_once 'PEAR/Dependency2.php';
                }
                $newdep = PEAR_Dependency2::normalizeDep($param);
                $newdep = $newdep[0];
            } elseif (isset($param['min'])) {
                $newdep = $param;
            }
        }
        if (isset($newdep)) {
            if (!isset($newdep['min'])) {
                $newdep['min'] = '0';
            }
            if (!isset($newdep['max'])) {
                $newdep['max'] = '100000000000000000000';
            }
            // use magic to support pecl packages suddenly jumping to the pecl channel
            // we need to support both dependency possibilities
            if ($channel == 'pear.php.net' && $this->getChannel() == 'pecl.php.net') {
                if ($package == $this->getPackage()) {
                    $channel = 'pecl.php.net';
                }
            }
            if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') {
                if ($package == $this->getPackage()) {
                    $channel = 'pear.php.net';
                }
            }
            return (strtolower($package) == strtolower($this->getPackage()) &&
                $channel == $this->getChannel() &&
                version_compare($newdep['min'], $this->getVersion(), '<=') &&
                version_compare($newdep['max'], $this->getVersion(), '>='));
        }
        // use magic to support pecl packages suddenly jumping to the pecl channel
        if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') {
            if (strtolower($package) == strtolower($this->getPackage())) {
                $channel = 'pear.php.net';
            }
        }
        if (isset($param['version'])) {
            return (strtolower($package) == strtolower($this->getPackage()) &&
                $channel == $this->getChannel() &&
                $param['version'] == $this->getVersion());
        } else {
            return strtolower($package) == strtolower($this->getPackage()) &&
                $channel == $this->getChannel();
        }
    }

    function isInstalled($dep, $oper = '==')
    {
        if (!$dep) {
            return false;
        }
        if ($oper != 'ge' && $oper != 'gt' && $oper != 'has' && $oper != '==') {
            return false;
        }
        if (is_object($dep)) {
            $package = $dep->getPackage();
            $channel = $dep->getChannel();
            if ($dep->getURI()) {
                $dep = array(
                    'uri' => $dep->getURI(),
                    'version' => $dep->getVersion(),
                );
            } else {
                $dep = array(
                    'version' => $dep->getVersion(),
                );
            }
        } else {
            if (isset($dep['uri'])) {
                $channel = '__uri';
                $package = $dep['dep']['name'];
            } else {
                $channel = $dep['info']->getChannel();
                $package = $dep['info']->getPackage();
            }
        }
        $options = $this->_downloader->getOptions();
        $test = $this->_installRegistry->packageExists($package, $channel);
        if (!$test && $channel == 'pecl.php.net') {
            // do magic to allow upgrading from old pecl packages to new ones
            $test = $this->_installRegistry->packageExists($package, 'pear.php.net');
            $channel = 'pear.php.net';
        }
        if ($test) {
            if (isset($dep['uri'])) {
                if ($this->_installRegistry->packageInfo($package, 'uri', '__uri') == $dep['uri']) {
                    return true;
                }
            }
            if (isset($options['upgrade'])) {
                if ($oper == 'has') {
                    if (version_compare($this->_installRegistry->packageInfo(
                          $package, 'version', $channel),
                          $dep['version'], '>=')) {
                        return true;
                    } else {
                        return false;
                    }
                } else {
                    if (version_compare($this->_installRegistry->packageInfo(
                          $package, 'version', $channel),
                          $dep['version'], '>=')) {
                        return true;
                    }
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    /**
     * Detect duplicate package names with differing versions
     *
     * If a user requests to install Date 1.4.6 and Date 1.4.7,
     * for instance, this is a logic error.  This method
     * detects this situation.
     *
     * @param array $params array of PEAR_Downloader_Package objects
     * @param array $errorparams empty array
     * @return array array of stupid duplicated packages in PEAR_Downloader_Package obejcts
     */
    function detectStupidDuplicates($params, &$errorparams)
    {
        $existing = array();
        foreach ($params as $i => $param) {
            $package = $param->getPackage();
            $channel = $param->getChannel();
            $group   = $param->getGroup();
            if (!isset($existing[$channel . '/' . $package])) {
                $existing[$channel . '/' . $package] = array();
            }
            if (!isset($existing[$channel . '/' . $package][$group])) {
                $existing[$channel . '/' . $package][$group] = array();
            }
            $existing[$channel . '/' . $package][$group][] = $i;
        }

        $indices = array();
        foreach ($existing as $package => $groups) {
            foreach ($groups as $group => $dupes) {
                if (count($dupes) > 1) {
                    $indices = $indices + $dupes;
                }
            }
        }

        $indices = array_unique($indices);
        foreach ($indices as $index) {
            $errorparams[] = $params[$index];
        }
        return count($errorparams);
    }

    /**
     * @param array
     * @param bool ignore install groups - for final removal of dupe packages
     * @static
     */
    function removeDuplicates(&$params, $ignoreGroups = false)
    {
        $pnames = array();
        foreach ($params as $i => $param) {
            if (!$param) {
                continue;
            }
            if ($param->getPackage()) {
                if ($ignoreGroups) {
                    $group = '';
                } else {
                    $group = $param->getGroup();
                }
                $pnames[$i] = $param->getChannel() . '/' .
                    $param->getPackage() . '-' . $param->getVersion() . '#' . $group;
            }
        }
        $pnames = array_unique($pnames);
        $unset = array_diff(array_keys($params), array_keys($pnames));
        $testp = array_flip($pnames);
        foreach ($params as $i => $param) {
            if (!$param) {
                $unset[] = $i;
                continue;
            }
            if (!is_a($param, 'PEAR_Downloader_Package')) {
                $unset[] = $i;
                continue;
            }
            if ($ignoreGroups) {
                $group = '';
            } else {
                $group = $param->getGroup();
            }
            if (!isset($testp[$param->getChannel() . '/' . $param->getPackage() . '-' .
                  $param->getVersion() . '#' . $group])) {
                $unset[] = $i;
            }
        }
        foreach ($unset as $i) {
            unset($params[$i]);
        }
        $ret = array();
        foreach ($params as $i => $param) {
            $ret[] = &$params[$i];
        }
        $params = array();
        foreach ($ret as $i => $param) {
            $params[] = &$ret[$i];
        }
    }

    function explicitState()
    {
        return $this->_explicitState;
    }

    function setExplicitState($s)
    {
        $this->_explicitState = $s;
    }

    /**
     * @static
     */
    function mergeDependencies(&$params)
    {
        $newparams = array();
        $bundles = array();
        foreach ($params as $i => $param) {
            if (!$param->isBundle()) {
                continue;
            }
            $bundles[] = $i;
            $pf = &$param->getPackageFile();
            $newdeps = array();
            $contents = $pf->getBundledPackages();
            if (!is_array($contents)) {
                $contents = array($contents);
            }
            foreach ($contents as $file) {
                $filecontents = $pf->getFileContents($file);
                $dl = &$param->getDownloader();
                $options = $dl->getOptions();
                if (PEAR::isError($dir = $dl->getDownloadDir())) {
                    return $dir;
                }
                $fp = @fopen($dir . DIRECTORY_SEPARATOR . $file, 'wb');
                if (!$fp) {
                    continue;
                }
                fwrite($fp, $filecontents, strlen($filecontents));
                fclose($fp);
                if ($s = $params[$i]->explicitState()) {
                    $obj->setExplicitState($s);
                }
                $obj = &new PEAR_Downloader_Package($params[$i]->getDownloader());
                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                if (PEAR::isError($dir = $dl->getDownloadDir())) {
                    PEAR::popErrorHandling();
                    return $dir;
                }
                $e = $obj->_fromFile($a = $dir . DIRECTORY_SEPARATOR . $file);
                PEAR::popErrorHandling();
                if (PEAR::isError($e)) {
                    if (!isset($options['soft'])) {
                        $dl->log(0, $e->getMessage());
                    }
                    continue;
                }
                $j = &$obj;
                if (!PEAR_Downloader_Package::willDownload($j,
                      array_merge($params, $newparams)) && !$param->isInstalled($j)) {
                    $newparams[] = &$j;
                }
            }
        }
        foreach ($bundles as $i) {
            unset($params[$i]); // remove bundles - only their contents matter for installation
        }
        PEAR_Downloader_Package::removeDuplicates($params); // strip any unset indices
        if (count($newparams)) { // add in bundled packages for install
            foreach ($newparams as $i => $unused) {
                $params[] = &$newparams[$i];
            }
            $newparams = array();
        }
        foreach ($params as $i => $param) {
            $newdeps = array();
            foreach ($param->_downloadDeps as $dep) {
                if (!PEAR_Downloader_Package::willDownload($dep,
                      array_merge($params, $newparams)) && !$param->isInstalled($dep)) {
                    $newdeps[] = $dep;
                } else {
                    // detect versioning conflicts here
                }
            }
            // convert the dependencies into PEAR_Downloader_Package objects for the next time
            // around
            $params[$i]->_downloadDeps = array();
            foreach ($newdeps as $dep) {
                $obj = &new PEAR_Downloader_Package($params[$i]->getDownloader());
                if ($s = $params[$i]->explicitState()) {
                    $obj->setExplicitState($s);
                }
                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                $e = $obj->fromDepURL($dep);
                PEAR::popErrorHandling();
                if (PEAR::isError($e)) {
                    if (!isset($options['soft'])) {
                        $obj->_downloader->log(0, $e->getMessage());
                    }
                    continue;
                }
                $e = $obj->detectDependencies($params);
                if (PEAR::isError($e)) {
                    if (!isset($options['soft'])) {
                        $obj->_downloader->log(0, $e->getMessage());
                    }
                }
                $j = &$obj;
                $newparams[] = &$j;
            }
        }
        if (count($newparams)) {
            foreach ($newparams as $i => $unused) {
                $params[] = &$newparams[$i];
            }
            return true;
        } else {
            return false;
        }
    }


    /**
     * @static
     */
    function willDownload($param, $params)
    {
        if (!is_array($params)) {
            return false;
        }
        foreach ($params as $obj) {
            if ($obj->isEqual($param)) {
                return true;
            }
        }
        return false;
    }

    /**
     * For simpler unit-testing
     * @param PEAR_Config
     * @param int
     * @param string
     */
    function &getPackagefileObject(&$c, $d, $t = false)
    {
        $a = &new PEAR_PackageFile($c, $d, $t);
        return $a;
    }


    /**
     * This will retrieve from a local file if possible, and parse out
     * a group name as well.  The original parameter will be modified to reflect this.
     * @param string|array can be a parsed package name as well
     * @access private
     */
    function _fromFile(&$param)
    {
        $saveparam = $param;
        if (is_string($param)) {
            if (!@file_exists($param)) {
                $test = explode('#', $param);
                $group = array_pop($test);
                if (@file_exists(implode('#', $test))) {
                    $this->setGroup($group);
                    $param = implode('#', $test);
                    $this->_explicitGroup = true;
                }
            }
            if (@is_file($param)) {
                $this->_type = 'local';
                $options = $this->_downloader->getOptions();
                if (isset($options['downloadonly'])) {
                    $pkg = &$this->getPackagefileObject($this->_config,
                        $this->_downloader->_debug);
                } else {
                    if (PEAR::isError($dir = $this->_downloader->getDownloadDir())) {
                        return $dir;
                    }
                    $pkg = &$this->getPackagefileObject($this->_config,
                        $this->_downloader->_debug, $dir);
                }
                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                $pf = &$pkg->fromAnyFile($param, PEAR_VALIDATE_INSTALLING);
                PEAR::popErrorHandling();
                if (PEAR::isError($pf)) {
                    $this->_valid = false;
                    $param = $saveparam;
                    return $pf;
                }
                $this->_packagefile = &$pf;
                if (!$this->getGroup()) {
                    $this->setGroup('default'); // install the default dependency group
                }
                return $this->_valid = true;
            }
        }
        $param = $saveparam;
        return $this->_valid = false;
    }

    function _fromUrl($param, $saveparam = '')
    {
        if (!is_array($param) &&
              (preg_match('#^(http|ftp)://#', $param))) {
            $options = $this->_downloader->getOptions();
            $this->_type = 'url';
            $callback = $this->_downloader->ui ?
                array(&$this->_downloader, '_downloadCallback') : null;
            $this->_downloader->pushErrorHandling(PEAR_ERROR_RETURN);
            if (PEAR::isError($dir = $this->_downloader->getDownloadDir())) {
                $this->_downloader->popErrorHandling();
                return $dir;
            }
            $this->_downloader->log(3, 'Downloading "' . $param . '"');
            $file = $this->_downloader->downloadHttp($param, $this->_downloader->ui,
                $dir, $callback, null, false, $this->getChannel());
            $this->_downloader->popErrorHandling();
            if (PEAR::isError($file)) {
                if (!empty($saveparam)) {
                    $saveparam = ", cannot download \"$saveparam\"";
                }
                $err = PEAR::raiseError('Could not download from "' . $param .
                    '"' . $saveparam . ' (' . $file->getMessage() . ')');
                    return $err;
            }
            if ($this->_rawpackagefile) {
                require_once 'Archive/Tar.php';
                $tar = &new Archive_Tar($file);
                $packagexml = $tar->extractInString('package2.xml');
                if (!$packagexml) {
                    $packagexml = $tar->extractInString('package.xml');
                }
                if (str_replace(array("\n", "\r"), array('',''), $packagexml) !=
                      str_replace(array("\n", "\r"), array('',''), $this->_rawpackagefile)) {
                    if ($this->getChannel() == 'pear.php.net') {
                        // be more lax for the existing PEAR packages that have not-ok
                        // characters in their package.xml
                        $this->_downloader->log(0, 'CRITICAL WARNING: The "' .
                            $this->getPackage() . '" package has invalid characters in its ' .
                            'package.xml.  The next version of PEAR may not be able to install ' .
                            'this package for security reasons.  Please open a bug report at ' .
                            'http://pear.php.net/package/' . $this->getPackage() . '/bugs');
                    } else {
                        return PEAR::raiseError('CRITICAL ERROR: package.xml downloaded does ' .
                            'not match value returned from xml-rpc');
                    }
                }
            }
            // whew, download worked!
            if (isset($options['downloadonly'])) {
                $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->debug);
            } else {
                if (PEAR::isError($dir = $this->_downloader->getDownloadDir())) {
                    return $dir;
                }
                $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->debug,
                    $dir);
            }
            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
            $pf = &$pkg->fromAnyFile($file, PEAR_VALIDATE_INSTALLING);
            PEAR::popErrorHandling();
            if (PEAR::isError($pf)) {
                if (is_array($pf->getUserInfo())) {
                    foreach ($pf->getUserInfo() as $err) {
                        if (is_array($err)) {
                            $err = $err['message'];
                        }
                        if (!isset($options['soft'])) {
                            $this->_downloader->log(0, "Validation Error: $err");
                        }
                    }
                }
                if (!isset($options['soft'])) {
                    $this->_downloader->log(0, $pf->getMessage());
                }
                $err = PEAR::raiseError('Download of "' . ($saveparam ? $saveparam :
                    $param) . '" succeeded, but it is not a valid package archive');
                $this->_valid = false;
                return $err;
            }
            $this->_packagefile = &$pf;
            $this->setGroup('default'); // install the default dependency group
            return $this->_valid = true;
        }
        return $this->_valid = false;
    }

    /**
     *
     * @param string|array pass in an array of format
     *                     array(
     *                      'package' => 'pname',
     *                     ['channel' => 'channame',]
     *                     ['version' => 'version',]
     *                     ['state' => 'state',])
     *                     or a string of format [channame/]pname[-version|-state]
     */
    function _fromString($param)
    {
        $options = $this->_downloader->getOptions();
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        $pname = $this->_registry->parsePackageName($param,
            $this->_config->get('default_channel'));
        PEAR::popErrorHandling();
        if (PEAR::isError($pname)) {
            if ($pname->getCode() == 'invalid') {
                $this->_valid = false;
                return false;
            }
            if ($pname->getCode() == 'channel') {
                $parsed = $pname->getUserInfo();
                if ($this->_downloader->discover($parsed['channel'])) {
                    if ($this->_config->get('auto_discover')) {
                        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                        $pname = $this->_registry->parsePackageName($param,
                            $this->_config->get('default_channel'));
                        PEAR::popErrorHandling();
                    } else {
                        if (!isset($options['soft'])) {
                            $this->_downloader->log(0, 'Channel "' . $parsed['channel'] .
                                '" is not initialized, use ' .
                                '"pear channel-discover ' . $parsed['channel'] . '" to initialize' .
                                'or pear config-set auto_discover 1');
                        }
                    }
                }
                if (PEAR::isError($pname)) {
                    if (!isset($options['soft'])) {
                        $this->_downloader->log(0, $pname->getMessage());
                    }
                    if (is_array($param)) {
                        $param = $this->_registry->parsedPackageNameToString($param);
                    }
                    $err = PEAR::raiseError('invalid package name/package file "' .
                        $param . '"');
                    $this->_valid = false;
                    return $err;
                }
            } else {
                if (!isset($options['soft'])) {
                    $this->_downloader->log(0, $pname->getMessage());
                }
                $err = PEAR::raiseError('invalid package name/package file "' .
                    $param . '"');
                $this->_valid = false;
                return $err;
            }
        }
        if (!isset($this->_type)) {
            $this->_type = 'xmlrpc';
        }
        $this->_parsedname = $pname;
        if (isset($pname['state'])) {
            $this->_explicitState = $pname['state'];
        } else {
            $this->_explicitState = false;
        }
        if (isset($pname['group'])) {
            $this->_explicitGroup = true;
        } else {
            $this->_explicitGroup = false;
        }
        $info = $this->_downloader->_getPackageDownloadUrl($pname);
        if (PEAR::isError($info)) {
            if ($info->getCode() != -976 && $pname['channel'] == 'pear.php.net') {
                // try pecl
                $pname['channel'] = 'pecl.php.net';
                if ($test = $this->_downloader->_getPackageDownloadUrl($pname)) {
                    if (!PEAR::isError($test)) {
                        $info = PEAR::raiseError($info->getMessage() . ' - package ' .
                            $this->_registry->parsedPackageNameToString($pname, true) .
                            ' can be installed with "pecl install ' . $pname['package'] .
                            '"');
                    } else {
                        $pname['channel'] = 'pear.php.net';
                    }
                } else {
                    $pname['channel'] = 'pear.php.net';
                }
            }
            return $info;
        }
        $this->_rawpackagefile = $info['raw'];
        $ret = $this->_analyzeDownloadURL($info, $param, $pname);
        if (PEAR::isError($ret)) {
            return $ret;
        }
        if ($ret) {
            $this->_downloadURL = $ret;
            return $this->_valid = (bool) $ret;
        }
    }

    /**
     * @param array output of package.getDownloadURL
     * @param string|array|object information for detecting packages to be downloaded, and
     *                            for errors
     * @param array name information of the package
     * @param array|null packages to be downloaded
     * @param bool is this an optional dependency?
     * @param bool is this any kind of dependency?
     * @access private
     */
    function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = false,
                                 $isdependency = false)
    {
        if (!is_string($param) && PEAR_Downloader_Package::willDownload($param, $params)) {
            return false;
        }
        if (!$info) {
            if (!is_string($param)) {
                $saveparam = ", cannot download \"$param\"";
            } else {
                $saveparam = '';
            }
            // no releases exist
            return PEAR::raiseError('No releases for package "' .
                $this->_registry->parsedPackageNameToString($pname, true) . '" exist' . $saveparam);
        }
        if (strtolower($info['info']->getChannel()) != strtolower($pname['channel'])) {
            $err = false;
            if ($pname['channel'] == 'pecl.php.net') {
                if ($info['info']->getChannel() != 'pear.php.net') {
                    $err = true;
                }
            } elseif ($info['info']->getChannel() == 'pecl.php.net') {
                if ($pname['channel'] != 'pear.php.net') {
                    $err = true;
                }
            } else {
                $err = true;
            }
            if ($err) {
                return PEAR::raiseError('SECURITY ERROR: package in channel "' . $pname['channel'] .
                    '" retrieved another channel\'s name for download! ("' .
                    $info['info']->getChannel() . '")');
            }
        }
        if (!isset($info['url'])) {
            if ($this->isInstalled($info)) {
                if ($isdependency && version_compare($info['version'],
                      $this->_registry->packageInfo($info['info']->getPackage(),
                            'version', $info['info']->getChannel()), '<=')) {
                    // ignore bogus errors of "failed to download dependency"
                    // if it is already installed and the one that would be
                    // downloaded is older or the same version (Bug #7219)
                    return false;
                }
            }
            $instead =  ', will instead download version ' . $info['version'] .
                        ', stability "' . $info['info']->getState() . '"';
            // releases exist, but we failed to get any
            if (isset($this->_downloader->_options['force'])) {
                if (isset($pname['version'])) {
                    $vs = ', version "' . $pname['version'] . '"';
                } elseif (isset($pname['state'])) {
                    $vs = ', stability "' . $pname['state'] . '"';
                } elseif ($param == 'dependency') {
                    if (!class_exists('PEAR_Common')) {
                        require_once 'PEAR/Common.php';
                    }
                    if (!in_array($info['info']->getState(),
                          PEAR_Common::betterStates($this->_config->get('preferred_state'), true))) {
                        if ($optional) {
                            // don't spit out confusing error message
                            return $this->_downloader->_getPackageDownloadUrl(
                                array('package' => $pname['package'],
                                      'channel' => $pname['channel'],
                                      'version' => $info['version']));
                        }
                        $vs = ' within preferred state "' . $this->_config->get('preferred_state') .
                            '"';
                    } else {
                        if (!class_exists('PEAR_Dependency2')) {
                            require_once 'PEAR/Dependency2.php';
                        }
                        if ($optional) {
                            // don't spit out confusing error message
                            return $this->_downloader->_getPackageDownloadUrl(
                                array('package' => $pname['package'],
                                      'channel' => $pname['channel'],
                                      'version' => $info['version']));
                        }
                        $vs = PEAR_Dependency2::_getExtraString($pname);
                        $instead = '';
                    }
                } else {
                    $vs = ' within preferred state "' . $this->_config->get(
                        'preferred_state') . '"';
                }
                if (!isset($options['soft'])) {
                    $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
                        '/' . $pname['package'] . $vs . $instead);
                }
                // download the latest release
                return $this->_downloader->_getPackageDownloadUrl(
                    array('package' => $pname['package'],
                          'channel' => $pname['channel'],
                          'version' => $info['version']));
            } else {
                if (isset($info['php']) && $info['php']) {
                    $err = PEAR::raiseError('Failed to download ' .
                        $this->_registry->parsedPackageNameToString(
                            array('channel' => $pname['channel'],
                                  'package' => $pname['package']),
                                true) .
                        ', latest release is version ' . $info['php']['v'] .
                        ', but it requires PHP version "' .
                        $info['php']['m'] . '", use "' .
                        $this->_registry->parsedPackageNameToString(
                            array('channel' => $pname['channel'], 'package' => $pname['package'],
                            'version' => $info['php']['v'])) . '" to install',
                            PEAR_DOWNLOADER_PACKAGE_PHPVERSION);
                    return $err;
                }
                // construct helpful error message
                if (isset($pname['version'])) {
                    $vs = ', version "' . $pname['version'] . '"';
                } elseif (isset($pname['state'])) {
                    $vs = ', stability "' . $pname['state'] . '"';
                } elseif ($param == 'dependency') {
                    if (!class_exists('PEAR_Common')) {
                        require_once 'PEAR/Common.php';
                    }
                    if (!in_array($info['info']->getState(),
                          PEAR_Common::betterStates($this->_config->get('preferred_state'), true))) {
                        if ($optional) {
                            // don't spit out confusing error message, and don't die on
                            // optional dep failure!
                            return $this->_downloader->_getPackageDownloadUrl(
                                array('package' => $pname['package'],
                                      'channel' => $pname['channel'],
                                      'version' => $info['version']));
                        }
                        $vs = ' within preferred state "' . $this->_config->get('preferred_state') .
                            '"';
                    } else {
                        if (!class_exists('PEAR_Dependency2')) {
                            require_once 'PEAR/Dependency2.php';
                        }
                        if ($optional) {
                            // don't spit out confusing error message, and don't die on
                            // optional dep failure!
                            return $this->_downloader->_getPackageDownloadUrl(
                                array('package' => $pname['package'],
                                      'channel' => $pname['channel'],
                                      'version' => $info['version']));
                        }
                        $vs = PEAR_Dependency2::_getExtraString($pname);
                    }
                } else {
                    $vs = ' within preferred state "' . $this->_downloader->config->get(
                        'preferred_state') . '"';
                }
                $options = $this->_downloader->getOptions();
                // this is only set by the "download-all" command
                if (isset($options['ignorepreferred_state'])) {
                    $err = PEAR::raiseError(
                        'Failed to download ' . $this->_registry->parsedPackageNameToString(
                            array('channel' => $pname['channel'], 'package' => $pname['package']),
                                true)
                         . $vs .
                        ', latest release is version ' . $info['version'] .
                        ', stability "' . $info['info']->getState() . '", use "' .
                        $this->_registry->parsedPackageNameToString(
                            array('channel' => $pname['channel'], 'package' => $pname['package'],
                            'version' => $info['version'])) . '" to install',
                            PEAR_DOWNLOADER_PACKAGE_STATE);
                    return $err;
                }
                $err = PEAR::raiseError(
                    'Failed to download ' . $this->_registry->parsedPackageNameToString(
                        array('channel' => $pname['channel'], 'package' => $pname['package']),
                            true)
                     . $vs .
                    ', latest release is version ' . $info['version'] .
                    ', stability "' . $info['info']->getState() . '", use "' .
                    $this->_registry->parsedPackageNameToString(
                        array('channel' => $pname['channel'], 'package' => $pname['package'],
                        'version' => $info['version'])) . '" to install');
                return $err;
            }
        }
        if (isset($info['deprecated']) && $info['deprecated']) {
            $this->_downloader->log(0,
                'WARNING: "' .
                    $this->_registry->parsedPackageNameToString(
                            array('channel' => $info['info']->getChannel(),
                                  'package' => $info['info']->getPackage()), true) .
                '" is deprecated in favor of "' .
                    $this->_registry->parsedPackageNameToString($info['deprecated'], true) .
                '"');
        }
        return $info;
    }
}
?>
PEAR-1.7.2/PEAR/Frontend/CLI.php100644   1750   1750       67043 11014312742  11145 <?php
/**
 * PEAR_Frontend_CLI
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: CLI.php,v 1.68 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */
/**
 * base class
 */
require_once 'PEAR/Frontend.php';

/**
 * Command-line Frontend for the PEAR Installer
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */
class PEAR_Frontend_CLI extends PEAR_Frontend
{
    // {{{ properties

    /**
     * What type of user interface this frontend is for.
     * @var string
     * @access public
     */
    var $type = 'CLI';
    var $lp = ''; // line prefix

    var $params = array();
    var $term = array(
        'bold' => '',
        'normal' => '',
        );

    // }}}

    // {{{ constructor

    function PEAR_Frontend_CLI()
    {
        parent::PEAR();
        $term = getenv('TERM'); //(cox) $_ENV is empty for me in 4.1.1
        if (function_exists('posix_isatty') && !posix_isatty(1)) {
            // output is being redirected to a file or through a pipe
        } elseif ($term) {
            // XXX can use ncurses extension here, if available
            if (preg_match('/^(xterm|vt220|linux)/', $term)) {
                $this->term['bold'] = sprintf("%c%c%c%c", 27, 91, 49, 109);
                $this->term['normal']=sprintf("%c%c%c", 27, 91, 109);
            } elseif (preg_match('/^vt100/', $term)) {
                $this->term['bold'] = sprintf("%c%c%c%c%c%c", 27, 91, 49, 109, 0, 0);
                $this->term['normal']=sprintf("%c%c%c%c%c", 27, 91, 109, 0, 0);
            }
        } elseif (OS_WINDOWS) {
            // XXX add ANSI codes here
        }
    }

    // }}}

    // {{{ displayLine(text)

    function displayLine($text)
    {
        trigger_error("PEAR_Frontend_CLI::displayLine deprecated", E_USER_ERROR);
    }

    function _displayLine($text)
    {
        print "$this->lp$text\n";
    }

    // }}}
    // {{{ display(text)

    function display($text)
    {
        trigger_error("PEAR_Frontend_CLI::display deprecated", E_USER_ERROR);
    }

    function _display($text)
    {
        print $text;
    }

    // }}}
    // {{{ displayError(eobj)

    /**
     * @param object PEAR_Error object
     */
    function displayError($eobj)
    {
        return $this->_displayLine($eobj->getMessage());
    }

    // }}}
    // {{{ displayFatalError(eobj)

    /**
     * @param object PEAR_Error object
     */
    function displayFatalError($eobj)
    {
        $this->displayError($eobj);
        if (class_exists('PEAR_Config')) {
            $config = &PEAR_Config::singleton();
            if ($config->get('verbose') > 5) {
                if (function_exists('debug_print_backtrace')) {
                    debug_print_backtrace();
                } elseif (function_exists('debug_backtrace')) {
                    $trace = debug_backtrace();
                    $raised = false;
                    foreach ($trace as $i => $frame) {
                        if (!$raised) {
                            if (isset($frame['class']) && strtolower($frame['class']) ==
                                  'pear' && strtolower($frame['function']) == 'raiseerror') {
                                $raised = true;
                            } else {
                                continue;
                            }
                        }
                        if (!isset($frame['class'])) {
                            $frame['class'] = '';
                        }
                        if (!isset($frame['type'])) {
                            $frame['type'] = '';
                        }
                        if (!isset($frame['function'])) {
                            $frame['function'] = '';
                        }
                        if (!isset($frame['line'])) {
                            $frame['line'] = '';
                        }
                        $this->_displayLine("#$i: $frame[class]$frame[type]$frame[function] $frame[line]");
                    }
                }
            }
        }
        exit(1);
    }

    // }}}
    // {{{ displayHeading(title)

    function displayHeading($title)
    {
        trigger_error("PEAR_Frontend_CLI::displayHeading deprecated", E_USER_ERROR);
    }

    function _displayHeading($title)
    {
        print $this->lp.$this->bold($title)."\n";
        print $this->lp.str_repeat("=", strlen($title))."\n";
    }

    // }}}

    /**
     * Instruct the runInstallScript method to skip a paramgroup that matches the
     * id value passed in.
     *
     * This method is useful for dynamically configuring which sections of a post-install script
     * will be run based on the user's setup, which is very useful for making flexible
     * post-install scripts without losing the cross-Frontend ability to retrieve user input
     * @param string
     */
    function skipParamgroup($id)
    {
        $this->_skipSections[$id] = true;
    }

    function runPostinstallScripts(&$scripts)
    {
        foreach ($scripts as $i => $script) {
            $this->runInstallScript($scripts[$i]->_params, $scripts[$i]->_obj);
        }
    }

    /**
     * @param array $xml contents of postinstallscript tag
     * @param object $script post-installation script
     * @param string install|upgrade
     */
    function runInstallScript($xml, &$script)
    {
        $this->_skipSections = array();
        if (!is_array($xml) || !isset($xml['paramgroup'])) {
            $script->run(array(), '_default');
        } else {
            $completedPhases = array();
            if (!isset($xml['paramgroup'][0])) {
                $xml['paramgroup'] = array($xml['paramgroup']);
            }
            foreach ($xml['paramgroup'] as $group) {
                if (isset($this->_skipSections[$group['id']])) {
                    // the post-install script chose to skip this section dynamically
                    continue;
                }
                if (isset($group['name'])) {
                    $paramname = explode('::', $group['name']);
                    if ($lastgroup['id'] != $paramname[0]) {
                        continue;
                    }
                    $group['name'] = $paramname[1];
                    if (isset($answers)) {
                        if (isset($answers[$group['name']])) {
                            switch ($group['conditiontype']) {
                                case '=' :
                                    if ($answers[$group['name']] != $group['value']) {
                                        continue 2;
                                    }
                                break;
                                case '!=' :
                                    if ($answers[$group['name']] == $group['value']) {
                                        continue 2;
                                    }
                                break;
                                case 'preg_match' :
                                    if (!@preg_match('/' . $group['value'] . '/',
                                          $answers[$group['name']])) {
                                        continue 2;
                                    }
                                break;
                                default :
                                return;
                            }
                        }
                    } else {
                        return;
                    }
                }
                $lastgroup = $group;
                if (isset($group['instructions'])) {
                    $this->_display($group['instructions']);
                }
                if (!isset($group['param'][0])) {
                    $group['param'] = array($group['param']);
                }
                if (isset($group['param'])) {
                    if (method_exists($script, 'postProcessPrompts')) {
                        $prompts = $script->postProcessPrompts($group['param'], $group['id']);
                        if (!is_array($prompts) || count($prompts) != count($group['param'])) {
                            $this->outputData('postinstall', 'Error: post-install script did not ' .
                                'return proper post-processed prompts');
                            $prompts = $group['param'];
                        } else {
                            foreach ($prompts as $i => $var) {
                                if (!is_array($var) || !isset($var['prompt']) ||
                                      !isset($var['name']) ||
                                      ($var['name'] != $group['param'][$i]['name']) ||
                                      ($var['type'] != $group['param'][$i]['type'])) {
                                    $this->outputData('postinstall', 'Error: post-install script ' .
                                        'modified the variables or prompts, severe security risk. ' .
                                        'Will instead use the defaults from the package.xml');
                                    $prompts = $group['param'];
                                }
                            }
                        }
                        $answers = $this->confirmDialog($prompts);
                    } else {
                        $answers = $this->confirmDialog($group['param']);
                    }
                }
                if ((isset($answers) && $answers) || !isset($group['param'])) {
                    if (!isset($answers)) {
                        $answers = array();
                    }
                    array_unshift($completedPhases, $group['id']);
                    if (!$script->run($answers, $group['id'])) {
                        $script->run($completedPhases, '_undoOnError');
                        return;
                    }
                } else {
                    $script->run($completedPhases, '_undoOnError');
                    return;
                }
            }
        }
    }

    /**
     * Ask for user input, confirm the answers and continue until the user is satisfied
     * @param array an array of arrays, format array('name' => 'paramname', 'prompt' =>
     *              'text to display', 'type' => 'string'[, default => 'default value'])
     * @return array
     */
    function confirmDialog($params)
    {
        $answers = array();
        $prompts = $types = array();
        foreach ($params as $param) {
            $prompts[$param['name']] = $param['prompt'];
            $types[$param['name']] = $param['type'];
            if (isset($param['default'])) {
                $answers[$param['name']] = $param['default'];
            } else {
                $answers[$param['name']] = '';
            }
        }
        $tried = false;
        do {
            if ($tried) {
                $i = 1;
                foreach ($answers as $var => $value) {
                    if (!strlen($value)) {
                        echo $this->bold("* Enter an answer for #" . $i . ": ({$prompts[$var]})\n");
                    }
                    $i++;
                }
            }
            $answers = $this->userDialog('', $prompts, $types, $answers);
            $tried = true;
        } while (is_array($answers) && count(array_filter($answers)) != count($prompts));
        return $answers;
    }
    // {{{ userDialog(prompt, [type], [default])

    function userDialog($command, $prompts, $types = array(), $defaults = array(),
                        $screensize = 20)
    {
        if (!is_array($prompts)) {
            return array();
        }
        $testprompts = array_keys($prompts);
        $result = $defaults;
        if (!defined('STDIN')) {
            $fp = fopen('php://stdin', 'r');
        } else {
            $fp = STDIN;
        }
        reset($prompts);
        if (count($prompts) == 1 && $types[key($prompts)] == 'yesno') {
            foreach ($prompts as $key => $prompt) {
                $type = $types[$key];
                $default = @$defaults[$key];
                print "$prompt ";
                if ($default) {
                    print "[$default] ";
                }
                print ": ";
                if (version_compare(phpversion(), '5.0.0', '<')) {
                    $line = fgets($fp, 2048);
                } else {
                    if (!defined('STDIN')) {
                        define('STDIN', fopen('php://stdin', 'r'));
                    }
                    $line = fgets(STDIN, 2048);
                }
                if ($default && trim($line) == "") {
                    $result[$key] = $default;
                } else {
                    $result[$key] = trim($line);
                }
            }
            return $result;
        }
        while (true) {
            $descLength = max(array_map('strlen', $prompts));
            $descFormat = "%-{$descLength}s";
            $last = count($prompts);

            $i = 0;
            foreach ($prompts as $n => $var) {
                printf("%2d. $descFormat : %s\n", ++$i, $prompts[$n], isset($result[$n]) ?
                    $result[$n] : null);
            }

            print "\n1-$last, 'all', 'abort', or Enter to continue: ";
            $tmp = trim(fgets($fp, 1024));
            if (empty($tmp)) {
                break;
            }
            if ($tmp == 'abort') {
                return false;
            }
            if (isset($testprompts[(int)$tmp - 1])) {
                $var = $testprompts[(int)$tmp - 1];
                $desc = $prompts[$var];
                $current = @$result[$var];
                print "$desc [$current] : ";
                $tmp = trim(fgets($fp, 1024));
                if (trim($tmp) !== '') {
                    $result[$var] = trim($tmp);
                }
            } elseif ($tmp == 'all') {
                foreach ($prompts as $var => $desc) {
                    $current = $result[$var];
                    print "$desc [$current] : ";
                    $tmp = trim(fgets($fp, 1024));
                    if (trim($tmp) !== '') {
                        $result[$var] = trim($tmp);
                    }
                }
            }
        }
        if (!defined('STDIN')) {
            fclose($fp);
        }
        return $result;
    }

    // }}}
    // {{{ userConfirm(prompt, [default])

    function userConfirm($prompt, $default = 'yes')
    {
        trigger_error("PEAR_Frontend_CLI::userConfirm not yet converted", E_USER_ERROR);
        static $positives = array('y', 'yes', 'on', '1');
        static $negatives = array('n', 'no', 'off', '0');
        print "$this->lp$prompt [$default] : ";
        $fp = fopen("php://stdin", "r");
        $line = fgets($fp, 2048);
        fclose($fp);
        $answer = strtolower(trim($line));
        if (empty($answer)) {
            $answer = $default;
        }
        if (in_array($answer, $positives)) {
            return true;
        }
        if (in_array($answer, $negatives)) {
            return false;
        }
        if (in_array($default, $positives)) {
            return true;
        }
        return false;
    }

    // }}}
    // {{{ startTable([params])

    function startTable($params = array())
    {
        trigger_error("PEAR_Frontend_CLI::startTable deprecated", E_USER_ERROR);
    }

    function _startTable($params = array())
    {
        $params['table_data'] = array();
        $params['widest'] = array();  // indexed by column
        $params['highest'] = array(); // indexed by row
        $params['ncols'] = 0;
        $this->params = $params;
    }

    // }}}
    // {{{ tableRow(columns, [rowparams], [colparams])

    function tableRow($columns, $rowparams = array(), $colparams = array())
    {
        trigger_error("PEAR_Frontend_CLI::tableRow deprecated", E_USER_ERROR);
    }

    function _tableRow($columns, $rowparams = array(), $colparams = array())
    {
        $highest = 1;
        for ($i = 0; $i < sizeof($columns); $i++) {
            $col = &$columns[$i];
            if (isset($colparams[$i]) && !empty($colparams[$i]['wrap'])) {
                $col = wordwrap($col, $colparams[$i]['wrap'], "\n", 0);
            }
            if (strpos($col, "\n") !== false) {
                $multiline = explode("\n", $col);
                $w = 0;
                foreach ($multiline as $n => $line) {
                    if (strlen($line) > $w) {
                        $w = strlen($line);
                    }
                }
                $lines = sizeof($multiline);
            } else {
                $w = strlen($col);
            }

            if (isset($this->params['widest'][$i])) {
                if ($w > $this->params['widest'][$i]) {
                    $this->params['widest'][$i] = $w;
                }
            } else {
                $this->params['widest'][$i] = $w;
            }
            $tmp = count_chars($columns[$i], 1);
            // handle unix, mac and windows formats
            $lines = (isset($tmp[10]) ? $tmp[10] : (isset($tmp[13]) ? $tmp[13] : 0)) + 1;
            if ($lines > $highest) {
                $highest = $lines;
            }
        }
        if (sizeof($columns) > $this->params['ncols']) {
            $this->params['ncols'] = sizeof($columns);
        }
        $new_row = array(
            'data' => $columns,
            'height' => $highest,
            'rowparams' => $rowparams,
            'colparams' => $colparams,
            );
        $this->params['table_data'][] = $new_row;
    }

    // }}}
    // {{{ endTable()

    function endTable()
    {
        trigger_error("PEAR_Frontend_CLI::endTable deprecated", E_USER_ERROR);
    }

    function _endTable()
    {
        extract($this->params);
        if (!empty($caption)) {
            $this->_displayHeading($caption);
        }
        if (count($table_data) == 0) {
            return;
        }
        if (!isset($width)) {
            $width = $widest;
        } else {
            for ($i = 0; $i < $ncols; $i++) {
                if (!isset($width[$i])) {
                    $width[$i] = $widest[$i];
                }
            }
        }
        $border = false;
        if (empty($border)) {
            $cellstart = '';
            $cellend = ' ';
            $rowend = '';
            $padrowend = false;
            $borderline = '';
        } else {
            $cellstart = '| ';
            $cellend = ' ';
            $rowend = '|';
            $padrowend = true;
            $borderline = '+';
            foreach ($width as $w) {
                $borderline .= str_repeat('-', $w + strlen($cellstart) + strlen($cellend) - 1);
                $borderline .= '+';
            }
        }
        if ($borderline) {
            $this->_displayLine($borderline);
        }
        for ($i = 0; $i < sizeof($table_data); $i++) {
            extract($table_data[$i]);
            if (!is_array($rowparams)) {
                $rowparams = array();
            }
            if (!is_array($colparams)) {
                $colparams = array();
            }
            $rowlines = array();
            if ($height > 1) {
                for ($c = 0; $c < sizeof($data); $c++) {
                    $rowlines[$c] = preg_split('/(\r?\n|\r)/', $data[$c]);
                    if (sizeof($rowlines[$c]) < $height) {
                        $rowlines[$c] = array_pad($rowlines[$c], $height, '');
                    }
                }
            } else {
                for ($c = 0; $c < sizeof($data); $c++) {
                    $rowlines[$c] = array($data[$c]);
                }
            }
            for ($r = 0; $r < $height; $r++) {
                $rowtext = '';
                for ($c = 0; $c < sizeof($data); $c++) {
                    if (isset($colparams[$c])) {
                        $attribs = array_merge($rowparams, $colparams);
                    } else {
                        $attribs = $rowparams;
                    }
                    $w = isset($width[$c]) ? $width[$c] : 0;
                    //$cell = $data[$c];
                    $cell = $rowlines[$c][$r];
                    $l = strlen($cell);
                    if ($l > $w) {
                        $cell = substr($cell, 0, $w);
                    }
                    if (isset($attribs['bold'])) {
                        $cell = $this->bold($cell);
                    }
                    if ($l < $w) {
                        // not using str_pad here because we may
                        // add bold escape characters to $cell
                        $cell .= str_repeat(' ', $w - $l);
                    }

                    $rowtext .= $cellstart . $cell . $cellend;
                }
                if (!$border) {
                    $rowtext = rtrim($rowtext);
                }
                $rowtext .= $rowend;
                $this->_displayLine($rowtext);
            }
        }
        if ($borderline) {
            $this->_displayLine($borderline);
        }
    }

    // }}}
    // {{{ outputData()

    function outputData($data, $command = '_default')
    {
        switch ($command) {
            case 'channel-info':
                foreach ($data as $type => $section) {
                    if ($type == 'main') {
                        $section['data'] = array_values($section['data']);
                    }
                    $this->outputData($section);
                }
                break;
            case 'install':
            case 'upgrade':
            case 'upgrade-all':
                if (isset($data['release_warnings'])) {
                    $this->_displayLine('');
                    $this->_startTable(array(
                        'border' => false,
                        'caption' => 'Release Warnings'
                        ));
                    $this->_tableRow(array($data['release_warnings']), null, array(1 => array('wrap' => 55)));
                    $this->_endTable();
                    $this->_displayLine('');
                }
                $this->_displayLine($data['data']);
                break;
            case 'search':
                $this->_startTable($data);
                if (isset($data['headline']) && is_array($data['headline'])) {
                    $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55)));
                }

                foreach($data['data'] as $category) {
                    foreach($category as $pkg) {
                        $this->_tableRow($pkg, null, array(1 => array('wrap' => 55)));
                    }
                };
                $this->_endTable();
                break;
            case 'list-all':
                if (!isset($data['data'])) {
                      $this->_displayLine('No packages in channel');
                      break;
                }
                $this->_startTable($data);
                if (isset($data['headline']) && is_array($data['headline'])) {
                    $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55)));
                }

                foreach($data['data'] as $category) {
                    foreach($category as $pkg) {
                        unset($pkg[4]);
                        unset($pkg[5]);
                        $this->_tableRow($pkg, null, array(1 => array('wrap' => 55)));
                    }
                };
                $this->_endTable();
                break;
            case 'config-show':
                $data['border'] = false;
                $opts = array(0 => array('wrap' => 30),
                              1 => array('wrap' => 20),
                              2 => array('wrap' => 35));
                $this->_startTable($data);
                if (isset($data['headline']) && is_array($data['headline'])) {
                    $this->_tableRow($data['headline'],
                                     array('bold' => true),
                                     $opts);
                }
                foreach($data['data'] as $group) {
                    foreach($group as $value) {
                        if ($value[2] == '') {
                            $value[2] = "<not set>";
                        }
                        $this->_tableRow($value, null, $opts);
                    }
                }
                $this->_endTable();
                break;
            case 'remote-info':
                $d = $data;
                $data = array(
                    'caption' => 'Package details:',
                    'border' => false,
                    'data' => array(
                        array("Latest",    $data['stable']),
                        array("Installed", $data['installed']),
                        array("Package",   $data['name']),
                        array("License",   $data['license']),
                        array("Category",  $data['category']),
                        array("Summary",   $data['summary']),
                        array("Description", $data['description']),
                        ),
                    );
                    if (isset($d['deprecated']) && $d['deprecated']) {
                        $conf = &PEAR_Config::singleton();
                        $reg = $conf->getRegistry();
                        $name = $reg->parsedPackageNameToString($d['deprecated'], true);
                        $data['data'][] = array('Deprecated! use', $name);
                    }
            default: {
                if (is_array($data)) {
                    $this->_startTable($data);
                    $count = count($data['data'][0]);
                    if ($count == 2) {
                        $opts = array(0 => array('wrap' => 25),
                                      1 => array('wrap' => 48)
                        );
                    } elseif ($count == 3) {
                        $opts = array(0 => array('wrap' => 30),
                                      1 => array('wrap' => 20),
                                      2 => array('wrap' => 35)
                        );
                    } else {
                        $opts = null;
                    }
                    if (isset($data['headline']) && is_array($data['headline'])) {
                        $this->_tableRow($data['headline'],
                                         array('bold' => true),
                                         $opts);
                    }
                    foreach($data['data'] as $row) {
                        $this->_tableRow($row, null, $opts);
                    }
                    $this->_endTable();
                } else {
                    $this->_displayLine($data);
                }
            }
        }
    }

    // }}}
    // {{{ log(text)


    function log($text, $append_crlf = true)
    {
        if ($append_crlf) {
            return $this->_displayLine($text);
        }
        return $this->_display($text);
    }


    // }}}
    // {{{ bold($text)

    function bold($text)
    {
        if (empty($this->term['bold'])) {
            return strtoupper($text);
        }
        return $this->term['bold'] . $text . $this->term['normal'];
    }

    // }}}
}

?>
PEAR-1.7.2/PEAR/Installer/Role/Common.php100644   1750   1750       14765 11014312742  13050 <?php
/**
 * Base class for all installation roles.
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2006 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Common.php,v 1.12 2006/10/19 23:55:32 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * Base class for all installation roles.
 *
 * This class allows extensibility of file roles.  Packages with complex
 * customization can now provide custom file roles along with the possibility of
 * adding configuration values to match.
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2006 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Installer_Role_Common
{
    /**
     * @var PEAR_Config
     * @access protected
     */
    var $config;

    /**
     * @param PEAR_Config
     */
    function PEAR_Installer_Role_Common(&$config)
    {
        $this->config = $config;
    }

    /**
     * Retrieve configuration information about a file role from its XML info
     *
     * @param string $role Role Classname, as in "PEAR_Installer_Role_Data"
     * @return array
     */
    function getInfo($role)
    {
        if (empty($GLOBALS['_PEAR_INSTALLER_ROLES'][$role])) {
            return PEAR::raiseError('Unknown Role class: "' . $role . '"');
        }
        return $GLOBALS['_PEAR_INSTALLER_ROLES'][$role];
    }

    /**
     * This is called for each file to set up the directories and files
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @param array attributes from the <file> tag
     * @param string file name
     * @return array an array consisting of:
     *
     *    1 the original, pre-baseinstalldir installation directory
     *    2 the final installation directory
     *    3 the full path to the final location of the file
     *    4 the location of the pre-installation file
     */
    function processInstallation($pkg, $atts, $file, $tmp_path, $layer = null)
    {
        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
        if (PEAR::isError($roleInfo)) {
            return $roleInfo;
        }
        if (!$roleInfo['locationconfig']) {
            return false;
        }
        if ($roleInfo['honorsbaseinstall']) {
            $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'], $layer,
                $pkg->getChannel());
            if (!empty($atts['baseinstalldir'])) {
                $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
            }
        } elseif ($roleInfo['unusualbaseinstall']) {
            $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'],
                    $layer, $pkg->getChannel()) . DIRECTORY_SEPARATOR . $pkg->getPackage();
            if (!empty($atts['baseinstalldir'])) {
                $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
            }
        } else {
            $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'],
                    $layer, $pkg->getChannel()) . DIRECTORY_SEPARATOR . $pkg->getPackage();
        }
        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_dir, $dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
                                                    array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR,
                                                          DIRECTORY_SEPARATOR),
                                                    array($dest_dir, $dest_file, $orig_file));
        return array($save_destdir, $dest_dir, $dest_file, $orig_file);
    }

    /**
     * Get the name of the configuration variable that specifies the location of this file
     * @return string|false
     */
    function getLocationConfig()
    {
        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
        if (PEAR::isError($roleInfo)) {
            return $roleInfo;
        }
        return $roleInfo['locationconfig'];
    }

    /**
     * Do any unusual setup here
     * @param PEAR_Installer
     * @param PEAR_PackageFile_v2
     * @param array file attributes
     * @param string file name
     */
    function setup(&$installer, $pkg, $atts, $file)
    {
    }

    function isExecutable()
    {
        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
        if (PEAR::isError($roleInfo)) {
            return $roleInfo;
        }
        return $roleInfo['executable'];
    }

    function isInstallable()
    {
        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
        if (PEAR::isError($roleInfo)) {
            return $roleInfo;
        }
        return $roleInfo['installable'];
    }

    function isExtension()
    {
        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
        if (PEAR::isError($roleInfo)) {
            return $roleInfo;
        }
        return $roleInfo['phpextension'];
    }
}
?>PEAR-1.7.2/PEAR/Installer/Role/Cfg.xml100644   1750   1750         645 11014312742  12260 <role version="1.0">
 <releasetypes>php</releasetypes>
 <releasetypes>extsrc</releasetypes>
 <releasetypes>extbin</releasetypes>
 <releasetypes>zendextsrc</releasetypes>
 <releasetypes>zendextbin</releasetypes>
 <installable>1</installable>
 <locationconfig>cfg_dir</locationconfig>
 <honorsbaseinstall />
 <unusualbaseinstall>1</unusualbaseinstall>
 <phpfile />
 <executable />
 <phpextension />
 <config_vars />
</role>PEAR-1.7.2/PEAR/Installer/Role/Cfg.php100644   1750   1750       10612 11014312742  12302 <?php
/**
 * PEAR_Installer_Role_Cfg
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  2007-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Cfg.php,v 1.8 2008/05/14 21:26:30 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.7.0
 */

/**
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  2007-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.7.0
 */
class PEAR_Installer_Role_Cfg extends PEAR_Installer_Role_Common
{
    /**
     * @var PEAR_Installer
     */
    var $installer;
    /**
     * the md5 of the original file
     *
     * @var unknown_type
     */
    var $md5 = null;
    /**
     * Do any unusual setup here
     * @param PEAR_Installer
     * @param PEAR_PackageFile_v2
     * @param array file attributes
     * @param string file name
     */
    function setup(&$installer, $pkg, $atts, $file)
    {
        $this->installer = &$installer;
        $reg = &$this->installer->config->getRegistry();
        $package = $reg->getPackage($pkg->getPackage(), $pkg->getChannel());
        if ($package) {
            $filelist = $package->getFilelist();
            if (isset($filelist[$file]) && isset($filelist[$file]['md5sum'])) {
                $this->md5 = $filelist[$file]['md5sum'];
            }
        }
    }

    function processInstallation($pkg, $atts, $file, $tmp_path, $layer = null)
    {
        $test = parent::processInstallation($pkg, $atts, $file, $tmp_path, $layer);
        if (@file_exists($test[2]) && @file_exists($test[3])) {
            $md5 = md5_file($test[2]);
            // configuration has already been installed, check for mods
            if ($md5 !== $this->md5 && $md5 !== md5_file($test[3])) {
                // configuration has been modified, so save our version as
                // configfile-version
                $old = $test[2];
                $test[2] .= '.new-' . $pkg->getVersion();
                // backup original and re-install it
                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                $tmpcfg = $this->config->get('temp_dir');
                $newloc = System::mkdir(array('-p', $tmpcfg));
                if (!$newloc) {
                    // try temp_dir
                    $newloc = System::mktemp(array('-d'));
                    if (!$newloc || PEAR::isError($newloc)) {
                        PEAR::popErrorHandling();
                        return PEAR::raiseError('Could not save existing configuration file '.
                            $old . ', unable to install.  Please set temp_dir ' .
                            'configuration variable to a writeable location and try again');
                    }
                } else {
                    $newloc = $tmpcfg;
                }
                $temp_file = $newloc . DIRECTORY_SEPARATOR . uniqid('savefile');
                if (!@copy($old, $temp_file)) {
                    PEAR::popErrorHandling();
                    return PEAR::raiseError('Could not save existing configuration file '.
                        $old . ', unable to install.  Please set temp_dir ' .
                        'configuration variable to a writeable location and try again');
                }
                PEAR::popErrorHandling();
                $this->installer->log(0, "WARNING: configuration file $old is being installed as $test[2], you should manually merge in changes to the existing configuration file");
                $this->installer->addFileOperation('rename', array($temp_file, $old, false));
                $this->installer->addFileOperation('delete', array($temp_file));
            }
        }
        return $test;
    }
}
?>
PEAR-1.7.2/PEAR/Installer/Role/Data.xml100644   1750   1750         622 11014312742  12425 <role version="1.0">
 <releasetypes>php</releasetypes>
 <releasetypes>extsrc</releasetypes>
 <releasetypes>extbin</releasetypes>
 <releasetypes>zendextsrc</releasetypes>
 <releasetypes>zendextbin</releasetypes>
 <installable>1</installable>
 <locationconfig>data_dir</locationconfig>
 <honorsbaseinstall />
 <unusualbaseinstall />
 <phpfile />
 <executable />
 <phpextension />
 <config_vars />
</role>PEAR-1.7.2/PEAR/Installer/Role/Data.php100644   1750   1750        2260 11014312742  12434 <?php
/**
 * PEAR_Installer_Role_Data
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Data.php,v 1.7 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Installer_Role_Data extends PEAR_Installer_Role_Common {}
?>PEAR-1.7.2/PEAR/Installer/Role/Doc.xml100644   1750   1750         621 11014312742  12260 <role version="1.0">
 <releasetypes>php</releasetypes>
 <releasetypes>extsrc</releasetypes>
 <releasetypes>extbin</releasetypes>
 <releasetypes>zendextsrc</releasetypes>
 <releasetypes>zendextbin</releasetypes>
 <installable>1</installable>
 <locationconfig>doc_dir</locationconfig>
 <honorsbaseinstall />
 <unusualbaseinstall />
 <phpfile />
 <executable />
 <phpextension />
 <config_vars />
</role>PEAR-1.7.2/PEAR/Installer/Role/Doc.php100644   1750   1750        2255 11014312742  12274 <?php
/**
 * PEAR_Installer_Role_Doc
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Doc.php,v 1.7 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Installer_Role_Doc extends PEAR_Installer_Role_Common {}
?>PEAR-1.7.2/PEAR/Installer/Role/Ext.xml100644   1750   1750         502 11014312742  12311 <role version="1.0">
 <releasetypes>extbin</releasetypes>
 <releasetypes>zendextbin</releasetypes>
 <installable>1</installable>
 <locationconfig>ext_dir</locationconfig>
 <honorsbaseinstall>1</honorsbaseinstall>
 <unusualbaseinstall />
 <phpfile />
 <executable />
 <phpextension>1</phpextension>
 <config_vars />
</role>PEAR-1.7.2/PEAR/Installer/Role/Ext.php100644   1750   1750        2255 11014312742  12327 <?php
/**
 * PEAR_Installer_Role_Ext
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Ext.php,v 1.7 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Installer_Role_Ext extends PEAR_Installer_Role_Common {}
?>PEAR-1.7.2/PEAR/Installer/Role/Php.xml100644   1750   1750         655 11014312742  12311 <role version="1.0">
 <releasetypes>php</releasetypes>
 <releasetypes>extsrc</releasetypes>
 <releasetypes>extbin</releasetypes>
 <releasetypes>zendextsrc</releasetypes>
 <releasetypes>zendextbin</releasetypes>
 <installable>1</installable>
 <locationconfig>php_dir</locationconfig>
 <honorsbaseinstall>1</honorsbaseinstall>
 <unusualbaseinstall />
 <phpfile>1</phpfile>
 <executable />
 <phpextension />
 <config_vars />
</role>PEAR-1.7.2/PEAR/Installer/Role/Php.php100644   1750   1750        2255 11014312742  12316 <?php
/**
 * PEAR_Installer_Role_Php
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Php.php,v 1.8 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Installer_Role_Php extends PEAR_Installer_Role_Common {}
?>PEAR-1.7.2/PEAR/Installer/Role/Script.xml100644   1750   1750         660 11014312742  13022 <role version="1.0">
 <releasetypes>php</releasetypes>
 <releasetypes>extsrc</releasetypes>
 <releasetypes>extbin</releasetypes>
 <releasetypes>zendextsrc</releasetypes>
 <releasetypes>zendextbin</releasetypes>
 <installable>1</installable>
 <locationconfig>bin_dir</locationconfig>
 <honorsbaseinstall>1</honorsbaseinstall>
 <unusualbaseinstall />
 <phpfile />
 <executable>1</executable>
 <phpextension />
 <config_vars />
</role>PEAR-1.7.2/PEAR/Installer/Role/Script.php100644   1750   1750        2266 11014312742  13035 <?php
/**
 * PEAR_Installer_Role_Script
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Script.php,v 1.7 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Installer_Role_Script extends PEAR_Installer_Role_Common {}
?>PEAR-1.7.2/PEAR/Installer/Role/Src.xml100644   1750   1750         442 11014312742  12303 <role version="1.0">
 <releasetypes>extsrc</releasetypes>
 <releasetypes>zendextsrc</releasetypes>
 <installable>1</installable>
 <locationconfig>temp_dir</locationconfig>
 <honorsbaseinstall />
 <unusualbaseinstall />
 <phpfile />
 <executable />
 <phpextension />
 <config_vars />
</role>PEAR-1.7.2/PEAR/Installer/Role/Src.php100644   1750   1750        2422 11014312742  12312 <?php
/**
 * PEAR_Installer_Role_Src
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Src.php,v 1.7 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Installer_Role_Src extends PEAR_Installer_Role_Common
{
    function setup(&$installer, $pkg, $atts, $file)
    {
        $installer->source_files++;
    }
}
?>PEAR-1.7.2/PEAR/Installer/Role/Test.xml100644   1750   1750         622 11014312742  12473 <role version="1.0">
 <releasetypes>php</releasetypes>
 <releasetypes>extsrc</releasetypes>
 <releasetypes>extbin</releasetypes>
 <releasetypes>zendextsrc</releasetypes>
 <releasetypes>zendextbin</releasetypes>
 <installable>1</installable>
 <locationconfig>test_dir</locationconfig>
 <honorsbaseinstall />
 <unusualbaseinstall />
 <phpfile />
 <executable />
 <phpextension />
 <config_vars />
</role>PEAR-1.7.2/PEAR/Installer/Role/Test.php100644   1750   1750        2260 11014312742  12502 <?php
/**
 * PEAR_Installer_Role_Test
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Test.php,v 1.7 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Installer_Role_Test extends PEAR_Installer_Role_Common {}
?>PEAR-1.7.2/PEAR/Installer/Role/Www.xml100644   1750   1750         644 11014312742  12344 <role version="1.0">
 <releasetypes>php</releasetypes>
 <releasetypes>extsrc</releasetypes>
 <releasetypes>extbin</releasetypes>
 <releasetypes>zendextsrc</releasetypes>
 <releasetypes>zendextbin</releasetypes>
 <installable>1</installable>
 <locationconfig>www_dir</locationconfig>
 <honorsbaseinstall>1</honorsbaseinstall>
 <unusualbaseinstall />
 <phpfile />
 <executable />
 <phpextension />
 <config_vars />
</role>PEAR-1.7.2/PEAR/Installer/Role/Www.php100644   1750   1750        2251 11014312742  12347 <?php
/**
 * PEAR_Installer_Role_Www
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  2007-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Www.php,v 1.2 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.7.0
 */

/**
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  2007-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.7.0
 */
class PEAR_Installer_Role_Www extends PEAR_Installer_Role_Common {}
?>PEAR-1.7.2/PEAR/Installer/Role.php100644   1750   1750       20173 11014312742  11606 <?php
/**
 * PEAR_Installer_Role
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Role.php,v 1.20 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * base class for installer roles
 */
require_once 'PEAR/Installer/Role/Common.php';
require_once 'PEAR/XMLParser.php';
/**
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Installer_Role
{
    /**
     * Set up any additional configuration variables that file roles require
     *
     * Never call this directly, it is called by the PEAR_Config constructor
     * @param PEAR_Config
     * @access private
     * @static
     */
    function initializeConfig(&$config)
    {
        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
            PEAR_Installer_Role::registerRoles();
        }
        foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $class => $info) {
            if (!$info['config_vars']) {
                continue;
            }
            $config->_addConfigVars($class, $info['config_vars']);
        }
    }

    /**
     * @param PEAR_PackageFile_v2
     * @param string role name
     * @param PEAR_Config
     * @return PEAR_Installer_Role_Common
     * @static
     */
    function &factory($pkg, $role, &$config)
    {
        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
            PEAR_Installer_Role::registerRoles();
        }
        if (!in_array($role, PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) {
            $a = false;
            return $a;
        }
        $a = 'PEAR_Installer_Role_' . ucfirst($role);
        if (!class_exists($a)) {
            require_once str_replace('_', '/', $a) . '.php';
        }
        $b = new $a($config);
        return $b;
    }

    /**
     * Get a list of file roles that are valid for the particular release type.
     *
     * For instance, src files serve no purpose in regular php releases.
     * @param string
     * @param bool clear cache
     * @return array
     * @static
     */
    function getValidRoles($release, $clear = false)
    {
        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
            PEAR_Installer_Role::registerRoles();
        }
        static $ret = array();
        if ($clear) {
            $ret = array();
        }
        if (isset($ret[$release])) {
            return $ret[$release];
        }
        $ret[$release] = array();
        foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
            if (in_array($release, $okreleases['releasetypes'])) {
                $ret[$release][] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
            }
        }
        return $ret[$release];
    }

    /**
     * Get a list of roles that require their files to be installed
     *
     * Most roles must be installed, but src and package roles, for instance
     * are pseudo-roles.  src files are compiled into a new extension.  Package
     * roles are actually fully bundled releases of a package
     * @param bool clear cache
     * @return array
     * @static
     */
    function getInstallableRoles($clear = false)
    {
        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
            PEAR_Installer_Role::registerRoles();
        }
        static $ret;
        if ($clear) {
            unset($ret);
        }
        if (!isset($ret)) {
            $ret = array();
            foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
                if ($okreleases['installable']) {
                    $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
                }
            }
        }
        return $ret;
    }

    /**
     * Return an array of roles that are affected by the baseinstalldir attribute
     *
     * Most roles ignore this attribute, and instead install directly into:
     * PackageName/filepath
     * so a tests file tests/file.phpt is installed into PackageName/tests/filepath.php
     * @param bool clear cache
     * @return array
     * @static
     */
    function getBaseinstallRoles($clear = false)
    {
        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
            PEAR_Installer_Role::registerRoles();
        }
        static $ret;
        if ($clear) {
            unset($ret);
        }
        if (!isset($ret)) {
            $ret = array();
            foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
                if ($okreleases['honorsbaseinstall']) {
                    $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
                }
            }
        }
        return $ret;
    }

    /**
     * Return an array of file roles that should be analyzed for PHP content at package time,
     * like the "php" role.
     * @param bool clear cache
     * @return array
     * @static
     */
    function getPhpRoles($clear = false)
    {
        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
            PEAR_Installer_Role::registerRoles();
        }
        static $ret;
        if ($clear) {
            unset($ret);
        }
        if (!isset($ret)) {
            $ret = array();
            foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
                if ($okreleases['phpfile']) {
                    $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
                }
            }
        }
        return $ret;
    }

    /**
     * Scan through the Command directory looking for classes
     * and see what commands they implement.
     * @param string which directory to look for classes, defaults to
     *               the Installer/Roles subdirectory of
     *               the directory from where this file (__FILE__) is
     *               included.
     *
     * @return bool TRUE on success, a PEAR error on failure
     * @access public
     * @static
     */
    function registerRoles($dir = null)
    {
        $GLOBALS['_PEAR_INSTALLER_ROLES'] = array();
        $parser = new PEAR_XMLParser;
        if ($dir === null) {
            $dir = dirname(__FILE__) . '/Role';
        }
        if (!file_exists($dir) || !is_dir($dir)) {
            return PEAR::raiseError("registerRoles: opendir($dir) failed: does not exist/is not directory");
        }
        $dp = @opendir($dir);
        if (empty($dp)) {
            return PEAR::raiseError("registerRoles: opendir($dir) failed: $php_errmsg");
        }
        while ($entry = readdir($dp)) {
            if ($entry{0} == '.' || substr($entry, -4) != '.xml') {
                continue;
            }
            $class = "PEAR_Installer_Role_".substr($entry, 0, -4);
            // List of roles
            if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'][$class])) {
                $file = "$dir/$entry";
                $parser->parse(file_get_contents($file));
                $data = $parser->getData();
                if (!is_array($data['releasetypes'])) {
                    $data['releasetypes'] = array($data['releasetypes']);
                }
                $GLOBALS['_PEAR_INSTALLER_ROLES'][$class] = $data;
            }
        }
        closedir($dp);
        ksort($GLOBALS['_PEAR_INSTALLER_ROLES']);
        PEAR_Installer_Role::getBaseinstallRoles(true);
        PEAR_Installer_Role::getInstallableRoles(true);
        PEAR_Installer_Role::getPhpRoles(true);
        PEAR_Installer_Role::getValidRoles('****', true);
        return true;
    }
}
?>
PEAR-1.7.2/PEAR/PackageFile/Generator/v1.php100644   1750   1750      143124 11014312742  13401 <?php
/**
 * package.xml generation class, package.xml version 1.0
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: v1.php,v 1.74 2008/01/03 20:26:37 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * needed for PEAR_VALIDATE_* constants
 */
require_once 'PEAR/Validate.php';
require_once 'System.php';
require_once 'PEAR/PackageFile/v2.php';
/**
 * This class converts a PEAR_PackageFile_v1 object into any output format.
 *
 * Supported output formats include array, XML string, and a PEAR_PackageFile_v2
 * object, for converting package.xml 1.0 into package.xml 2.0 with no sweat.
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_PackageFile_Generator_v1
{
    /**
     * @var PEAR_PackageFile_v1
     */
    var $_packagefile;
    function PEAR_PackageFile_Generator_v1(&$packagefile)
    {
        $this->_packagefile = &$packagefile;
    }

    function getPackagerVersion()
    {
        return '1.7.2';
    }

    /**
     * @param PEAR_Packager
     * @param bool if true, a .tgz is written, otherwise a .tar is written
     * @param string|null directory in which to save the .tgz
     * @return string|PEAR_Error location of package or error object
     */
    function toTgz(&$packager, $compress = true, $where = null)
    {
        require_once 'Archive/Tar.php';
        if ($where === null) {
            if (!($where = System::mktemp(array('-d')))) {
                return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: mktemp failed');
            }
        } elseif (!@System::mkDir(array('-p', $where))) {
            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: "' . $where . '" could' .
                ' not be created');
        }
        if (file_exists($where . DIRECTORY_SEPARATOR . 'package.xml') &&
              !is_file($where . DIRECTORY_SEPARATOR . 'package.xml')) {
            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: unable to save package.xml as' .
                ' "' . $where . DIRECTORY_SEPARATOR . 'package.xml"');
        }
        if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) {
            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: invalid package file');
        }
        $pkginfo = $this->_packagefile->getArray();
        $ext = $compress ? '.tgz' : '.tar';
        $pkgver = $pkginfo['package'] . '-' . $pkginfo['version'];
        $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext;
        if (file_exists(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext) &&
              !is_file(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext)) {
            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: cannot create tgz file "' .
                getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext . '"');
        }
        if ($pkgfile = $this->_packagefile->getPackageFile()) {
            $pkgdir = dirname(realpath($pkgfile));
            $pkgfile = basename($pkgfile);
        } else {
            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: package file object must ' .
                'be created from a real file');
        }
        // {{{ Create the package file list
        $filelist = array();
        $i = 0;

        foreach ($this->_packagefile->getFilelist() as $fname => $atts) {
            $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
            if (!file_exists($file)) {
                return PEAR::raiseError("File does not exist: $fname");
            } else {
                $filelist[$i++] = $file;
                if (!isset($atts['md5sum'])) {
                    $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($file));
                }
                $packager->log(2, "Adding file $fname");
            }
        }
        // }}}
        $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, 'package.xml', true);
        if ($packagexml) {
            $tar =& new Archive_Tar($dest_package, $compress);
            $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors
            // ----- Creates with the package.xml file
            $ok = $tar->createModify(array($packagexml), '', $where);
            if (PEAR::isError($ok)) {
                return $ok;
            } elseif (!$ok) {
                return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: tarball creation failed');
            }
            // ----- Add the content of the package
            if (!$tar->addModify($filelist, $pkgver, $pkgdir)) {
                return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: tarball creation failed');
            }
            return $dest_package;
        }
    }

    /**
     * @param string|null directory to place the package.xml in, or null for a temporary dir
     * @param int one of the PEAR_VALIDATE_* constants
     * @param string name of the generated file
     * @param bool if true, then no analysis will be performed on role="php" files
     * @return string|PEAR_Error path to the created file on success
     */
    function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'package.xml',
                           $nofilechecking = false)
    {
        if (!$this->_packagefile->validate($state, $nofilechecking)) {
            return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: invalid package.xml',
                null, null, null, $this->_packagefile->getValidationWarnings());
        }
        if ($where === null) {
            if (!($where = System::mktemp(array('-d')))) {
                return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: mktemp failed');
            }
        } elseif (!@System::mkDir(array('-p', $where))) {
            return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: "' . $where . '" could' .
                ' not be created');
        }
        $newpkgfile = $where . DIRECTORY_SEPARATOR . $name;
        $np = @fopen($newpkgfile, 'wb');
        if (!$np) {
            return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: unable to save ' .
               "$name as $newpkgfile");
        }
        fwrite($np, $this->toXml($state, true));
        fclose($np);
        return $newpkgfile;
    }

    /**
     * fix both XML encoding to be UTF8, and replace standard XML entities < > " & '
     *
     * @param string $string
     * @return string
     * @access private
     */
    function _fixXmlEncoding($string)
    {
        if (version_compare(phpversion(), '5.0.0', 'lt')) {
            $string = utf8_encode($string);
        }
        return strtr($string, array(
                                          '&'  => '&amp;',
                                          '>'  => '&gt;',
                                          '<'  => '&lt;',
                                          '"'  => '&quot;',
                                          '\'' => '&apos;' ));
    }

    /**
     * Return an XML document based on the package info (as returned
     * by the PEAR_Common::infoFrom* methods).
     *
     * @return string XML data
     */
    function toXml($state = PEAR_VALIDATE_NORMAL, $nofilevalidation = false)
    {
        $this->_packagefile->setDate(date('Y-m-d'));
        if (!$this->_packagefile->validate($state, $nofilevalidation)) {
            return false;
        }
        $pkginfo = $this->_packagefile->getArray();
        static $maint_map = array(
            "handle" => "user",
            "name" => "name",
            "email" => "email",
            "role" => "role",
            );
        $ret = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
        $ret .= "<!DOCTYPE package SYSTEM \"http://pear.php.net/dtd/package-1.0\">\n";
        $ret .= "<package version=\"1.0\" packagerversion=\"1.7.2\">\n" .
" <name>$pkginfo[package]</name>";
        if (isset($pkginfo['extends'])) {
            $ret .= "\n<extends>$pkginfo[extends]</extends>";
        }
        $ret .=
 "\n <summary>".$this->_fixXmlEncoding($pkginfo['summary'])."</summary>\n" .
" <description>".trim($this->_fixXmlEncoding($pkginfo['description']))."\n </description>\n" .
" <maintainers>\n";
        foreach ($pkginfo['maintainers'] as $maint) {
            $ret .= "  <maintainer>\n";
            foreach ($maint_map as $idx => $elm) {
                $ret .= "   <$elm>";
                $ret .= $this->_fixXmlEncoding($maint[$idx]);
                $ret .= "</$elm>\n";
            }
            $ret .= "  </maintainer>\n";
        }
        $ret .= "  </maintainers>\n";
        $ret .= $this->_makeReleaseXml($pkginfo, false, $state);
        if (isset($pkginfo['changelog']) && count($pkginfo['changelog']) > 0) {
            $ret .= " <changelog>\n";
            foreach ($pkginfo['changelog'] as $oldrelease) {
                $ret .= $this->_makeReleaseXml($oldrelease, true);
            }
            $ret .= " </changelog>\n";
        }
        $ret .= "</package>\n";
        return $ret;
    }

    // }}}
    // {{{ _makeReleaseXml()

    /**
     * Generate part of an XML description with release information.
     *
     * @param array  $pkginfo    array with release information
     * @param bool   $changelog  whether the result will be in a changelog element
     *
     * @return string XML data
     *
     * @access private
     */
    function _makeReleaseXml($pkginfo, $changelog = false, $state = PEAR_VALIDATE_NORMAL)
    {
        // XXX QUOTE ENTITIES IN PCDATA, OR EMBED IN CDATA BLOCKS!!
        $indent = $changelog ? "  " : "";
        $ret = "$indent <release>\n";
        if (!empty($pkginfo['version'])) {
            $ret .= "$indent  <version>$pkginfo[version]</version>\n";
        }
        if (!empty($pkginfo['release_date'])) {
            $ret .= "$indent  <date>$pkginfo[release_date]</date>\n";
        }
        if (!empty($pkginfo['release_license'])) {
            $ret .= "$indent  <license>$pkginfo[release_license]</license>\n";
        }
        if (!empty($pkginfo['release_state'])) {
            $ret .= "$indent  <state>$pkginfo[release_state]</state>\n";
        }
        if (!empty($pkginfo['release_notes'])) {
            $ret .= "$indent  <notes>".trim($this->_fixXmlEncoding($pkginfo['release_notes']))
            ."\n$indent  </notes>\n";
        }
        if (!empty($pkginfo['release_warnings'])) {
            $ret .= "$indent  <warnings>".$this->_fixXmlEncoding($pkginfo['release_warnings'])."</warnings>\n";
        }
        if (isset($pkginfo['release_deps']) && sizeof($pkginfo['release_deps']) > 0) {
            $ret .= "$indent  <deps>\n";
            foreach ($pkginfo['release_deps'] as $dep) {
                $ret .= "$indent   <dep type=\"$dep[type]\" rel=\"$dep[rel]\"";
                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 {
                    $ret .= "/>\n";
                }
            }
            $ret .= "$indent  </deps>\n";
        }
        if (isset($pkginfo['configure_options'])) {
            $ret .= "$indent  <configureoptions>\n";
            foreach ($pkginfo['configure_options'] as $c) {
                $ret .= "$indent   <configureoption name=\"".
                    $this->_fixXmlEncoding($c['name']) . "\"";
                if (isset($c['default'])) {
                    $ret .= " default=\"" . $this->_fixXmlEncoding($c['default']) . "\"";
                }
                $ret .= " prompt=\"" . $this->_fixXmlEncoding($c['prompt']) . "\"";
                $ret .= "/>\n";
            }
            $ret .= "$indent  </configureoptions>\n";
        }
        if (isset($pkginfo['provides'])) {
            foreach ($pkginfo['provides'] as $key => $what) {
                $ret .= "$indent  <provides type=\"$what[type]\" ";
                $ret .= "name=\"$what[name]\" ";
                if (isset($what['extends'])) {
                    $ret .= "extends=\"$what[extends]\" ";
                }
                $ret .= "/>\n";
            }
        }
        if (isset($pkginfo['filelist'])) {
            $ret .= "$indent  <filelist>\n";
            if ($state ^ PEAR_VALIDATE_PACKAGING) {
                $ret .= $this->recursiveXmlFilelist($pkginfo['filelist']);
            } else {
                foreach ($pkginfo['filelist'] as $file => $fa) {
                    if (!isset($fa['role'])) {
                        $fa['role'] = '';
                    }
                    $ret .= "$indent   <file role=\"$fa[role]\"";
                    if (isset($fa['baseinstalldir'])) {
                        $ret .= ' baseinstalldir="' .
                            $this->_fixXmlEncoding($fa['baseinstalldir']) . '"';
                    }
                    if (isset($fa['md5sum'])) {
                        $ret .= " md5sum=\"$fa[md5sum]\"";
                    }
                    if (isset($fa['platform'])) {
                        $ret .= " platform=\"$fa[platform]\"";
                    }
                    if (!empty($fa['install-as'])) {
                        $ret .= ' install-as="' .
                            $this->_fixXmlEncoding($fa['install-as']) . '"';
                    }
                    $ret .= ' name="' . $this->_fixXmlEncoding($file) . '"';
                    if (empty($fa['replacements'])) {
                        $ret .= "/>\n";
                    } else {
                        $ret .= ">\n";
                        foreach ($fa['replacements'] as $r) {
                            $ret .= "$indent    <replace";
                            foreach ($r as $k => $v) {
                                $ret .= " $k=\"" . $this->_fixXmlEncoding($v) .'"';
                            }
                            $ret .= "/>\n";
                        }
                        $ret .= "$indent   </file>\n";
                    }
                }
            }
            $ret .= "$indent  </filelist>\n";
        }
        $ret .= "$indent </release>\n";
        return $ret;
    }

    /**
     * @param array
     * @access protected
     */
    function recursiveXmlFilelist($list)
    {
        $this->_dirs = array();
        foreach ($list as $file => $attributes) {
            $this->_addDir($this->_dirs, explode('/', dirname($file)), $file, $attributes);
        }
        return $this->_formatDir($this->_dirs);
    }

    /**
     * @param array
     * @param array
     * @param string|null
     * @param array|null
     * @access private
     */
    function _addDir(&$dirs, $dir, $file = null, $attributes = null)
    {
        if ($dir == array() || $dir == array('.')) {
            $dirs['files'][basename($file)] = $attributes;
            return;
        }
        $curdir = array_shift($dir);
        if (!isset($dirs['dirs'][$curdir])) {
            $dirs['dirs'][$curdir] = array();
        }
        $this->_addDir($dirs['dirs'][$curdir], $dir, $file, $attributes);
    }

    /**
     * @param array
     * @param string
     * @param string
     * @access private
     */
    function _formatDir($dirs, $indent = '', $curdir = '')
    {
        $ret = '';
        if (!count($dirs)) {
            return '';
        }
        if (isset($dirs['dirs'])) {
            uksort($dirs['dirs'], 'strnatcasecmp');
            foreach ($dirs['dirs'] as $dir => $contents) {
                $usedir = "$curdir/$dir";
                $ret .= "$indent   <dir name=\"$dir\">\n";
                $ret .= $this->_formatDir($contents, "$indent ", $usedir);
                $ret .= "$indent   </dir> <!-- $usedir -->\n";
            }
        }
        if (isset($dirs['files'])) {
            uksort($dirs['files'], 'strnatcasecmp');
            foreach ($dirs['files'] as $file => $attribs) {
                $ret .= $this->_formatFile($file, $attribs, $indent);
            }
        }
        return $ret;
    }

    /**
     * @param string
     * @param array
     * @param string
     * @access private
     */
    function _formatFile($file, $attributes, $indent)
    {
        $ret = "$indent   <file role=\"$attributes[role]\"";
        if (isset($attributes['baseinstalldir'])) {
            $ret .= ' baseinstalldir="' .
                $this->_fixXmlEncoding($attributes['baseinstalldir']) . '"';
        }
        if (isset($attributes['md5sum'])) {
            $ret .= " md5sum=\"$attributes[md5sum]\"";
        }
        if (isset($attributes['platform'])) {
            $ret .= " platform=\"$attributes[platform]\"";
        }
        if (!empty($attributes['install-as'])) {
            $ret .= ' install-as="' .
                $this->_fixXmlEncoding($attributes['install-as']) . '"';
        }
        $ret .= ' name="' . $this->_fixXmlEncoding($file) . '"';
        if (empty($attributes['replacements'])) {
            $ret .= "/>\n";
        } else {
            $ret .= ">\n";
            foreach ($attributes['replacements'] as $r) {
                $ret .= "$indent    <replace";
                foreach ($r as $k => $v) {
                    $ret .= " $k=\"" . $this->_fixXmlEncoding($v) .'"';
                }
                $ret .= "/>\n";
            }
            $ret .= "$indent   </file>\n";
        }
        return $ret;
    }

    // {{{ _unIndent()

    /**
     * Unindent given string (?)
     *
     * @param string $str The string that has to be unindented.
     * @return string
     * @access private
     */
    function _unIndent($str)
    {
        // remove leading newlines
        $str = preg_replace('/^[\r\n]+/', '', $str);
        // find whitespace at the beginning of the first line
        $indent_len = strspn($str, " \t");
        $indent = substr($str, 0, $indent_len);
        $data = '';
        // remove the same amount of whitespace from following lines
        foreach (explode("\n", $str) as $line) {
            if (substr($line, 0, $indent_len) == $indent) {
                $data .= substr($line, $indent_len) . "\n";
            }
        }
        return $data;
    }

    /**
     * @return array
     */
    function dependenciesToV2()
    {
        $arr = array();
        $this->_convertDependencies2_0($arr);
        return $arr['dependencies'];
    }

    /**
     * Convert a package.xml version 1.0 into version 2.0
     *
     * Note that this does a basic conversion, to allow more advanced
     * features like bundles and multiple releases
     * @param string the classname to instantiate and return.  This must be
     *               PEAR_PackageFile_v2 or a descendant
     * @param boolean if true, only valid, deterministic package.xml 1.0 as defined by the
     *                strictest parameters will be converted
     * @return PEAR_PackageFile_v2|PEAR_Error
     */
    function &toV2($class = 'PEAR_PackageFile_v2', $strict = false)
    {
        if ($strict) {
            if (!$this->_packagefile->validate()) {
                $a = PEAR::raiseError('invalid package.xml version 1.0 cannot be converted' .
                    ' to version 2.0', null, null, null,
                    $this->_packagefile->getValidationWarnings(true));
                return $a;
            }
        }
        $arr = array(
            'attribs' => array(
                             'version' => '2.0',
                             'xmlns' => 'http://pear.php.net/dtd/package-2.0',
                             'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
                             'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
                             'xsi:schemaLocation' => "http://pear.php.net/dtd/tasks-1.0\n" .
"http://pear.php.net/dtd/tasks-1.0.xsd\n" .
"http://pear.php.net/dtd/package-2.0\n" .
'http://pear.php.net/dtd/package-2.0.xsd',
                         ),
            'name' => $this->_packagefile->getPackage(),
            'channel' => 'pear.php.net',
        );
        $arr['summary'] = $this->_packagefile->getSummary();
        $arr['description'] = $this->_packagefile->getDescription();
        $maintainers = $this->_packagefile->getMaintainers();
        foreach ($maintainers as $maintainer) {
            if ($maintainer['role'] != 'lead') {
                continue;
            }
            $new = array(
                'name' => $maintainer['name'],
                'user' => $maintainer['handle'],
                'email' => $maintainer['email'],
                'active' => 'yes',
            );
            $arr['lead'][] = $new;
        }
        if (!isset($arr['lead'])) { // some people... you know?
            $arr['lead'] = array(
                'name' => 'unknown',
                'user' => 'unknown',
                'email' => 'noleadmaintainer@example.com',
                'active' => 'no',
            );
        }
        if (count($arr['lead']) == 1) {
            $arr['lead'] = $arr['lead'][0];
        }
        foreach ($maintainers as $maintainer) {
            if ($maintainer['role'] == 'lead') {
                continue;
            }
            $new = array(
                'name' => $maintainer['name'],
                'user' => $maintainer['handle'],
                'email' => $maintainer['email'],
                'active' => 'yes',
            );
            $arr[$maintainer['role']][] = $new;
        }
        if (isset($arr['developer']) && count($arr['developer']) == 1) {
            $arr['developer'] = $arr['developer'][0];
        }
        if (isset($arr['contributor']) && count($arr['contributor']) == 1) {
            $arr['contributor'] = $arr['contributor'][0];
        }
        if (isset($arr['helper']) && count($arr['helper']) == 1) {
            $arr['helper'] = $arr['helper'][0];
        }
        $arr['date'] = $this->_packagefile->getDate();
        $arr['version'] =
            array(
                'release' => $this->_packagefile->getVersion(),
                'api' => $this->_packagefile->getVersion(),
            );
        $arr['stability'] =
            array(
                'release' => $this->_packagefile->getState(),
                'api' => $this->_packagefile->getState(),
            );
        $licensemap =
            array(
                'php' => 'http://www.php.net/license',
                'php license' => 'http://www.php.net/license',
                'lgpl' => 'http://www.gnu.org/copyleft/lesser.html',
                'bsd' => 'http://www.opensource.org/licenses/bsd-license.php',
                'bsd style' => 'http://www.opensource.org/licenses/bsd-license.php',
                'bsd-style' => 'http://www.opensource.org/licenses/bsd-license.php',
                'mit' => 'http://www.opensource.org/licenses/mit-license.php',
                'gpl' => 'http://www.gnu.org/copyleft/gpl.html',
                'apache' => 'http://www.opensource.org/licenses/apache2.0.php'
            );
        if (isset($licensemap[strtolower($this->_packagefile->getLicense())])) {
            $arr['license'] = array(
                'attribs' => array('uri' =>
                    $licensemap[strtolower($this->_packagefile->getLicense())]),
                '_content' => $this->_packagefile->getLicense()
                );
        } else {
            // don't use bogus uri
            $arr['license'] = $this->_packagefile->getLicense();
        }
        $arr['notes'] = $this->_packagefile->getNotes();
        $temp = array();
        $arr['contents'] = $this->_convertFilelist2_0($temp);
        $this->_convertDependencies2_0($arr);
        $release = ($this->_packagefile->getConfigureOptions() || $this->_isExtension) ?
            'extsrcrelease' : 'phprelease';
        if ($release == 'extsrcrelease') {
            $arr['channel'] = 'pecl.php.net';
            $arr['providesextension'] = $arr['name']; // assumption
        }
        $arr[$release] = array();
        if ($this->_packagefile->getConfigureOptions()) {
            $arr[$release]['configureoption'] = $this->_packagefile->getConfigureOptions();
            foreach ($arr[$release]['configureoption'] as $i => $opt) {
                $arr[$release]['configureoption'][$i] = array('attribs' => $opt);
            }
            if (count($arr[$release]['configureoption']) == 1) {
                $arr[$release]['configureoption'] = $arr[$release]['configureoption'][0];
            }
        }
        $this->_convertRelease2_0($arr[$release], $temp);
        if ($release == 'extsrcrelease' && count($arr[$release]) > 1) {
            // multiple extsrcrelease tags added in PEAR 1.4.1
            $arr['dependencies']['required']['pearinstaller']['min'] = '1.4.1';
        }
        if ($cl = $this->_packagefile->getChangelog()) {
            foreach ($cl as $release) {
                $rel = array();
                $rel['version'] =
                    array(
                        'release' => $release['version'],
                        'api' => $release['version'],
                    );
                if (!isset($release['release_state'])) {
                    $release['release_state'] = 'stable';
                }
                $rel['stability'] =
                    array(
                        'release' => $release['release_state'],
                        'api' => $release['release_state'],
                    );
                if (isset($release['release_date'])) {
                    $rel['date'] = $release['release_date'];
                } else {
                    $rel['date'] = date('Y-m-d');
                }
                if (isset($release['release_license'])) {
                    if (isset($licensemap[strtolower($release['release_license'])])) {
                        $uri = $licensemap[strtolower($release['release_license'])];
                    } else {
                        $uri = 'http://www.example.com';
                    }
                    $rel['license'] = array(
                            'attribs' => array('uri' => $uri),
                            '_content' => $release['release_license']
                        );
                } else {
                    $rel['license'] = $arr['license'];
                }
                if (!isset($release['release_notes'])) {
                    $release['release_notes'] = 'no release notes';
                }
                $rel['notes'] = $release['release_notes'];
                $arr['changelog']['release'][] = $rel;
            }
        }
        $ret = new $class;
        $ret->setConfig($this->_packagefile->_config);
        if (isset($this->_packagefile->_logger) && is_object($this->_packagefile->_logger)) {
            $ret->setLogger($this->_packagefile->_logger);
        }
        $ret->fromArray($arr);
        return $ret;
    }

    /**
     * @param array
     * @param bool
     * @access private
     */
    function _convertDependencies2_0(&$release, $internal = false)
    {
        $peardep = array('pearinstaller' =>
            array('min' => '1.4.0b1')); // this is a lot safer
        $required = $optional = array();
        $release['dependencies'] = array('required' => array());
        if ($this->_packagefile->hasDeps()) {
            foreach ($this->_packagefile->getDeps() as $dep) {
                if (!isset($dep['optional']) || $dep['optional'] == 'no') {
                    $required[] = $dep;
                } else {
                    $optional[] = $dep;
                }
            }
            foreach (array('required', 'optional') as $arr) {
                $deps = array();
                foreach ($$arr as $dep) {
                    // organize deps by dependency type and name
                    if (!isset($deps[$dep['type']])) {
                        $deps[$dep['type']] = array();
                    }
                    if (isset($dep['name'])) {
                        $deps[$dep['type']][$dep['name']][] = $dep;
                    } else {
                        $deps[$dep['type']][] = $dep;
                    }
                }
                do {
                    if (isset($deps['php'])) {
                        $php = array();
                        if (count($deps['php']) > 1) {
                            $php = $this->_processPhpDeps($deps['php']);
                        } else {
                            if (!isset($deps['php'][0])) {
                                list($key, $blah) = each ($deps['php']); // stupid buggy versions
                                $deps['php'] = array($blah[0]);
                            }
                            $php = $this->_processDep($deps['php'][0]);
                            if (!$php) {
                                break; // poor mans throw
                            }
                        }
                        $release['dependencies'][$arr]['php'] = $php;
                    }
                } while (false);
                do {
                    if (isset($deps['pkg'])) {
                        $pkg = array();
                        $pkg = $this->_processMultipleDepsName($deps['pkg']);
                        if (!$pkg) {
                            break; // poor mans throw
                        }
                        $release['dependencies'][$arr]['package'] = $pkg;
                    }
                } while (false);
                do {
                    if (isset($deps['ext'])) {
                        $pkg = array();
                        $pkg = $this->_processMultipleDepsName($deps['ext']);
                        $release['dependencies'][$arr]['extension'] = $pkg;
                    }
                } while (false);
                // skip sapi - it's not supported so nobody will have used it
                // skip os - it's not supported in 1.0
            }
        }
        if (isset($release['dependencies']['required'])) {
            $release['dependencies']['required'] =
                array_merge($peardep, $release['dependencies']['required']);
        } else {
            $release['dependencies']['required'] = $peardep;
        }
        if (!isset($release['dependencies']['required']['php'])) {
            $release['dependencies']['required']['php'] =
                array('min' => '4.0.0');
        }
        $order = array();
        $bewm = $release['dependencies']['required'];
        $order['php'] = $bewm['php'];
        $order['pearinstaller'] = $bewm['pearinstaller'];
        isset($bewm['package']) ? $order['package'] = $bewm['package'] :0;
        isset($bewm['extension']) ? $order['extension'] = $bewm['extension'] :0;
        $release['dependencies']['required'] = $order;
    }

    /**
     * @param array
     * @access private
     */
    function _convertFilelist2_0(&$package)
    {
        $ret = array('dir' =>
                    array(
                        'attribs' => array('name' => '/'),
                        'file' => array()
                        )
                    );
        $package['platform'] =
        $package['install-as'] = array();
        $this->_isExtension = false;
        foreach ($this->_packagefile->getFilelist() as $name => $file) {
            $file['name'] = $name;
            if (isset($file['role']) && $file['role'] == 'src') {
                $this->_isExtension = true;
            }
            if (isset($file['replacements'])) {
                $repl = $file['replacements'];
                unset($file['replacements']);
            } else {
                unset($repl);
            }
            if (isset($file['install-as'])) {
                $package['install-as'][$name] = $file['install-as'];
                unset($file['install-as']);
            }
            if (isset($file['platform'])) {
                $package['platform'][$name] = $file['platform'];
                unset($file['platform']);
            }
            $file = array('attribs' => $file);
            if (isset($repl)) {
                foreach ($repl as $replace ) {
                    $file['tasks:replace'][] = array('attribs' => $replace);
                }
                if (count($repl) == 1) {
                    $file['tasks:replace'] = $file['tasks:replace'][0];
                }
            }
            $ret['dir']['file'][] = $file;
        }
        return $ret;
    }

    /**
     * Post-process special files with install-as/platform attributes and
     * make the release tag.
     * 
     * This complex method follows this work-flow to create the release tags:
     * 
     * <pre>
     * - if any install-as/platform exist, create a generic release and fill it with
     *   o <install as=..> tags for <file name=... install-as=...>
     *   o <install as=..> tags for <file name=... platform=!... install-as=..>
     *   o <ignore> tags for <file name=... platform=...>
     *   o <ignore> tags for <file name=... platform=... install-as=..>
     * - create a release for each platform encountered and fill with
     *   o <install as..> tags for <file name=... install-as=...>
     *   o <install as..> tags for <file name=... platform=this platform install-as=..>
     *   o <install as..> tags for <file name=... platform=!other platform install-as=..>
     *   o <ignore> tags for <file name=... platform=!this platform>
     *   o <ignore> tags for <file name=... platform=other platform>
     *   o <ignore> tags for <file name=... platform=other platform install-as=..>
     *   o <ignore> tags for <file name=... platform=!this platform install-as=..>
     * </pre>
     * 
     * It does this by accessing the $package parameter, which contains an array with
     * indices:
     * 
     *  - platform: mapping of file => OS the file should be installed on
     *  - install-as: mapping of file => installed name
     *  - osmap: mapping of OS => list of files that should be installed
     *    on that OS
     *  - notosmap: mapping of OS => list of files that should not be
     *    installed on that OS
     *
     * @param array
     * @param array
     * @access private
     */
    function _convertRelease2_0(&$release, $package)
    {
        //- if any install-as/platform exist, create a generic release and fill it with 
        if (count($package['platform']) || count($package['install-as'])) {
            $generic = array();
            $genericIgnore = array();
            foreach ($package['install-as'] as $file => $as) {
                //o <install as=..> tags for <file name=... install-as=...>
                if (!isset($package['platform'][$file])) {
                    $generic[] = $file;
                    continue;
                }
                //o <install as=..> tags for <file name=... platform=!... install-as=..>
                if (isset($package['platform'][$file]) &&
                      $package['platform'][$file]{0} == '!') {
                    $generic[] = $file;
                    continue;
                }
                //o <ignore> tags for <file name=... platform=... install-as=..>
                if (isset($package['platform'][$file]) &&
                      $package['platform'][$file]{0} != '!') {
                    $genericIgnore[] = $file;
                    continue;
                }
            }
            foreach ($package['platform'] as $file => $platform) {
                if (isset($package['install-as'][$file])) {
                    continue;
                }
                if ($platform{0} != '!') {
                    //o <ignore> tags for <file name=... platform=...>
                    $genericIgnore[] = $file;
                }
            }
            if (count($package['platform'])) {
                $oses = $notplatform = $platform = array();
                foreach ($package['platform'] as $file => $os) {
                    // get a list of oses
                    if ($os{0} == '!') {
                        if (isset($oses[substr($os, 1)])) {
                            continue;
                        }
                        $oses[substr($os, 1)] = count($oses);
                    } else {
                        if (isset($oses[$os])) {
                            continue;
                        }
                        $oses[$os] = count($oses);
                    }
                }
                //- create a release for each platform encountered and fill with
                foreach ($oses as $os => $releaseNum) {
                    $release[$releaseNum]['installconditions']['os']['name'] = $os;
                    $release[$releaseNum]['filelist'] = array('install' => array(),
                        'ignore' => array());
                    foreach ($package['install-as'] as $file => $as) {
                        //o <install as=..> tags for <file name=... install-as=...>
                        if (!isset($package['platform'][$file])) {
                            $release[$releaseNum]['filelist']['install'][] =
                                array(
                                    'attribs' => array(
                                        'name' => $file,
                                        'as' => $as,
                                    ),
                                );
                            continue;
                        }
                        //o <install as..> tags for
                        //  <file name=... platform=this platform install-as=..>
                        if (isset($package['platform'][$file]) &&
                              $package['platform'][$file] == $os) {
                            $release[$releaseNum]['filelist']['install'][] =
                                array(
                                    'attribs' => array(
                                        'name' => $file,
                                        'as' => $as,
                                    ),
                                );
                            continue;
                        }
                        //o <install as..> tags for
                        //  <file name=... platform=!other platform install-as=..>
                        if (isset($package['platform'][$file]) &&
                              $package['platform'][$file] != "!$os" &&
                              $package['platform'][$file]{0} == '!') {
                            $release[$releaseNum]['filelist']['install'][] =
                                array(
                                    'attribs' => array(
                                        'name' => $file,
                                        'as' => $as,
                                    ),
                                );
                            continue;
                        }
                        //o <ignore> tags for
                        //  <file name=... platform=!this platform install-as=..>
                        if (isset($package['platform'][$file]) &&
                              $package['platform'][$file] == "!$os") {
                            $release[$releaseNum]['filelist']['ignore'][] =
                                array(
                                    'attribs' => array(
                                        'name' => $file,
                                    ),
                                );
                            continue;
                        }
                        //o <ignore> tags for
                        //  <file name=... platform=other platform install-as=..>
                        if (isset($package['platform'][$file]) &&
                              $package['platform'][$file]{0} != '!' &&
                              $package['platform'][$file] != $os) {
                            $release[$releaseNum]['filelist']['ignore'][] =
                                array(
                                    'attribs' => array(
                                        'name' => $file,
                                    ),
                                );
                            continue;
                        }
                    }
                    foreach ($package['platform'] as $file => $platform) {
                        if (isset($package['install-as'][$file])) {
                            continue;
                        }
                        //o <ignore> tags for <file name=... platform=!this platform>
                        if ($platform == "!$os") {
                            $release[$releaseNum]['filelist']['ignore'][] =
                                array(
                                    'attribs' => array(
                                        'name' => $file,
                                    ),
                                );
                            continue;
                        }
                        //o <ignore> tags for <file name=... platform=other platform>
                        if ($platform{0} != '!' && $platform != $os) {
                            $release[$releaseNum]['filelist']['ignore'][] =
                                array(
                                    'attribs' => array(
                                        'name' => $file,
                                    ),
                                );
                        }
                    }
                    if (!count($release[$releaseNum]['filelist']['install'])) {
                        unset($release[$releaseNum]['filelist']['install']);
                    }
                    if (!count($release[$releaseNum]['filelist']['ignore'])) {
                        unset($release[$releaseNum]['filelist']['ignore']);
                    }
                }
                if (count($generic) || count($genericIgnore)) {
                    $release[count($oses)] = array();
                    if (count($generic)) {
                        foreach ($generic as $file) {
                            if (isset($package['install-as'][$file])) {
                                $installas = $package['install-as'][$file];
                            } else {
                                $installas = $file;
                            }
                            $release[count($oses)]['filelist']['install'][] =
                                array(
                                    'attribs' => array(
                                        'name' => $file,
                                        'as' => $installas,
                                    )
                                );
                        }
                    }
                    if (count($genericIgnore)) {
                        foreach ($genericIgnore as $file) {
                            $release[count($oses)]['filelist']['ignore'][] =
                                array(
                                    'attribs' => array(
                                        'name' => $file,
                                    )
                                );
                        }
                    }
                }
                // cleanup
                foreach ($release as $i => $rel) {
                    if (isset($rel['filelist']['install']) &&
                          count($rel['filelist']['install']) == 1) {
                        $release[$i]['filelist']['install'] =
                            $release[$i]['filelist']['install'][0];
                    }
                    if (isset($rel['filelist']['ignore']) &&
                          count($rel['filelist']['ignore']) == 1) {
                        $release[$i]['filelist']['ignore'] =
                            $release[$i]['filelist']['ignore'][0];
                    }
                }
                if (count($release) == 1) {
                    $release = $release[0];
                }
            } else {
                // no platform atts, but some install-as atts
                foreach ($package['install-as'] as $file => $value) {
                    $release['filelist']['install'][] =
                        array(
                            'attribs' => array(
                                'name' => $file,
                                'as' => $value
                            )
                        );
                }
                if (count($release['filelist']['install']) == 1) {
                    $release['filelist']['install'] = $release['filelist']['install'][0];
                }
            }
        }
    }

    /**
     * @param array
     * @return array
     * @access private
     */
    function _processDep($dep)
    {
        if ($dep['type'] == 'php') {
            if ($dep['rel'] == 'has') {
                // come on - everyone has php!
                return false;
            }
        }
        $php = array();
        if ($dep['type'] != 'php') {
            $php['name'] = $dep['name'];
            if ($dep['type'] == 'pkg') {
                $php['channel'] = 'pear.php.net';
            }
        }
        switch ($dep['rel']) {
            case 'gt' :
                $php['min'] = $dep['version'];
                $php['exclude'] = $dep['version'];
            break;
            case 'ge' :
                if (!isset($dep['version'])) {
                    if ($dep['type'] == 'php') {
                        if (isset($dep['name'])) {
                            $dep['version'] = $dep['name'];
                        }
                    }
                }
                $php['min'] = $dep['version'];
            break;
            case 'lt' :
                $php['max'] = $dep['version'];
                $php['exclude'] = $dep['version'];
            break;
            case 'le' :
                $php['max'] = $dep['version'];
            break;
            case 'eq' :
                $php['min'] = $dep['version'];
                $php['max'] = $dep['version'];
            break;
            case 'ne' :
                $php['exclude'] = $dep['version'];
            break;
            case 'not' :
                $php['conflicts'] = 'yes';
            break;
        }
        return $php;
    }

    /**
     * @param array
     * @return array
     */
    function _processPhpDeps($deps)
    {
        $test = array();
        foreach ($deps as $dep) {
            $test[] = $this->_processDep($dep);
        }
        $min = array();
        $max = array();
        foreach ($test as $dep) {
            if (!$dep) {
                continue;
            }
            if (isset($dep['min'])) {
                $min[$dep['min']] = count($min);
            }
            if (isset($dep['max'])) {
                $max[$dep['max']] = count($max);
            }
        }
        if (count($min) > 0) {
            uksort($min, 'version_compare');
        }
        if (count($max) > 0) {
            uksort($max, 'version_compare');
        }
        if (count($min)) {
            // get the highest minimum
            $min = array_pop($a = array_flip($min));
        } else {
            $min = false;
        }
        if (count($max)) {
            // get the lowest maximum
            $max = array_shift($a = array_flip($max));
        } else {
            $max = false;
        }
        if ($min) {
            $php['min'] = $min;
        }
        if ($max) {
            $php['max'] = $max;
        }
        $exclude = array();
        foreach ($test as $dep) {
            if (!isset($dep['exclude'])) {
                continue;
            }
            $exclude[] = $dep['exclude'];
        }
        if (count($exclude)) {
            $php['exclude'] = $exclude;
        }
        return $php;
    }

    /**
     * process multiple dependencies that have a name, like package deps
     * @param array
     * @return array
     * @access private
     */
    function _processMultipleDepsName($deps)
    {
        $tests = array();
        foreach ($deps as $name => $dep) {
            foreach ($dep as $d) {
                $tests[$name][] = $this->_processDep($d);
            }
        }
        foreach ($tests as $name => $test) {
            $php = array();
            $min = array();
            $max = array();
            $php['name'] = $name;
            foreach ($test as $dep) {
                if (!$dep) {
                    continue;
                }
                if (isset($dep['channel'])) {
                    $php['channel'] = 'pear.php.net';
                }
                if (isset($dep['conflicts']) && $dep['conflicts'] == 'yes') {
                    $php['conflicts'] = 'yes';
                }
                if (isset($dep['min'])) {
                    $min[$dep['min']] = count($min);
                }
                if (isset($dep['max'])) {
                    $max[$dep['max']] = count($max);
                }
            }
            if (count($min) > 0) {
                uksort($min, 'version_compare');
            }
            if (count($max) > 0) {
                uksort($max, 'version_compare');
            }
            if (count($min)) {
                // get the highest minimum
                $min = array_pop($a = array_flip($min));
            } else {
                $min = false;
            }
            if (count($max)) {
                // get the lowest maximum
                $max = array_shift($a = array_flip($max));
            } else {
                $max = false;
            }
            if ($min) {
                $php['min'] = $min;
            }
            if ($max) {
                $php['max'] = $max;
            }
            $exclude = array();
            foreach ($test as $dep) {
                if (!isset($dep['exclude'])) {
                    continue;
                }
                $exclude[] = $dep['exclude'];
            }
            if (count($exclude)) {
                $php['exclude'] = $exclude;
            }
            $ret[] = $php;
        }
        return $ret;
    }
}
?>PEAR-1.7.2/PEAR/PackageFile/Generator/v2.php100644   1750   1750      166634 11014312742  13415 <?php
/**
 * package.xml generation class, package.xml version 2.0
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @author     Stephan Schmidt (original XML_Serializer code)
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: v2.php,v 1.39 2008/05/13 05:29:24 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * file/dir manipulation routines
 */
require_once 'System.php';
/**
 * This class converts a PEAR_PackageFile_v2 object into any output format.
 *
 * Supported output formats include array, XML string (using S. Schmidt's
 * XML_Serializer, slightly customized)
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @author     Stephan Schmidt (original XML_Serializer code)
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_PackageFile_Generator_v2
{
   /**
    * default options for the serialization
    * @access private
    * @var array $_defaultOptions
    */
    var $_defaultOptions = array(
                         'indent'             => ' ',                    // string used for indentation
                         'linebreak'          => "\n",                  // string used for newlines
                         'typeHints'          => false,                 // automatically add type hin attributes
                         'addDecl'            => true,                 // add an XML declaration
                         'defaultTagName'     => 'XML_Serializer_Tag',  // tag used for indexed arrays or invalid names
                         'classAsTagName'     => false,                 // use classname for objects in indexed arrays
                         'keyAttribute'       => '_originalKey',        // attribute where original key is stored
                         'typeAttribute'      => '_type',               // attribute for type (only if typeHints => true)
                         'classAttribute'     => '_class',              // attribute for class of objects (only if typeHints => true)
                         'scalarAsAttributes' => false,                 // scalar values (strings, ints,..) will be serialized as attribute
                         'prependAttributes'  => '',                    // prepend string for attributes
                         'indentAttributes'   => false,                 // indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column
                         'mode'               => 'simplexml',             // use 'simplexml' to use parent name as tagname if transforming an indexed array
                         'addDoctype'         => false,                 // add a doctype declaration
                         'doctype'            => null,                  // supply a string or an array with id and uri ({@see PEAR_PackageFile_Generator_v2_PEAR_PackageFile_Generator_v2_XML_Util::getDoctypeDeclaration()}
                         'rootName'           => 'package',                  // name of the root tag
                         'rootAttributes'     => array(
                             'version' => '2.0',
                             'xmlns' => 'http://pear.php.net/dtd/package-2.0',
                             'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
                             'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
                             'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0
http://pear.php.net/dtd/tasks-1.0.xsd
http://pear.php.net/dtd/package-2.0
http://pear.php.net/dtd/package-2.0.xsd',
                         ),               // attributes of the root tag
                         'attributesArray'    => 'attribs',                  // all values in this key will be treated as attributes
                         'contentName'        => '_content',                   // this value will be used directly as content, instead of creating a new tag, may only be used in conjuction with attributesArray
                         'beautifyFilelist'   => false,
                         'encoding' => 'UTF-8',
                        );

   /**
    * options for the serialization
    * @access private
    * @var array $options
    */
    var $options = array();

   /**
    * current tag depth
    * @var integer $_tagDepth
    */
    var $_tagDepth = 0;

   /**
    * serilialized representation of the data
    * @var string $_serializedData
    */
    var $_serializedData = null;
    /**
     * @var PEAR_PackageFile_v2
     */
    var $_packagefile;
    /**
     * @param PEAR_PackageFile_v2
     */
    function PEAR_PackageFile_Generator_v2(&$packagefile)
    {
        $this->_packagefile = &$packagefile;
    }

    /**
     * @return string
     */
    function getPackagerVersion()
    {
        return '1.7.2';
    }

    /**
     * @param PEAR_Packager
     * @param bool generate a .tgz or a .tar
     * @param string|null temporary directory to package in
     */
    function toTgz(&$packager, $compress = true, $where = null)
    {
        $a = null;
        return $this->toTgz2($packager, $a, $compress, $where);
    }

    /**
     * Package up both a package.xml and package2.xml for the same release
     * @param PEAR_Packager
     * @param PEAR_PackageFile_v1
     * @param bool generate a .tgz or a .tar
     * @param string|null temporary directory to package in
     */
    function toTgz2(&$packager, &$pf1, $compress = true, $where = null)
    {
        require_once 'Archive/Tar.php';
        if (!$this->_packagefile->isEquivalent($pf1)) {
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' .
                basename($pf1->getPackageFile()) .
                '" is not equivalent to "' . basename($this->_packagefile->getPackageFile())
                . '"');
        }
        if ($where === null) {
            if (!($where = System::mktemp(array('-d')))) {
                return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: mktemp failed');
            }
        } elseif (!@System::mkDir(array('-p', $where))) {
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' . $where . '" could' .
                ' not be created');
        }
        if (file_exists($where . DIRECTORY_SEPARATOR . 'package.xml') &&
              !is_file($where . DIRECTORY_SEPARATOR . 'package.xml')) {
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: unable to save package.xml as' .
                ' "' . $where . DIRECTORY_SEPARATOR . 'package.xml"');
        }
        if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) {
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: invalid package.xml');
        }
        $ext = $compress ? '.tgz' : '.tar';
        $pkgver = $this->_packagefile->getPackage() . '-' . $this->_packagefile->getVersion();
        $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext;
        if (file_exists(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext) &&
              !is_file(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext)) {
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: cannot create tgz file "' .
                getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext . '"');
        }
        if ($pkgfile = $this->_packagefile->getPackageFile()) {
            $pkgdir = dirname(realpath($pkgfile));
            $pkgfile = basename($pkgfile);
        } else {
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: package file object must ' .
                'be created from a real file');
        }
        // {{{ Create the package file list
        $filelist = array();
        $i = 0;
        $this->_packagefile->flattenFilelist();
        $contents = $this->_packagefile->getContents();
        if (isset($contents['bundledpackage'])) { // bundles of packages
            $contents = $contents['bundledpackage'];
            if (!isset($contents[0])) {
                $contents = array($contents);
            }
            $packageDir = $where;
            foreach ($contents as $i => $package) {
                $fname = $package;
                $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
                if (!file_exists($file)) {
                    return $packager->raiseError("File does not exist: $fname");
                }
                $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname;
                System::mkdir(array('-p', dirname($tfile)));
                copy($file, $tfile);
                $filelist[$i++] = $tfile;
                $packager->log(2, "Adding package $fname");
            }
        } else { // normal packages
            $contents = $contents['dir']['file'];
            if (!isset($contents[0])) {
                $contents = array($contents);
            }
    
            $packageDir = $where;
            foreach ($contents as $i => $file) {
                $fname = $file['attribs']['name'];
                $atts = $file['attribs'];
                $orig = $file;
                $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
                if (!file_exists($file)) {
                    return $packager->raiseError("File does not exist: $fname");
                } else {
                    $origperms = fileperms($file);
                    $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname;
                    unset($orig['attribs']);
                    if (count($orig)) { // file with tasks
                        // run any package-time tasks
                        $contents = file_get_contents($file);
                        foreach ($orig as $tag => $raw) {
                            $tag = str_replace(
                                array($this->_packagefile->getTasksNs() . ':', '-'),
                                array('', '_'), $tag);
                            $task = "PEAR_Task_$tag";
                            $task = &new $task($this->_packagefile->_config,
                                $this->_packagefile->_logger,
                                PEAR_TASK_PACKAGE);
                            $task->init($raw, $atts, null);
                            $res = $task->startSession($this->_packagefile, $contents, $tfile);
                            if (!$res) {
                                continue; // skip this task
                            }
                            if (PEAR::isError($res)) {
                                return $res;
                            }
                            $contents = $res; // save changes
                            System::mkdir(array('-p', dirname($tfile)));
                            $wp = fopen($tfile, "wb");
                            fwrite($wp, $contents);
                            fclose($wp);
                        }
                    }
                    if (!file_exists($tfile)) {
                        System::mkdir(array('-p', dirname($tfile)));
                        copy($file, $tfile);
                    }
                    chmod($tfile, $origperms);
                    $filelist[$i++] = $tfile;
                    $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($tfile), $i - 1);
                    $packager->log(2, "Adding file $fname");
                }
            }
        }
            // }}}
        if ($pf1 !== null) {
            $name = 'package2.xml';
        } else {
            $name = 'package.xml';
        }
        $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, $name);
        if ($packagexml) {
            $tar =& new Archive_Tar($dest_package, $compress);
            $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors
            // ----- Creates with the package.xml file
            $ok = $tar->createModify(array($packagexml), '', $where);
            if (PEAR::isError($ok)) {
                return $packager->raiseError($ok);
            } elseif (!$ok) {
                return $packager->raiseError('PEAR_Packagefile_v2::toTgz(): adding ' . $name .
                    ' failed');
            }
            // ----- Add the content of the package
            if (!$tar->addModify($filelist, $pkgver, $where)) {
                return $packager->raiseError(
                    'PEAR_Packagefile_v2::toTgz(): tarball creation failed');
            }
            // add the package.xml version 1.0
            if ($pf1 !== null) {
                $pfgen = &$pf1->getDefaultGenerator();
                $packagexml1 = $pfgen->toPackageFile($where, PEAR_VALIDATE_PACKAGING,
                    'package.xml', true);
                if (!$tar->addModify(array($packagexml1), '', $where)) {
                    return $packager->raiseError(
                        'PEAR_Packagefile_v2::toTgz(): adding package.xml failed');
                }
            }
            return $dest_package;
        }
    }

    function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'package.xml')
    {
        if (!$this->_packagefile->validate($state)) {
            return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: invalid package.xml',
                null, null, null, $this->_packagefile->getValidationWarnings());
        }
        if ($where === null) {
            if (!($where = System::mktemp(array('-d')))) {
                return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: mktemp failed');
            }
        } elseif (!@System::mkDir(array('-p', $where))) {
            return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: "' . $where . '" could' .
                ' not be created');
        }
        $newpkgfile = $where . DIRECTORY_SEPARATOR . $name;
        $np = @fopen($newpkgfile, 'wb');
        if (!$np) {
            return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: unable to save ' .
               "$name as $newpkgfile");
        }
        fwrite($np, $this->toXml($state));
        fclose($np);
        return $newpkgfile;
    }

    function &toV2()
    {
        return $this->_packagefile;
    }

    /**
     * Return an XML document based on the package info (as returned
     * by the PEAR_Common::infoFrom* methods).
     *
     * @return string XML data
     */
    function toXml($state = PEAR_VALIDATE_NORMAL, $options = array())
    {
        $this->_packagefile->setDate(date('Y-m-d'));
        $this->_packagefile->setTime(date('H:i:s'));
        if (!$this->_packagefile->validate($state)) {
            return false;
        }
        if (is_array($options)) {
            $this->options = array_merge($this->_defaultOptions, $options);
        } else {
            $this->options = $this->_defaultOptions;
        }
        $arr = $this->_packagefile->getArray();
        if (isset($arr['filelist'])) {
            unset($arr['filelist']);
        }
        if (isset($arr['_lastversion'])) {
            unset($arr['_lastversion']);
        }
        if ($state ^ PEAR_VALIDATE_PACKAGING && !isset($arr['bundle'])) {
            $use = $this->_recursiveXmlFilelist($arr['contents']['dir']['file']);
            unset($arr['contents']['dir']['file']);
            if (isset($use['dir'])) {
                $arr['contents']['dir']['dir'] = $use['dir'];
            }
            if (isset($use['file'])) {
                $arr['contents']['dir']['file'] = $use['file'];
            }
            $this->options['beautifyFilelist'] = true;
        }
        $arr['attribs']['packagerversion'] = '1.7.2';
        if ($this->serialize($arr, $options)) {
            return $this->_serializedData . "\n";
        }
        return false;
    }


    function _recursiveXmlFilelist($list)
    {
        $dirs = array();
        if (isset($list['attribs'])) {
            $file = $list['attribs']['name'];
            unset($list['attribs']['name']);
            $attributes = $list['attribs'];
            $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes);
        } else {
            foreach ($list as $a) {
                $file = $a['attribs']['name'];
                $attributes = $a['attribs'];
                unset($a['attribs']);
                $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes, $a);
            }
        }
        $this->_formatDir($dirs);
        $this->_deFormat($dirs);
        return $dirs;
    }

    function _addDir(&$dirs, $dir, $file = null, $attributes = null, $tasks = null)
    {
        if (!$tasks) {
            $tasks = array();
        }
        if ($dir == array() || $dir == array('.')) {
            $dirs['file'][basename($file)] = $tasks;
            $attributes['name'] = basename($file);
            $dirs['file'][basename($file)]['attribs'] = $attributes;
            return;
        }
        $curdir = array_shift($dir);
        if (!isset($dirs['dir'][$curdir])) {
            $dirs['dir'][$curdir] = array();
        }
        $this->_addDir($dirs['dir'][$curdir], $dir, $file, $attributes, $tasks);
    }

    function _formatDir(&$dirs)
    {
        if (!count($dirs)) {
            return array();
        }
        $newdirs = array();
        if (isset($dirs['dir'])) {
            $newdirs['dir'] = $dirs['dir'];
        }
        if (isset($dirs['file'])) {
            $newdirs['file'] = $dirs['file'];
        }
        $dirs = $newdirs;
        if (isset($dirs['dir'])) {
            uksort($dirs['dir'], 'strnatcasecmp');
            foreach ($dirs['dir'] as $dir => $contents) {
                $this->_formatDir($dirs['dir'][$dir]);
            }
        }
        if (isset($dirs['file'])) {
            uksort($dirs['file'], 'strnatcasecmp');
        };
    }

    function _deFormat(&$dirs)
    {
        if (!count($dirs)) {
            return array();
        }
        $newdirs = array();
        if (isset($dirs['dir'])) {
            foreach ($dirs['dir'] as $dir => $contents) {
                $newdir = array();
                $newdir['attribs']['name'] = $dir;
                $this->_deFormat($contents);
                foreach ($contents as $tag => $val) {
                    $newdir[$tag] = $val;
                }
                $newdirs['dir'][] = $newdir;
            }
            if (count($newdirs['dir']) == 1) {
                $newdirs['dir'] = $newdirs['dir'][0];
            }
        }
        if (isset($dirs['file'])) {
            foreach ($dirs['file'] as $name => $file) {
                $newdirs['file'][] = $file;
            }
            if (count($newdirs['file']) == 1) {
                $newdirs['file'] = $newdirs['file'][0];
            }
        }
        $dirs = $newdirs;
    }

    /**
    * reset all options to default options
    *
    * @access   public
    * @see      setOption(), XML_Unserializer()
    */
    function resetOptions()
    {
        $this->options = $this->_defaultOptions;
    }

   /**
    * set an option
    *
    * You can use this method if you do not want to set all options in the constructor
    *
    * @access   public
    * @see      resetOption(), XML_Serializer()
    */
    function setOption($name, $value)
    {
        $this->options[$name] = $value;
    }
    
   /**
    * sets several options at once
    *
    * You can use this method if you do not want to set all options in the constructor
    *
    * @access   public
    * @see      resetOption(), XML_Unserializer(), setOption()
    */
    function setOptions($options)
    {
        $this->options = array_merge($this->options, $options);
    }

   /**
    * serialize data
    *
    * @access   public
    * @param    mixed    $data data to serialize
    * @return   boolean  true on success, pear error on failure
    */
    function serialize($data, $options = null)
    {
        // if options have been specified, use them instead
        // of the previously defined ones
        if (is_array($options)) {
            $optionsBak = $this->options;
            if (isset($options['overrideOptions']) && $options['overrideOptions'] == true) {
                $this->options = array_merge($this->_defaultOptions, $options);
            } else {
                $this->options = array_merge($this->options, $options);
            }
        }
        else {
            $optionsBak = null;
        }
        
        //  start depth is zero
        $this->_tagDepth = 0;

        $this->_serializedData = '';
        // serialize an array
        if (is_array($data)) {
            if (isset($this->options['rootName'])) {
                $tagName = $this->options['rootName'];
            } else {
                $tagName = 'array';
            }

            $this->_serializedData .= $this->_serializeArray($data, $tagName, $this->options['rootAttributes']);
        }
        
        // add doctype declaration
        if ($this->options['addDoctype'] === true) {
            $this->_serializedData = PEAR_PackageFile_Generator_v2_XML_Util::getDoctypeDeclaration($tagName, $this->options['doctype'])
                                   . $this->options['linebreak']
                                   . $this->_serializedData;
        }

        //  build xml declaration
        if ($this->options['addDecl']) {
            $atts = array();
            if (isset($this->options['encoding']) ) {
                $encoding = $this->options['encoding'];
            } else {
                $encoding = null;
            }
            $this->_serializedData = PEAR_PackageFile_Generator_v2_XML_Util::getXMLDeclaration('1.0', $encoding)
                                   . $this->options['linebreak']
                                   . $this->_serializedData;
        }
        
        
		if ($optionsBak !== null) {
			$this->options = $optionsBak;
		}
		
        return  true;
    }

   /**
    * get the result of the serialization
    *
    * @access public
    * @return string serialized XML
    */
    function getSerializedData()
    {
        if ($this->_serializedData == null ) {
            return  $this->raiseError('No serialized data available. Use XML_Serializer::serialize() first.', XML_SERIALIZER_ERROR_NO_SERIALIZATION);
        }
        return $this->_serializedData;
    }
    
   /**
    * serialize any value
    *
    * This method checks for the type of the value and calls the appropriate method
    *
    * @access private
    * @param  mixed     $value
    * @param  string    $tagName
    * @param  array     $attributes
    * @return string
    */
    function _serializeValue($value, $tagName = null, $attributes = array())
    {
        if (is_array($value)) {
            $xml = $this->_serializeArray($value, $tagName, $attributes);
        } elseif (is_object($value)) {
            $xml = $this->_serializeObject($value, $tagName);
        } else {
            $tag = array(
                          'qname'      => $tagName,
                          'attributes' => $attributes,
                          'content'    => $value
                        );
            $xml = $this->_createXMLTag($tag);
        }
        return $xml;
    }
    
   /**
    * serialize an array
    *
    * @access   private
    * @param    array   $array       array to serialize
    * @param    string  $tagName     name of the root tag
    * @param    array   $attributes  attributes for the root tag
    * @return   string  $string      serialized data
    * @uses     PEAR_PackageFile_Generator_v2_XML_Util::isValidName() to check, whether key has to be substituted
    */
    function _serializeArray(&$array, $tagName = null, $attributes = array())
    {
        $_content = null;
        
        /**
         * check for special attributes
         */
        if ($this->options['attributesArray'] !== null) {
            if (isset($array[$this->options['attributesArray']])) {
                $attributes = $array[$this->options['attributesArray']];
                unset($array[$this->options['attributesArray']]);
            }
            /**
             * check for special content
             */
            if ($this->options['contentName'] !== null) {
                if (isset($array[$this->options['contentName']])) {
                    $_content = $array[$this->options['contentName']];
                    unset($array[$this->options['contentName']]);
                }
            }
        }

        /*
        * if mode is set to simpleXML, check whether
        * the array is associative or indexed
        */
        if (is_array($array) && $this->options['mode'] == 'simplexml') {
            $indexed = true;
            if (!count($array)) {
                $indexed = false;
            }
            foreach ($array as $key => $val) {
                if (!is_int($key)) {
                    $indexed = false;
                    break;
                }
            }

            if ($indexed && $this->options['mode'] == 'simplexml') {
                $string = '';
                foreach ($array as $key => $val) {
                    if ($this->options['beautifyFilelist'] && $tagName == 'dir') {
                        if (!isset($this->_curdir)) {
                            $this->_curdir = '';
                        }
                        $savedir = $this->_curdir;
                        if (isset($val['attribs'])) {
                            if ($val['attribs']['name'] == '/') {
                                $this->_curdir = '/';
                            } else {
                                if ($this->_curdir == '/') {
                                    $this->_curdir = '';
                                }
                                $this->_curdir .= '/' . $val['attribs']['name'];
                            }
                        }
                    }
                    $string .= $this->_serializeValue( $val, $tagName, $attributes);
                    if ($this->options['beautifyFilelist'] && $tagName == 'dir') {
                        $string .= ' <!-- ' . $this->_curdir . ' -->';
                        if (empty($savedir)) {
                            unset($this->_curdir);
                        } else {
                            $this->_curdir = $savedir;
                        }
                    }
                    
                    $string .= $this->options['linebreak'];
        			//	do indentation
                    if ($this->options['indent']!==null && $this->_tagDepth>0) {
                        $string .= str_repeat($this->options['indent'], $this->_tagDepth);
                    }
                }
                return rtrim($string);
            }
        }
        
		if ($this->options['scalarAsAttributes'] === true) {
	        foreach ($array as $key => $value) {
				if (is_scalar($value) && (PEAR_PackageFile_Generator_v2_XML_Util::isValidName($key) === true)) {
					unset($array[$key]);
					$attributes[$this->options['prependAttributes'].$key] = $value;
				}
			}
		}

        // check for empty array => create empty tag
        if (empty($array)) {
            $tag = array(
                            'qname'      => $tagName,
                            'content'    => $_content,
                            'attributes' => $attributes
                        );

        } else {
            $this->_tagDepth++;
            $tmp = $this->options['linebreak'];
            foreach ($array as $key => $value) {
    			//	do indentation
                if ($this->options['indent']!==null && $this->_tagDepth>0) {
                    $tmp .= str_repeat($this->options['indent'], $this->_tagDepth);
                }
    
    			//	copy key
    			$origKey	=	$key;
    			//	key cannot be used as tagname => use default tag
                $valid = PEAR_PackageFile_Generator_v2_XML_Util::isValidName($key);
    	        if (PEAR::isError($valid)) {
    	            if ($this->options['classAsTagName'] && is_object($value)) {
    	                $key = get_class($value);
    	            } else {
            	        $key = $this->options['defaultTagName'];
    	            }
           	 	}
                $atts = array();
                if ($this->options['typeHints'] === true) {
                    $atts[$this->options['typeAttribute']] = gettype($value);
    				if ($key !== $origKey) {
    					$atts[$this->options['keyAttribute']] = (string)$origKey;
    				}
    
                }
                if ($this->options['beautifyFilelist'] && $key == 'dir') {
                    if (!isset($this->_curdir)) {
                        $this->_curdir = '';
                    }
                    $savedir = $this->_curdir;
                    if (isset($value['attribs'])) {
                        if ($value['attribs']['name'] == '/') {
                            $this->_curdir = '/';
                        } else {
                            $this->_curdir .= '/' . $value['attribs']['name'];
                        }
                    }
                }

                if (is_string($value) && $value && ($value{strlen($value) - 1} == "\n")) {
                    $value .= str_repeat($this->options['indent'], $this->_tagDepth);
                }
                $tmp .= $this->_createXMLTag(array(
                                                    'qname'      => $key,
                                                    'attributes' => $atts,
                                                    'content'    => $value )
                                            );
                if ($this->options['beautifyFilelist'] && $key == 'dir') {
                    if (isset($value['attribs'])) {
                        $tmp .= ' <!-- ' . $this->_curdir . ' -->';
                        if (empty($savedir)) {
                            unset($this->_curdir);
                        } else {
                            $this->_curdir = $savedir;
                        }
                    }
                }
                $tmp .= $this->options['linebreak'];
            }
            
            $this->_tagDepth--;
            if ($this->options['indent']!==null && $this->_tagDepth>0) {
                $tmp .= str_repeat($this->options['indent'], $this->_tagDepth);
            }
    
    		if (trim($tmp) === '') {
    			$tmp = null;
    		}
    		
            $tag = array(
                            'qname'      => $tagName,
                            'content'    => $tmp,
                            'attributes' => $attributes
                        );
        }
        if ($this->options['typeHints'] === true) {
            if (!isset($tag['attributes'][$this->options['typeAttribute']])) {
                $tag['attributes'][$this->options['typeAttribute']] = 'array';
            }
        }

        $string = $this->_createXMLTag($tag, false);
        return $string;
    }
  
   /**
    * create a tag from an array
    * this method awaits an array in the following format
    * array(
    *       'qname'        => $tagName,
    *       'attributes'   => array(),
    *       'content'      => $content,      // optional
    *       'namespace'    => $namespace     // optional
    *       'namespaceUri' => $namespaceUri  // optional
    *   )
    *
    * @access   private
    * @param    array   $tag tag definition
    * @param    boolean $replaceEntities whether to replace XML entities in content or not
    * @return   string  $string XML tag
    */
    function _createXMLTag( $tag, $replaceEntities = true )
    {
        if ($this->options['indentAttributes'] !== false) {
            $multiline = true;
            $indent    = str_repeat($this->options['indent'], $this->_tagDepth);

            if ($this->options['indentAttributes'] == '_auto') {
                $indent .= str_repeat(' ', (strlen($tag['qname'])+2));

            } else {
                $indent .= $this->options['indentAttributes'];
            }
        } else {
            $multiline = false;
            $indent    = false;
        }
    
        if (is_array($tag['content'])) {
            if (empty($tag['content'])) {
                $tag['content'] =   '';
            }
        } elseif(is_scalar($tag['content']) && (string)$tag['content'] == '') {
            $tag['content'] =   '';
        }
    
        if (is_scalar($tag['content']) || is_null($tag['content'])) {
            if ($this->options['encoding'] == 'UTF-8' &&
                  version_compare(phpversion(), '5.0.0', 'lt')) {
                $encoding = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_UTF8_XML;
            } else {
                $encoding = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML;
            }
            $tag = PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $this->options['linebreak'], $encoding);
        } elseif (is_array($tag['content'])) {
            $tag    =   $this->_serializeArray($tag['content'], $tag['qname'], $tag['attributes']);
        } elseif (is_object($tag['content'])) {
            $tag    =   $this->_serializeObject($tag['content'], $tag['qname'], $tag['attributes']);
        } elseif (is_resource($tag['content'])) {
            settype($tag['content'], 'string');
            $tag    =   PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray($tag, $replaceEntities);
        }
        return  $tag;
    }
}

// well, it's one way to do things without extra deps ...
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// | PHP Version 4                                                        |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2002 The PHP Group                                |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license,       |
// | that is bundled with this package in the file LICENSE, and is        |
// | available at through the world-wide-web at                           |
// | http://www.php.net/license/2_02.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: Stephan Schmidt <schst@php-tools.net>                       |
// +----------------------------------------------------------------------+
//
//    $Id: v2.php,v 1.39 2008/05/13 05:29:24 cellog Exp $

/**
 * error code for invalid chars in XML name
 */
define("PEAR_PackageFile_Generator_v2_XML_Util_ERROR_INVALID_CHARS", 51);

/**
 * error code for invalid chars in XML name
 */
define("PEAR_PackageFile_Generator_v2_XML_Util_ERROR_INVALID_START", 52);

/**
 * error code for non-scalar tag content
 */
define("PEAR_PackageFile_Generator_v2_XML_Util_ERROR_NON_SCALAR_CONTENT", 60);
    
/**
 * error code for missing tag name
 */
define("PEAR_PackageFile_Generator_v2_XML_Util_ERROR_NO_TAG_NAME", 61);
    
/**
 * replace XML entities
 */
define("PEAR_PackageFile_Generator_v2_XML_Util_REPLACE_ENTITIES", 1);

/**
 * embedd content in a CData Section
 */
define("PEAR_PackageFile_Generator_v2_XML_Util_CDATA_SECTION", 2);

/**
 * do not replace entitites
 */
define("PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_NONE", 0);

/**
 * replace all XML entitites
 * This setting will replace <, >, ", ' and &
 */
define("PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML", 1);

/**
 * replace only required XML entitites
 * This setting will replace <, " and &
 */
define("PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML_REQUIRED", 2);

/**
 * replace HTML entitites
 * @link    http://www.php.net/htmlentities
 */
define("PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_HTML", 3);

/**
 * replace all XML entitites, and encode from ISO-8859-1 to UTF-8
 * This setting will replace <, >, ", ' and &
 */
define("PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_UTF8_XML", 4);

/**
 * utility class for working with XML documents
 * 
 * customized version of XML_Util 0.6.0
 *
 * @category XML
 * @package  PEAR
 * @version  0.6.0
 * @author   Stephan Schmidt <schst@php.net>
 * @author   Gregory Beaver <cellog@php.net>
 */
class PEAR_PackageFile_Generator_v2_XML_Util {

   /**
    * return API version
    *
    * @access   public
    * @static
    * @return   string  $version API version
    */
    function apiVersion()
    {
        return "0.6";
    }

   /**
    * replace XML entities
    *
    * With the optional second parameter, you may select, which
    * entities should be replaced.
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * // replace XML entites:
    * $string = PEAR_PackageFile_Generator_v2_XML_Util::replaceEntities("This string contains < & >.");
    * </code>
    *
    * @access   public
    * @static
    * @param    string  string where XML special chars should be replaced
    * @param    integer setting for entities in attribute values (one of PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML, PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML_REQUIRED, PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_HTML)
    * @return   string  string with replaced chars
    */
    function replaceEntities($string, $replaceEntities = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML)
    {
        switch ($replaceEntities) {
            case PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_UTF8_XML:
                return strtr(utf8_encode($string),array(
                                          '&'  => '&amp;',
                                          '>'  => '&gt;',
                                          '<'  => '&lt;',
                                          '"'  => '&quot;',
                                          '\'' => '&apos;' ));
                break;
            case PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML:
                return strtr($string,array(
                                          '&'  => '&amp;',
                                          '>'  => '&gt;',
                                          '<'  => '&lt;',
                                          '"'  => '&quot;',
                                          '\'' => '&apos;' ));
                break;
            case PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML_REQUIRED:
                return strtr($string,array(
                                          '&'  => '&amp;',
                                          '<'  => '&lt;',
                                          '"'  => '&quot;' ));
                break;
            case PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_HTML:
                return htmlspecialchars($string);
                break;
        }
        return $string;
    }

   /**
    * build an xml declaration
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * // get an XML declaration:
    * $xmlDecl = PEAR_PackageFile_Generator_v2_XML_Util::getXMLDeclaration("1.0", "UTF-8", true);
    * </code>
    *
    * @access   public
    * @static
    * @param    string  $version     xml version
    * @param    string  $encoding    character encoding
    * @param    boolean $standAlone  document is standalone (or not)
    * @return   string  $decl xml declaration
    * @uses     PEAR_PackageFile_Generator_v2_XML_Util::attributesToString() to serialize the attributes of the XML declaration
    */
    function getXMLDeclaration($version = "1.0", $encoding = null, $standalone = null)
    {
        $attributes = array(
                            "version" => $version,
                           );
        // add encoding
        if ($encoding !== null) {
            $attributes["encoding"] = $encoding;
        }
        // add standalone, if specified
        if ($standalone !== null) {
            $attributes["standalone"] = $standalone ? "yes" : "no";
        }
        
        return sprintf("<?xml%s?>", PEAR_PackageFile_Generator_v2_XML_Util::attributesToString($attributes, false));
    }

   /**
    * build a document type declaration
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * // get a doctype declaration:
    * $xmlDecl = PEAR_PackageFile_Generator_v2_XML_Util::getDocTypeDeclaration("rootTag","myDocType.dtd");
    * </code>
    *
    * @access   public
    * @static
    * @param    string  $root         name of the root tag
    * @param    string  $uri          uri of the doctype definition (or array with uri and public id)
    * @param    string  $internalDtd  internal dtd entries   
    * @return   string  $decl         doctype declaration
    * @since    0.2
    */
    function getDocTypeDeclaration($root, $uri = null, $internalDtd = null)
    {
        if (is_array($uri)) {
            $ref = sprintf( ' PUBLIC "%s" "%s"', $uri["id"], $uri["uri"] );
        } elseif (!empty($uri)) {
            $ref = sprintf( ' SYSTEM "%s"', $uri );
        } else {
            $ref = "";
        }

        if (empty($internalDtd)) {
            return sprintf("<!DOCTYPE %s%s>", $root, $ref);
        } else {
            return sprintf("<!DOCTYPE %s%s [\n%s\n]>", $root, $ref, $internalDtd);
        }
    }

   /**
    * create string representation of an attribute list
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * // build an attribute string
    * $att = array(
    *              "foo"   =>  "bar",
    *              "argh"  =>  "tomato"
    *            );
    *
    * $attList = PEAR_PackageFile_Generator_v2_XML_Util::attributesToString($att);    
    * </code>
    *
    * @access   public
    * @static
    * @param    array         $attributes        attribute array
    * @param    boolean|array $sort              sort attribute list alphabetically, may also be an assoc array containing the keys 'sort', 'multiline', 'indent', 'linebreak' and 'entities'
    * @param    boolean       $multiline         use linebreaks, if more than one attribute is given
    * @param    string        $indent            string used for indentation of multiline attributes
    * @param    string        $linebreak         string used for linebreaks of multiline attributes
    * @param    integer       $entities          setting for entities in attribute values (one of PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_NONE, PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML, PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML_REQUIRED, PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_HTML)
    * @return   string                           string representation of the attributes
    * @uses     PEAR_PackageFile_Generator_v2_XML_Util::replaceEntities() to replace XML entities in attribute values
    * @todo     allow sort also to be an options array
    */
    function attributesToString($attributes, $sort = true, $multiline = false, $indent = '    ', $linebreak = "\n", $entities = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML)
    {
        /**
         * second parameter may be an array
         */
        if (is_array($sort)) {
            if (isset($sort['multiline'])) {
                $multiline = $sort['multiline'];
            }
            if (isset($sort['indent'])) {
                $indent = $sort['indent'];
            }
            if (isset($sort['linebreak'])) {
                $multiline = $sort['linebreak'];
            }
            if (isset($sort['entities'])) {
                $entities = $sort['entities'];
            }
            if (isset($sort['sort'])) {
                $sort = $sort['sort'];
            } else {
                $sort = true;
            }
        }
        $string = '';
        if (is_array($attributes) && !empty($attributes)) {
            if ($sort) {
                ksort($attributes);
            }
            if( !$multiline || count($attributes) == 1) {
                foreach ($attributes as $key => $value) {
                    if ($entities != PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_NONE) {
                        $value = PEAR_PackageFile_Generator_v2_XML_Util::replaceEntities($value, $entities);
                    }
                    $string .= ' '.$key.'="'.$value.'"';
                }
            } else {
                $first = true;
                foreach ($attributes as $key => $value) {
                    if ($entities != PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_NONE) {
                        $value = PEAR_PackageFile_Generator_v2_XML_Util::replaceEntities($value, $entities);
                    }
                    if ($first) {
                        $string .= " ".$key.'="'.$value.'"';
                        $first = false;
                    } else {
                        $string .= $linebreak.$indent.$key.'="'.$value.'"';
                    }
                }
            }
        }
        return $string;
    }

   /**
    * create a tag
    *
    * This method will call PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray(), which
    * is more flexible.
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * // create an XML tag:
    * $tag = PEAR_PackageFile_Generator_v2_XML_Util::createTag("myNs:myTag", array("foo" => "bar"), "This is inside the tag", "http://www.w3c.org/myNs#");
    * </code>
    *
    * @access   public
    * @static
    * @param    string  $qname             qualified tagname (including namespace)
    * @param    array   $attributes        array containg attributes
    * @param    mixed   $content
    * @param    string  $namespaceUri      URI of the namespace
    * @param    integer $replaceEntities   whether to replace XML special chars in content, embedd it in a CData section or none of both
    * @param    boolean $multiline         whether to create a multiline tag where each attribute gets written to a single line
    * @param    string  $indent            string used to indent attributes (_auto indents attributes so they start at the same column)
    * @param    string  $linebreak         string used for linebreaks
    * @param    string  $encoding          encoding that should be used to translate content
    * @return   string  $string            XML tag
    * @see      PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray()
    * @uses     PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray() to create the tag
    */
    function createTag($qname, $attributes = array(), $content = null, $namespaceUri = null, $replaceEntities = PEAR_PackageFile_Generator_v2_XML_Util_REPLACE_ENTITIES, $multiline = false, $indent = "_auto", $linebreak = "\n", $encoding = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML)
    {
        $tag = array(
                     "qname"      => $qname,
                     "attributes" => $attributes
                    );

        // add tag content
        if ($content !== null) {
            $tag["content"] = $content;
        }
        
        // add namespace Uri
        if ($namespaceUri !== null) {
            $tag["namespaceUri"] = $namespaceUri;
        }

        return PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $linebreak, $encoding);
    }

   /**
    * create a tag from an array
    * this method awaits an array in the following format
    * <pre>
    * array(
    *  "qname"        => $qname         // qualified name of the tag
    *  "namespace"    => $namespace     // namespace prefix (optional, if qname is specified or no namespace)
    *  "localpart"    => $localpart,    // local part of the tagname (optional, if qname is specified)
    *  "attributes"   => array(),       // array containing all attributes (optional)
    *  "content"      => $content,      // tag content (optional)
    *  "namespaceUri" => $namespaceUri  // namespaceUri for the given namespace (optional)
    *   )
    * </pre>
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * $tag = array(
    *           "qname"        => "foo:bar",
    *           "namespaceUri" => "http://foo.com",
    *           "attributes"   => array( "key" => "value", "argh" => "fruit&vegetable" ),
    *           "content"      => "I'm inside the tag",
    *            );
    * // creating a tag with qualified name and namespaceUri
    * $string = PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray($tag);
    * </code>
    *
    * @access   public
    * @static
    * @param    array   $tag               tag definition
    * @param    integer $replaceEntities   whether to replace XML special chars in content, embedd it in a CData section or none of both
    * @param    boolean $multiline         whether to create a multiline tag where each attribute gets written to a single line
    * @param    string  $indent            string used to indent attributes (_auto indents attributes so they start at the same column)
    * @param    string  $linebreak         string used for linebreaks
    * @return   string  $string            XML tag
    * @see      PEAR_PackageFile_Generator_v2_XML_Util::createTag()
    * @uses     PEAR_PackageFile_Generator_v2_XML_Util::attributesToString() to serialize the attributes of the tag
    * @uses     PEAR_PackageFile_Generator_v2_XML_Util::splitQualifiedName() to get local part and namespace of a qualified name
    */
    function createTagFromArray($tag, $replaceEntities = PEAR_PackageFile_Generator_v2_XML_Util_REPLACE_ENTITIES, $multiline = false, $indent = "_auto", $linebreak = "\n", $encoding = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML)
    {
        if (isset($tag["content"]) && !is_scalar($tag["content"])) {
            return PEAR_PackageFile_Generator_v2_XML_Util::raiseError( "Supplied non-scalar value as tag content", PEAR_PackageFile_Generator_v2_XML_Util_ERROR_NON_SCALAR_CONTENT );
        }

        if (!isset($tag['qname']) && !isset($tag['localPart'])) {
            return PEAR_PackageFile_Generator_v2_XML_Util::raiseError( 'You must either supply a qualified name (qname) or local tag name (localPart).', PEAR_PackageFile_Generator_v2_XML_Util_ERROR_NO_TAG_NAME );
        }

        // if no attributes hav been set, use empty attributes
        if (!isset($tag["attributes"]) || !is_array($tag["attributes"])) {
            $tag["attributes"] = array();
        }
        
        // qualified name is not given
        if (!isset($tag["qname"])) {
            // check for namespace
            if (isset($tag["namespace"]) && !empty($tag["namespace"])) {
                $tag["qname"] = $tag["namespace"].":".$tag["localPart"];
            } else {
                $tag["qname"] = $tag["localPart"];
            }
        // namespace URI is set, but no namespace
        } elseif (isset($tag["namespaceUri"]) && !isset($tag["namespace"])) {
            $parts = PEAR_PackageFile_Generator_v2_XML_Util::splitQualifiedName($tag["qname"]);
            $tag["localPart"] = $parts["localPart"];
            if (isset($parts["namespace"])) {
                $tag["namespace"] = $parts["namespace"];
            }
        }

        if (isset($tag["namespaceUri"]) && !empty($tag["namespaceUri"])) {
            // is a namespace given
            if (isset($tag["namespace"]) && !empty($tag["namespace"])) {
                $tag["attributes"]["xmlns:".$tag["namespace"]] = $tag["namespaceUri"];
            } else {
                // define this Uri as the default namespace
                $tag["attributes"]["xmlns"] = $tag["namespaceUri"];
            }
        }

        // check for multiline attributes
        if ($multiline === true) {
            if ($indent === "_auto") {
                $indent = str_repeat(" ", (strlen($tag["qname"])+2));
            }
        }
        
        // create attribute list
        $attList    =   PEAR_PackageFile_Generator_v2_XML_Util::attributesToString($tag["attributes"], true, $multiline, $indent, $linebreak );
        if (!isset($tag["content"]) || (string)$tag["content"] == '') {
            $tag    =   sprintf("<%s%s />", $tag["qname"], $attList);
        } else {
            if ($replaceEntities == PEAR_PackageFile_Generator_v2_XML_Util_REPLACE_ENTITIES) {
                $tag["content"] = PEAR_PackageFile_Generator_v2_XML_Util::replaceEntities($tag["content"], $encoding);
            } elseif ($replaceEntities == PEAR_PackageFile_Generator_v2_XML_Util_CDATA_SECTION) {
                $tag["content"] = PEAR_PackageFile_Generator_v2_XML_Util::createCDataSection($tag["content"]);
            }
            $tag    =   sprintf("<%s%s>%s</%s>", $tag["qname"], $attList, $tag["content"], $tag["qname"] );
        }        
        return  $tag;
    }

   /**
    * create a start element
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * // create an XML start element:
    * $tag = PEAR_PackageFile_Generator_v2_XML_Util::createStartElement("myNs:myTag", array("foo" => "bar") ,"http://www.w3c.org/myNs#");
    * </code>
    *
    * @access   public
    * @static
    * @param    string  $qname             qualified tagname (including namespace)
    * @param    array   $attributes        array containg attributes
    * @param    string  $namespaceUri      URI of the namespace
    * @param    boolean $multiline         whether to create a multiline tag where each attribute gets written to a single line
    * @param    string  $indent            string used to indent attributes (_auto indents attributes so they start at the same column)
    * @param    string  $linebreak         string used for linebreaks
    * @return   string  $string            XML start element
    * @see      PEAR_PackageFile_Generator_v2_XML_Util::createEndElement(), PEAR_PackageFile_Generator_v2_XML_Util::createTag()
    */
    function createStartElement($qname, $attributes = array(), $namespaceUri = null, $multiline = false, $indent = '_auto', $linebreak = "\n")
    {
        // if no attributes hav been set, use empty attributes
        if (!isset($attributes) || !is_array($attributes)) {
            $attributes = array();
        }
        
        if ($namespaceUri != null) {
            $parts = PEAR_PackageFile_Generator_v2_XML_Util::splitQualifiedName($qname);
        }

        // check for multiline attributes
        if ($multiline === true) {
            if ($indent === "_auto") {
                $indent = str_repeat(" ", (strlen($qname)+2));
            }
        }

        if ($namespaceUri != null) {
            // is a namespace given
            if (isset($parts["namespace"]) && !empty($parts["namespace"])) {
                $attributes["xmlns:".$parts["namespace"]] = $namespaceUri;
            } else {
                // define this Uri as the default namespace
                $attributes["xmlns"] = $namespaceUri;
            }
        }

        // create attribute list
        $attList    =   PEAR_PackageFile_Generator_v2_XML_Util::attributesToString($attributes, true, $multiline, $indent, $linebreak);
        $element    =   sprintf("<%s%s>", $qname, $attList);
        return  $element;
    }

   /**
    * create an end element
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * // create an XML start element:
    * $tag = PEAR_PackageFile_Generator_v2_XML_Util::createEndElement("myNs:myTag");
    * </code>
    *
    * @access   public
    * @static
    * @param    string  $qname             qualified tagname (including namespace)
    * @return   string  $string            XML end element
    * @see      PEAR_PackageFile_Generator_v2_XML_Util::createStartElement(), PEAR_PackageFile_Generator_v2_XML_Util::createTag()
    */
    function createEndElement($qname)
    {
        $element    =   sprintf("</%s>", $qname);
        return  $element;
    }
    
   /**
    * create an XML comment
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * // create an XML start element:
    * $tag = PEAR_PackageFile_Generator_v2_XML_Util::createComment("I am a comment");
    * </code>
    *
    * @access   public
    * @static
    * @param    string  $content           content of the comment
    * @return   string  $comment           XML comment
    */
    function createComment($content)
    {
        $comment    =   sprintf("<!-- %s -->", $content);
        return  $comment;
    }
    
   /**
    * create a CData section
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * // create a CData section
    * $tag = PEAR_PackageFile_Generator_v2_XML_Util::createCDataSection("I am content.");
    * </code>
    *
    * @access   public
    * @static
    * @param    string  $data              data of the CData section
    * @return   string  $string            CData section with content
    */
    function createCDataSection($data)
    {
        return  sprintf("<![CDATA[%s]]>", $data);
    }

   /**
    * split qualified name and return namespace and local part
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * // split qualified tag
    * $parts = PEAR_PackageFile_Generator_v2_XML_Util::splitQualifiedName("xslt:stylesheet");
    * </code>
    * the returned array will contain two elements:
    * <pre>
    * array(
    *       "namespace" => "xslt",
    *       "localPart" => "stylesheet"
    *      );
    * </pre>
    *
    * @access public
    * @static
    * @param  string    $qname      qualified tag name
    * @param  string    $defaultNs  default namespace (optional)
    * @return array     $parts      array containing namespace and local part
    */
    function splitQualifiedName($qname, $defaultNs = null)
    {
        if (strstr($qname, ':')) {
            $tmp = explode(":", $qname);
            return array(
                          "namespace" => $tmp[0],
                          "localPart" => $tmp[1]
                        );
        }
        return array(
                      "namespace" => $defaultNs,
                      "localPart" => $qname
                    );
    }

   /**
    * check, whether string is valid XML name
    *
    * <p>XML names are used for tagname, attribute names and various
    * other, lesser known entities.</p>
    * <p>An XML name may only consist of alphanumeric characters,
    * dashes, undescores and periods, and has to start with a letter
    * or an underscore.
    * </p>
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * // verify tag name
    * $result = PEAR_PackageFile_Generator_v2_XML_Util::isValidName("invalidTag?");
    * if (PEAR_PackageFile_Generator_v2_XML_Util::isError($result)) {
    *    print "Invalid XML name: " . $result->getMessage();
    * }
    * </code>
    *
    * @access  public
    * @static
    * @param   string  $string string that should be checked
    * @return  mixed   $valid  true, if string is a valid XML name, PEAR error otherwise
    * @todo    support for other charsets
    */
    function isValidName($string)
    {
        // check for invalid chars
        if (!preg_match("/^[[:alnum:]_\-.]\\z/", $string{0})) {
            return PEAR_PackageFile_Generator_v2_XML_Util::raiseError( "XML names may only start with letter or underscore", PEAR_PackageFile_Generator_v2_XML_Util_ERROR_INVALID_START );
        }
        
        // check for invalid chars
        if (!preg_match("/^([a-zA-Z_]([a-zA-Z0-9_\-\.]*)?:)?[a-zA-Z_]([a-zA-Z0-9_\-\.]+)?\\z/", $string)) {
            return PEAR_PackageFile_Generator_v2_XML_Util::raiseError( "XML names may only contain alphanumeric chars, period, hyphen, colon and underscores", PEAR_PackageFile_Generator_v2_XML_Util_ERROR_INVALID_CHARS );
         }
        // XML name is valid
        return true;
    }

   /**
    * replacement for PEAR_PackageFile_Generator_v2_XML_Util::raiseError
    *
    * Avoids the necessity to always require
    * PEAR.php
    *
    * @access   public
    * @param    string      error message
    * @param    integer     error code
    * @return   object PEAR_Error
    */
    function raiseError($msg, $code)
    {
        require_once 'PEAR.php';
        return PEAR::raiseError($msg, $code);
    }
}
?>PEAR-1.7.2/PEAR/PackageFile/Parser/v1.php100644   1750   1750       41075 11014312742  12671 <?php
/**
 * package.xml parsing class, package.xml version 1.0
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: v1.php,v 1.27 2008/01/03 20:55:16 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * package.xml abstraction class
 */
require_once 'PEAR/PackageFile/v1.php';
/**
 * Parser for package.xml version 1.0
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: @PEAR-VER@
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_PackageFile_Parser_v1
{
    var $_registry;
    var $_config;
    var $_logger;
    /**
     * BC hack to allow PEAR_Common::infoFromString() to sort of
     * work with the version 2.0 format - there's no filelist though
     * @param PEAR_PackageFile_v2
     */
    function fromV2($packagefile)
    {
        $info = $packagefile->getArray(true);
        $ret = new PEAR_PackageFile_v1;
        $ret->fromArray($info['old']);
    }

    function setConfig(&$c)
    {
        $this->_config = &$c;
        $this->_registry = &$c->getRegistry();
    }

    function setLogger(&$l)
    {
        $this->_logger = &$l;
    }

    /**
     * @param string contents of package.xml file, version 1.0
     * @return bool success of parsing
     */
    function &parse($data, $file, $archive = false)
    {
        if (!extension_loaded('xml')) {
            return PEAR::raiseError('Cannot create xml parser for parsing package.xml, no xml extension');
        }
        $xp = xml_parser_create();
        if (!$xp) {
            $a = &PEAR::raiseError('Cannot create xml parser for parsing package.xml');
            return $a;
        }
        xml_set_object($xp, $this);
        xml_set_element_handler($xp, '_element_start_1_0', '_element_end_1_0');
        xml_set_character_data_handler($xp, '_pkginfo_cdata_1_0');
        xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, false);

        $this->element_stack = array();
        $this->_packageInfo = array('provides' => array());
        $this->current_element = false;
        unset($this->dir_install);
        $this->_packageInfo['filelist'] = array();
        $this->filelist =& $this->_packageInfo['filelist'];
        $this->dir_names = array();
        $this->in_changelog = false;
        $this->d_i = 0;
        $this->cdata = '';
        $this->_isValid = true;

        if (!xml_parse($xp, $data, 1)) {
            $code = xml_get_error_code($xp);
            $line = xml_get_current_line_number($xp);
            xml_parser_free($xp);
            $a = &PEAR::raiseError(sprintf("XML error: %s at line %d",
                           $str = xml_error_string($code), $line), 2);
            return $a;
        }

        xml_parser_free($xp);

        $pf = new PEAR_PackageFile_v1;
        $pf->setConfig($this->_config);
        if (isset($this->_logger)) {
            $pf->setLogger($this->_logger);
        }
        $pf->setPackagefile($file, $archive);
        $pf->fromArray($this->_packageInfo);
        return $pf;
    }
    // {{{ _unIndent()

    /**
     * Unindent given string
     *
     * @param string $str The string that has to be unindented.
     * @return string
     * @access private
     */
    function _unIndent($str)
    {
        // remove leading newlines
        $str = preg_replace('/^[\r\n]+/', '', $str);
        // find whitespace at the beginning of the first line
        $indent_len = strspn($str, " \t");
        $indent = substr($str, 0, $indent_len);
        $data = '';
        // remove the same amount of whitespace from following lines
        foreach (explode("\n", $str) as $line) {
            if (substr($line, 0, $indent_len) == $indent) {
                $data .= substr($line, $indent_len) . "\n";
            } elseif (trim(substr($line, 0, $indent_len))) {
                $data .= ltrim($line);
            }
        }
        return $data;
    }

    // Support for package DTD v1.0:
    // {{{ _element_start_1_0()

    /**
     * XML parser callback for ending elements.  Used for version 1.0
     * packages.
     *
     * @param resource  $xp    XML parser resource
     * @param string    $name  name of ending element
     *
     * @return void
     *
     * @access private
     */
    function _element_start_1_0($xp, $name, $attribs)
    {
        array_push($this->element_stack, $name);
        $this->current_element = $name;
        $spos = sizeof($this->element_stack) - 2;
        $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : '';
        $this->current_attributes = $attribs;
        $this->cdata = '';
        switch ($name) {
            case 'dir':
                if ($this->in_changelog) {
                    break;
                }
                if (array_key_exists('name', $attribs) && $attribs['name'] != '/') {
                    $attribs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
                        $attribs['name']);
                    if (strrpos($attribs['name'], '/') === strlen($attribs['name']) - 1) {
                        $attribs['name'] = substr($attribs['name'], 0,
                            strlen($attribs['name']) - 1);
                    }
                    if (strpos($attribs['name'], '/') === 0) {
                        $attribs['name'] = substr($attribs['name'], 1);
                    }
                    $this->dir_names[] = $attribs['name'];
                }
                if (isset($attribs['baseinstalldir'])) {
                    $this->dir_install = $attribs['baseinstalldir'];
                }
                if (isset($attribs['role'])) {
                    $this->dir_role = $attribs['role'];
                }
                break;
            case 'file':
                if ($this->in_changelog) {
                    break;
                }
                if (isset($attribs['name'])) {
                    $path = '';
                    if (count($this->dir_names)) {
                        foreach ($this->dir_names as $dir) {
                            $path .= $dir . '/';
                        }
                    }
                    $path .= preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
                        $attribs['name']);
                    unset($attribs['name']);
                    $this->current_path = $path;
                    $this->filelist[$path] = $attribs;
                    // Set the baseinstalldir only if the file don't have this attrib
                    if (!isset($this->filelist[$path]['baseinstalldir']) &&
                        isset($this->dir_install))
                    {
                        $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
                    }
                    // Set the Role
                    if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
                        $this->filelist[$path]['role'] = $this->dir_role;
                    }
                }
                break;
            case 'replace':
                if (!$this->in_changelog) {
                    $this->filelist[$this->current_path]['replacements'][] = $attribs;
                }
                break;
            case 'maintainers':
                $this->_packageInfo['maintainers'] = array();
                $this->m_i = 0; // maintainers array index
                break;
            case 'maintainer':
                // compatibility check
                if (!isset($this->_packageInfo['maintainers'])) {
                    $this->_packageInfo['maintainers'] = array();
                    $this->m_i = 0;
                }
                $this->_packageInfo['maintainers'][$this->m_i] = array();
                $this->current_maintainer =& $this->_packageInfo['maintainers'][$this->m_i];
                break;
            case 'changelog':
                $this->_packageInfo['changelog'] = array();
                $this->c_i = 0; // changelog array index
                $this->in_changelog = true;
                break;
            case 'release':
                if ($this->in_changelog) {
                    $this->_packageInfo['changelog'][$this->c_i] = array();
                    $this->current_release = &$this->_packageInfo['changelog'][$this->c_i];
                } else {
                    $this->current_release = &$this->_packageInfo;
                }
                break;
            case 'deps':
                if (!$this->in_changelog) {
                    $this->_packageInfo['release_deps'] = array();
                }
                break;
            case 'dep':
                // dependencies array index
                if (!$this->in_changelog) {
                    $this->d_i++;
                    isset($attribs['type']) ? ($attribs['type'] = strtolower($attribs['type'])) : false;
                    $this->_packageInfo['release_deps'][$this->d_i] = $attribs;
                }
                break;
            case 'configureoptions':
                if (!$this->in_changelog) {
                    $this->_packageInfo['configure_options'] = array();
                }
                break;
            case 'configureoption':
                if (!$this->in_changelog) {
                    $this->_packageInfo['configure_options'][] = $attribs;
                }
                break;
            case 'provides':
                if (empty($attribs['type']) || empty($attribs['name'])) {
                    break;
                }
                $attribs['explicit'] = true;
                $this->_packageInfo['provides']["$attribs[type];$attribs[name]"] = $attribs;
                break;
            case 'package' :
                if (isset($attribs['version'])) {
                    $this->_packageInfo['xsdversion'] = trim($attribs['version']);
                } else {
                    $this->_packageInfo['xsdversion'] = '1.0';
                }
                if (isset($attribs['packagerversion'])) {
                    $this->_packageInfo['packagerversion'] = $attribs['packagerversion'];
                }
                break;
        }
    }

    // }}}
    // {{{ _element_end_1_0()

    /**
     * XML parser callback for ending elements.  Used for version 1.0
     * packages.
     *
     * @param resource  $xp    XML parser resource
     * @param string    $name  name of ending element
     *
     * @return void
     *
     * @access private
     */
    function _element_end_1_0($xp, $name)
    {
        $data = trim($this->cdata);
        switch ($name) {
            case 'name':
                switch ($this->prev_element) {
                    case 'package':
                        $this->_packageInfo['package'] = $data;
                        break;
                    case 'maintainer':
                        $this->current_maintainer['name'] = $data;
                        break;
                }
                break;
            case 'extends' :
                $this->_packageInfo['extends'] = $data;
                break;
            case 'summary':
                $this->_packageInfo['summary'] = $data;
                break;
            case 'description':
                $data = $this->_unIndent($this->cdata);
                $this->_packageInfo['description'] = $data;
                break;
            case 'user':
                $this->current_maintainer['handle'] = $data;
                break;
            case 'email':
                $this->current_maintainer['email'] = $data;
                break;
            case 'role':
                $this->current_maintainer['role'] = $data;
                break;
            case 'version':
                //$data = ereg_replace ('[^a-zA-Z0-9._\-]', '_', $data);
                if ($this->in_changelog) {
                    $this->current_release['version'] = $data;
                } else {
                    $this->_packageInfo['version'] = $data;
                }
                break;
            case 'date':
                if ($this->in_changelog) {
                    $this->current_release['release_date'] = $data;
                } else {
                    $this->_packageInfo['release_date'] = $data;
                }
                break;
            case 'notes':
                // try to "de-indent" release notes in case someone
                // has been over-indenting their xml ;-)
                $data = $this->_unIndent($this->cdata);
                if ($this->in_changelog) {
                    $this->current_release['release_notes'] = $data;
                } else {
                    $this->_packageInfo['release_notes'] = $data;
                }
                break;
            case 'warnings':
                if ($this->in_changelog) {
                    $this->current_release['release_warnings'] = $data;
                } else {
                    $this->_packageInfo['release_warnings'] = $data;
                }
                break;
            case 'state':
                if ($this->in_changelog) {
                    $this->current_release['release_state'] = $data;
                } else {
                    $this->_packageInfo['release_state'] = $data;
                }
                break;
            case 'license':
                if ($this->in_changelog) {
                    $this->current_release['release_license'] = $data;
                } else {
                    $this->_packageInfo['release_license'] = $data;
                }
                break;
            case 'dep':
                if ($data && !$this->in_changelog) {
                    $this->_packageInfo['release_deps'][$this->d_i]['name'] = $data;
                }
                break;
            case 'dir':
                if ($this->in_changelog) {
                    break;
                }
                array_pop($this->dir_names);
                break;
            case 'file':
                if ($this->in_changelog) {
                    break;
                }
                if ($data) {
                    $path = '';
                    if (count($this->dir_names)) {
                        foreach ($this->dir_names as $dir) {
                            $path .= $dir . '/';
                        }
                    }
                    $path .= $data;
                    $this->filelist[$path] = $this->current_attributes;
                    // Set the baseinstalldir only if the file don't have this attrib
                    if (!isset($this->filelist[$path]['baseinstalldir']) &&
                        isset($this->dir_install))
                    {
                        $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
                    }
                    // Set the Role
                    if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
                        $this->filelist[$path]['role'] = $this->dir_role;
                    }
                }
                break;
            case 'maintainer':
                if (empty($this->_packageInfo['maintainers'][$this->m_i]['role'])) {
                    $this->_packageInfo['maintainers'][$this->m_i]['role'] = 'lead';
                }
                $this->m_i++;
                break;
            case 'release':
                if ($this->in_changelog) {
                    $this->c_i++;
                }
                break;
            case 'changelog':
                $this->in_changelog = false;
                break;
        }
        array_pop($this->element_stack);
        $spos = sizeof($this->element_stack) - 1;
        $this->current_element = ($spos > 0) ? $this->element_stack[$spos] : '';
        $this->cdata = '';
    }

    // }}}
    // {{{ _pkginfo_cdata_1_0()

    /**
     * XML parser callback for character data.  Used for version 1.0
     * packages.
     *
     * @param resource  $xp    XML parser resource
     * @param string    $name  character data
     *
     * @return void
     *
     * @access private
     */
    function _pkginfo_cdata_1_0($xp, $data)
    {
        if (isset($this->cdata)) {
            $this->cdata .= $data;
        }
    }

    // }}}
}
?>PEAR-1.7.2/PEAR/PackageFile/Parser/v2.php100644   1750   1750        6702 11014312742  12650 <?php
/**
 * package.xml parsing class, package.xml version 2.0
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: v2.php,v 1.21 2008/01/03 20:26:37 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * base xml parser class
 */
require_once 'PEAR/XMLParser.php';
require_once 'PEAR/PackageFile/v2.php';
/**
 * Parser for package.xml version 2.0
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: @PEAR-VER@
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_PackageFile_Parser_v2 extends PEAR_XMLParser
{
    var $_config;
    var $_logger;
    var $_registry;

    function setConfig(&$c)
    {
        $this->_config = &$c;
        $this->_registry = &$c->getRegistry();
    }

    function setLogger(&$l)
    {
        $this->_logger = &$l;
    }
    /**
     * Unindent given string
     *
     * @param string $str The string that has to be unindented.
     * @return string
     * @access private
     */
    function _unIndent($str)
    {
        // remove leading newlines
        $str = preg_replace('/^[\r\n]+/', '', $str);
        // find whitespace at the beginning of the first line
        $indent_len = strspn($str, " \t");
        $indent = substr($str, 0, $indent_len);
        $data = '';
        // remove the same amount of whitespace from following lines
        foreach (explode("\n", $str) as $line) {
            if (substr($line, 0, $indent_len) == $indent) {
                $data .= substr($line, $indent_len) . "\n";
            } else {
                $data .= $line . "\n";
            }
        }
        return $data;
    }

    /**
     * post-process data
     *
     * @param string $data
     * @param string $element element name
     */
    function postProcess($data, $element)
    {
        if ($element == 'notes') {
            return trim($this->_unIndent($data));
        }
        return trim($data);
    }

    /**
     * @param string
     * @param string file name of the package.xml
     * @param string|false name of the archive this package.xml came from, if any
     * @param string class name to instantiate and return.  This must be PEAR_PackageFile_v2 or
     *               a subclass
     * @return PEAR_PackageFile_v2
     */
    function &parse($data, $file, $archive = false, $class = 'PEAR_PackageFile_v2')
    {
        if (PEAR::isError($err = parent::parse($data, $file))) {
            return $err;
        }
        $ret = new $class;
        $ret->setConfig($this->_config);
        if (isset($this->_logger)) {
            $ret->setLogger($this->_logger);
        }
        $ret->fromArray($this->_unserializedData);
        $ret->setPackagefile($file, $archive);
        return $ret;
    }
}
?>PEAR-1.7.2/PEAR/PackageFile/v2/rw.php100644   1750   1750      173744 11014312742  12117 <?php
/**
 * PEAR_PackageFile_v2, package.xml version 2.0, read/write version
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: rw.php,v 1.22 2008/01/18 22:47:49 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a8
 */
/**
 * For base class
 */
require_once 'PEAR/PackageFile/v2.php';
/**
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a8
 */
class PEAR_PackageFile_v2_rw extends PEAR_PackageFile_v2
{
    /**
     * @param string Extension name
     * @return bool success of operation
     */
    function setProvidesExtension($extension)
    {
        if (in_array($this->getPackageType(),
              array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
            if (!isset($this->_packageInfo['providesextension'])) {
                // ensure that the channel tag is set up in the right location
                $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
                    array('usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease',
                    'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
                    'bundle', 'changelog'),
                    $extension, 'providesextension');
            }
            $this->_packageInfo['providesextension'] = $extension;
            return true;
        }
        return false;
    }

    function setPackage($package)
    {
        $this->_isValid = 0;
        if (!isset($this->_packageInfo['attribs'])) {
            $this->_packageInfo = array_merge(array('attribs' => array(
                                 'version' => '2.0',
                                 'xmlns' => 'http://pear.php.net/dtd/package-2.0',
                                 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
                                 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
                                 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0
    http://pear.php.net/dtd/tasks-1.0.xsd
    http://pear.php.net/dtd/package-2.0
    http://pear.php.net/dtd/package-2.0.xsd',
                             )), $this->_packageInfo);
        }
        if (!isset($this->_packageInfo['name'])) {
            return $this->_packageInfo = array_merge(array('name' => $package),
                $this->_packageInfo);
        }
        $this->_packageInfo['name'] = $package;
    }

    /**
     * set this as a package.xml version 2.1
     * @access private
     */
    function _setPackageVersion2_1()
    {
        $info = array(
                                 'version' => '2.1',
                                 'xmlns' => 'http://pear.php.net/dtd/package-2.1',
                                 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
                                 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
                                 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0
    http://pear.php.net/dtd/tasks-1.0.xsd
    http://pear.php.net/dtd/package-2.1
    http://pear.php.net/dtd/package-2.1.xsd',
                             );
        if (!isset($this->_packageInfo['attribs'])) {
            $this->_packageInfo = array_merge(array('attribs' => $info), $this->_packageInfo);
        } else {
            $this->_packageInfo['attribs'] = $info;
        }
    }

    function setUri($uri)
    {
        unset($this->_packageInfo['channel']);
        $this->_isValid = 0;
        if (!isset($this->_packageInfo['uri'])) {
            // ensure that the uri tag is set up in the right location
            $this->_packageInfo = $this->_insertBefore($this->_packageInfo, 
                array('extends', 'summary', 'description', 'lead',
                'developer', 'contributor', 'helper', 'date', 'time', 'version',
                'stability', 'license', 'notes', 'contents', 'compatible',
                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
                'extbinrelease', 'bundle', 'changelog'), $uri, 'uri');
        }
        $this->_packageInfo['uri'] = $uri;
    }

    function setChannel($channel)
    {
        unset($this->_packageInfo['uri']);
        $this->_isValid = 0;
        if (!isset($this->_packageInfo['channel'])) {
            // ensure that the channel tag is set up in the right location
            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
                array('extends', 'summary', 'description', 'lead',
                'developer', 'contributor', 'helper', 'date', 'time', 'version',
                'stability', 'license', 'notes', 'contents', 'compatible',
                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
                'extbinrelease', 'bundle', 'changelog'), $channel, 'channel');
        }
        $this->_packageInfo['channel'] = $channel;
    }

    function setExtends($extends)
    {
        $this->_isValid = 0;
        if (!isset($this->_packageInfo['extends'])) {
            // ensure that the extends tag is set up in the right location
            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
                array('summary', 'description', 'lead',
                'developer', 'contributor', 'helper', 'date', 'time', 'version',
                'stability', 'license', 'notes', 'contents', 'compatible',
                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
                'extbinrelease', 'bundle', 'changelog'), $extends, 'extends');
        }
        $this->_packageInfo['extends'] = $extends;
    }

    function setSummary($summary)
    {
        $this->_isValid = 0;
        if (!isset($this->_packageInfo['summary'])) {
            // ensure that the summary tag is set up in the right location
            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
                array('description', 'lead',
                'developer', 'contributor', 'helper', 'date', 'time', 'version',
                'stability', 'license', 'notes', 'contents', 'compatible',
                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
                'extbinrelease', 'bundle', 'changelog'), $summary, 'summary');
        }
        $this->_packageInfo['summary'] = $summary;
    }

    function setDescription($desc)
    {
        $this->_isValid = 0;
        if (!isset($this->_packageInfo['description'])) {
            // ensure that the description tag is set up in the right location
            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
                array('lead',
                'developer', 'contributor', 'helper', 'date', 'time', 'version',
                'stability', 'license', 'notes', 'contents', 'compatible',
                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
                'extbinrelease', 'bundle', 'changelog'), $desc, 'description');
        }
        $this->_packageInfo['description'] = $desc;
    }

    /**
     * Adds a new maintainer - no checking of duplicates is performed, use
     * updatemaintainer for that purpose.
     */
    function addMaintainer($role, $handle, $name, $email, $active = 'yes')
    {
        if (!in_array($role, array('lead', 'developer', 'contributor', 'helper'))) {
            return false;
        }
        if (isset($this->_packageInfo[$role])) {
            if (!isset($this->_packageInfo[$role][0])) {
                $this->_packageInfo[$role] = array($this->_packageInfo[$role]);
            }
            $this->_packageInfo[$role][] =
                array(
                    'name' => $name,
                    'user' => $handle,
                    'email' => $email,
                    'active' => $active,
                );
        } else {
            $testarr = array('lead',
                    'developer', 'contributor', 'helper', 'date', 'time', 'version',
                    'stability', 'license', 'notes', 'contents', 'compatible',
                    'dependencies', 'providesextension', 'usesrole', 'usestask',
                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease',
                    'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog');
            foreach (array('lead', 'developer', 'contributor', 'helper') as $testrole) {
                array_shift($testarr);
                if ($role == $testrole) {
                    break;
                }
            }
            if (!isset($this->_packageInfo[$role])) {
                // ensure that the extends tag is set up in the right location
                $this->_packageInfo = $this->_insertBefore($this->_packageInfo, $testarr,
                    array(), $role);
            }
            $this->_packageInfo[$role] =
                array(
                    'name' => $name,
                    'user' => $handle,
                    'email' => $email,
                    'active' => $active,
                );
        }
        $this->_isValid = 0;
    }

    function updateMaintainer($newrole, $handle, $name, $email, $active = 'yes')
    {
        $found = false;
        foreach (array('lead', 'developer', 'contributor', 'helper') as $role) {
            if (!isset($this->_packageInfo[$role])) {
                continue;
            }
            $info = $this->_packageInfo[$role];
            if (!isset($info[0])) {
                if ($info['user'] == $handle) {
                    $found = true;
                    break;
                }
            }
            foreach ($info as $i => $maintainer) {
                if ($maintainer['user'] == $handle) {
                    $found = $i;
                    break 2;
                }
            }
        }
        if ($found === false) {
            return $this->addMaintainer($newrole, $handle, $name, $email, $active);
        }
        if ($found !== false) {
            if ($found === true) {
                unset($this->_packageInfo[$role]);
            } else {
                unset($this->_packageInfo[$role][$found]);
                $this->_packageInfo[$role] = array_values($this->_packageInfo[$role]);
            }
        }
        $this->addMaintainer($newrole, $handle, $name, $email, $active);
        $this->_isValid = 0;
    }

    function deleteMaintainer($handle)
    {
        $found = false;
        foreach (array('lead', 'developer', 'contributor', 'helper') as $role) {
            if (!isset($this->_packageInfo[$role])) {
                continue;
            }
            if (!isset($this->_packageInfo[$role][0])) {
                $this->_packageInfo[$role] = array($this->_packageInfo[$role]);
            }
            foreach ($this->_packageInfo[$role] as $i => $maintainer) {
                if ($maintainer['user'] == $handle) {
                    $found = $i;
                    break;
                }
            }
            if ($found !== false) {
                unset($this->_packageInfo[$role][$found]);
                if (!count($this->_packageInfo[$role]) && $role == 'lead') {
                    $this->_isValid = 0;
                }
                if (!count($this->_packageInfo[$role])) {
                    unset($this->_packageInfo[$role]);
                    return true;
                }
                $this->_packageInfo[$role] =
                    array_values($this->_packageInfo[$role]);
                if (count($this->_packageInfo[$role]) == 1) {
                    $this->_packageInfo[$role] = $this->_packageInfo[$role][0];
                }
                return true;
            }
            if (count($this->_packageInfo[$role]) == 1) {
                $this->_packageInfo[$role] = $this->_packageInfo[$role][0];
            }
        }
        return false;
    }

    function setReleaseVersion($version)
    {
        if (isset($this->_packageInfo['version']) &&
              isset($this->_packageInfo['version']['release'])) {
            unset($this->_packageInfo['version']['release']);
        }
        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $version, array(
            'version' => array('stability', 'license', 'notes', 'contents', 'compatible',
                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
                'extbinrelease', 'bundle', 'changelog'),
            'release' => array('api')));
        $this->_isValid = 0;
    }

    function setAPIVersion($version)
    {
        if (isset($this->_packageInfo['version']) &&
              isset($this->_packageInfo['version']['api'])) {
            unset($this->_packageInfo['version']['api']);
        }
        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $version, array(
            'version' => array('stability', 'license', 'notes', 'contents', 'compatible',
                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
                'extbinrelease', 'bundle', 'changelog'),
            'api' => array()));
        $this->_isValid = 0;
    }

    /**
     * snapshot|devel|alpha|beta|stable
     */
    function setReleaseStability($state)
    {
        if (isset($this->_packageInfo['stability']) &&
              isset($this->_packageInfo['stability']['release'])) {
            unset($this->_packageInfo['stability']['release']);
        }
        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $state, array(
            'stability' => array('license', 'notes', 'contents', 'compatible',
                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
                'extbinrelease', 'bundle', 'changelog'),
            'release' => array('api')));
        $this->_isValid = 0;
    }

    /**
     * @param devel|alpha|beta|stable
     */
    function setAPIStability($state)
    {
        if (isset($this->_packageInfo['stability']) &&
              isset($this->_packageInfo['stability']['api'])) {
            unset($this->_packageInfo['stability']['api']);
        }
        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $state, array(
            'stability' => array('license', 'notes', 'contents', 'compatible',
                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
                'extbinrelease', 'bundle', 'changelog'),
            'api' => array()));
        $this->_isValid = 0;
    }

    function setLicense($license, $uri = false, $filesource = false)
    {
        if (!isset($this->_packageInfo['license'])) {
            // ensure that the license tag is set up in the right location
            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
                array('notes', 'contents', 'compatible',
                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
                'extbinrelease', 'bundle', 'changelog'), 0, 'license');
        }
        if ($uri || $filesource) {
            $attribs = array();
            if ($uri) {
                $attribs['uri'] = $uri;
            }
            $uri = true; // for test below
            if ($filesource) {
                $attribs['filesource'] = $filesource;
            }
        }
        $license = $uri ? array('attribs' => $attribs, '_content' => $license) : $license;
        $this->_packageInfo['license'] = $license;
        $this->_isValid = 0;
    }

    function setNotes($notes)
    {
        $this->_isValid = 0;
        if (!isset($this->_packageInfo['notes'])) {
            // ensure that the notes tag is set up in the right location
            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
                array('contents', 'compatible',
                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
                'extbinrelease', 'bundle', 'changelog'), $notes, 'notes');
        }
        $this->_packageInfo['notes'] = $notes;
    }

    /**
     * This is only used at install-time, after all serialization
     * is over.
     * @param string file name
     * @param string installed path
     */
    function setInstalledAs($file, $path)
    {
        if ($path) {
            return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
        }
        unset($this->_packageInfo['filelist'][$file]['installed_as']);
    }

    /**
     * This is only used at install-time, after all serialization
     * is over.
     */
    function installedFile($file, $atts)
    {
        if (isset($this->_packageInfo['filelist'][$file])) {
            $this->_packageInfo['filelist'][$file] =
                array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']);
        } else {
            $this->_packageInfo['filelist'][$file] = $atts['attribs'];
        }
    }

    /**
     * Reset the listing of package contents
     * @param string base installation dir for the whole package, if any
     */
    function clearContents($baseinstall = false)
    {
        $this->_filesValid = false;
        $this->_isValid = 0;
        if (!isset($this->_packageInfo['contents'])) {
            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
                array('compatible',
                    'dependencies', 'providesextension', 'usesrole', 'usestask',
                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease',
                    'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
                    'bundle', 'changelog'), array(), 'contents');
        }
        if ($this->getPackageType() != 'bundle') {
            $this->_packageInfo['contents'] = 
                array('dir' => array('attribs' => array('name' => '/')));
            if ($baseinstall) {
                $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'] = $baseinstall;
            }
        } else {
            $this->_packageInfo['contents'] = array('bundledpackage' => array());
        }
    }

    /**
     * @param string relative path of the bundled package.
     */
    function addBundledPackage($path)
    {
        if ($this->getPackageType() != 'bundle') {
            return false;
        }
        $this->_filesValid = false;
        $this->_isValid = 0;
        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $path, array(
                'contents' => array('compatible', 'dependencies', 'providesextension',
                'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease',
                'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
                'bundle', 'changelog'),
                'bundledpackage' => array()));
    }

    /**
     * @param string file name
     * @param PEAR_Task_Common a read/write task
     */
    function addTaskToFile($filename, $task)
    {
        if (!method_exists($task, 'getXml')) {
            return false;
        }
        if (!method_exists($task, 'getName')) {
            return false;
        }
        if (!method_exists($task, 'validate')) {
            return false;
        }
        if (!$task->validate()) {
            return false;
        }
        if (!isset($this->_packageInfo['contents']['dir']['file'])) {
            return false;
        }
        $this->getTasksNs(); // discover the tasks namespace if not done already
        $files = $this->_packageInfo['contents']['dir']['file'];
        if (!isset($files[0])) {
            $files = array($files);
            $ind = false;
        } else {
            $ind = true;
        }
        foreach ($files as $i => $file) {
            if (isset($file['attribs'])) {
                if ($file['attribs']['name'] == $filename) {
                    if ($ind) {
                        $t = isset($this->_packageInfo['contents']['dir']['file'][$i]
                              ['attribs'][$this->_tasksNs .
                              ':' . $task->getName()]) ?
                              $this->_packageInfo['contents']['dir']['file'][$i]
                              ['attribs'][$this->_tasksNs .
                              ':' . $task->getName()] : false;
                        if ($t && !isset($t[0])) {
                            $this->_packageInfo['contents']['dir']['file'][$i]
                                [$this->_tasksNs . ':' . $task->getName()] = array($t);
                        }
                        $this->_packageInfo['contents']['dir']['file'][$i][$this->_tasksNs .
                            ':' . $task->getName()][] = $task->getXml();
                    } else {
                        $t = isset($this->_packageInfo['contents']['dir']['file']
                              ['attribs'][$this->_tasksNs .
                              ':' . $task->getName()]) ? $this->_packageInfo['contents']['dir']['file']
                              ['attribs'][$this->_tasksNs .
                              ':' . $task->getName()] : false;
                        if ($t && !isset($t[0])) {
                            $this->_packageInfo['contents']['dir']['file']
                                [$this->_tasksNs . ':' . $task->getName()] = array($t);
                        }
                        $this->_packageInfo['contents']['dir']['file'][$this->_tasksNs .
                            ':' . $task->getName()][] = $task->getXml();
                    }
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * @param string path to the file
     * @param string filename
     * @param array extra attributes
     */
    function addFile($dir, $file, $attrs)
    {
        if ($this->getPackageType() == 'bundle') {
            return false;
        }
        $this->_filesValid = false;
        $this->_isValid = 0;
        $dir = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $dir);
        if ($dir == '/' || $dir == '') {
            $dir = '';
        } else {
            $dir .= '/';
        }
        $attrs['name'] = $dir . $file;
        if (!isset($this->_packageInfo['contents'])) {
            // ensure that the contents tag is set up
            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
                array('compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask',
                'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease',
                'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
                'bundle', 'changelog'), array(), 'contents');
        }
        if (isset($this->_packageInfo['contents']['dir']['file'])) {
            if (!isset($this->_packageInfo['contents']['dir']['file'][0])) {
                $this->_packageInfo['contents']['dir']['file'] =
                    array($this->_packageInfo['contents']['dir']['file']);
            }
            $this->_packageInfo['contents']['dir']['file'][]['attribs'] = $attrs;
        } else {
            $this->_packageInfo['contents']['dir']['file']['attribs'] = $attrs;
        }
    }

    /**
     * @param string Dependent package name
     * @param string Dependent package's channel name
     * @param string minimum version of specified package that this release is guaranteed to be
     *               compatible with
     * @param string maximum version of specified package that this release is guaranteed to be
     *               compatible with
     * @param string versions of specified package that this release is not compatible with
     */
    function addCompatiblePackage($name, $channel, $min, $max, $exclude = false)
    {
        $this->_isValid = 0;
        $set = array(
            'name' => $name,
            'channel' => $channel,
            'min' => $min,
            'max' => $max,
        );
        if ($exclude) {
            $set['exclude'] = $exclude;
        }
        $this->_isValid = 0;
        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array(
                'compatible' => array('dependencies', 'providesextension', 'usesrole', 'usestask',
                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')
            ));
    }

    /**
     * Removes the <usesrole> tag entirely
     */
    function resetUsesrole()
    {
        if (isset($this->_packageInfo['usesrole'])) {
            unset($this->_packageInfo['usesrole']);
        }
    }

    /**
     * @param string
     * @param string package name or uri
     * @param string channel name if non-uri
     */
    function addUsesrole($role, $packageOrUri, $channel = false) {
        $set = array('role' => $role);
        if ($channel) {
            $set['package'] = $packageOrUri;
            $set['channel'] = $channel;
        } else {
            $set['uri'] = $packageOrUri;
        }
        $this->_isValid = 0;
        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array(
                'usesrole' => array('usestask', 'srcpackage', 'srcuri',
                    'phprelease', 'extsrcrelease', 'extbinrelease',
                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')
            ));
    }

    /**
     * Removes the <usestask> tag entirely
     */
    function resetUsestask()
    {
        if (isset($this->_packageInfo['usestask'])) {
            unset($this->_packageInfo['usestask']);
        }
    }


    /**
     * @param string
     * @param string package name or uri
     * @param string channel name if non-uri
     */
    function addUsestask($task, $packageOrUri, $channel = false) {
        $set = array('task' => $task);
        if ($channel) {
            $set['package'] = $packageOrUri;
            $set['channel'] = $channel;
        } else {
            $set['uri'] = $packageOrUri;
        }
        $this->_isValid = 0;
        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array(
                'usestask' => array('srcpackage', 'srcuri',
                    'phprelease', 'extsrcrelease', 'extbinrelease',
                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')
            ));
    }

    /**
     * Remove all compatible tags
     */
    function clearCompatible()
    {
        unset($this->_packageInfo['compatible']);
    }

    /**
     * Reset dependencies prior to adding new ones
     */
    function clearDeps()
    {
        if (!isset($this->_packageInfo['dependencies'])) {
            $this->_packageInfo = $this->_mergeTag($this->_packageInfo, array(),
                array(
                    'dependencies' => array('providesextension', 'usesrole', 'usestask',
                        'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
                        'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')));
        }
        $this->_packageInfo['dependencies'] = array();
    }

    /**
     * @param string minimum PHP version allowed
     * @param string maximum PHP version allowed
     * @param array $exclude incompatible PHP versions
     */
    function setPhpDep($min, $max = false, $exclude = false)
    {
        $this->_isValid = 0;
        $dep =
            array(
                'min' => $min,
            );
        if ($max) {
            $dep['max'] = $max;
        }
        if ($exclude) {
            if (count($exclude) == 1) {
                $exclude = $exclude[0];
            }
            $dep['exclude'] = $exclude;
        }
        if (isset($this->_packageInfo['dependencies']['required']['php'])) {
            $this->_stack->push(__FUNCTION__, 'warning', array('dep' =>
            $this->_packageInfo['dependencies']['required']['php']),
                'warning: PHP dependency already exists, overwriting');
            unset($this->_packageInfo['dependencies']['required']['php']);
        }
        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
            array(
                'dependencies' => array('providesextension', 'usesrole', 'usestask',
                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
                'required' => array('optional', 'group'),
                'php' => array('pearinstaller', 'package', 'subpackage', 'extension', 'os', 'arch')
            ));
        return true;
    }

    /**
     * @param string minimum allowed PEAR installer version
     * @param string maximum allowed PEAR installer version
     * @param string recommended PEAR installer version
     * @param array incompatible version of the PEAR installer
     */
    function setPearinstallerDep($min, $max = false, $recommended = false, $exclude = false)
    {
        $this->_isValid = 0;
        $dep =
            array(
                'min' => $min,
            );
        if ($max) {
            $dep['max'] = $max;
        }
        if ($recommended) {
            $dep['recommended'] = $recommended;
        }
        if ($exclude) {
            if (count($exclude) == 1) {
                $exclude = $exclude[0];
            }
            $dep['exclude'] = $exclude;
        }
        if (isset($this->_packageInfo['dependencies']['required']['pearinstaller'])) {
            $this->_stack->push(__FUNCTION__, 'warning', array('dep' =>
            $this->_packageInfo['dependencies']['required']['pearinstaller']),
                'warning: PEAR Installer dependency already exists, overwriting');
            unset($this->_packageInfo['dependencies']['required']['pearinstaller']);
        }
        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
            array(
                'dependencies' => array('providesextension', 'usesrole', 'usestask',
                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
                'required' => array('optional', 'group'),
                'pearinstaller' => array('package', 'subpackage', 'extension', 'os', 'arch')
            ));
    }

    /**
     * Mark a package as conflicting with this package
     * @param string package name
     * @param string package channel
     * @param string extension this package provides, if any
     * @param string|false minimum version required
     * @param string|false maximum version allowed
     * @param array|false versions to exclude from installation
     */
    function addConflictingPackageDepWithChannel($name, $channel,
                $providesextension = false, $min = false, $max = false, $exclude = false)
    {
        $this->_isValid = 0;
        $dep = $this->_constructDep($name, $channel, false, $min, $max, false,
            $exclude, $providesextension, false, true);
        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
            array(
                'dependencies' => array('providesextension', 'usesrole', 'usestask',
                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
                'required' => array('optional', 'group'),
                'package' => array('subpackage', 'extension', 'os', 'arch')
            ));
    }

    /**
     * Mark a package as conflicting with this package
     * @param string package name
     * @param string package channel
     * @param string extension this package provides, if any
     */
    function addConflictingPackageDepWithUri($name, $uri, $providesextension = false)
    {
        $this->_isValid = 0;
        $dep =
            array(
                'name' => $name,
                'uri' => $uri,
                'conflicts' => '',
            );
        if ($providesextension) {
            $dep['providesextension'] = $providesextension;
        }
        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
            array(
                'dependencies' => array('providesextension', 'usesrole', 'usestask',
                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
                'required' => array('optional', 'group'),
                'package' => array('subpackage', 'extension', 'os', 'arch')
            ));
    }

    function addDependencyGroup($name, $hint)
    {
        $this->_isValid = 0;
        $this->_packageInfo = $this->_mergeTag($this->_packageInfo,
            array('attribs' => array('name' => $name, 'hint' => $hint)),
            array(
                'dependencies' => array('providesextension', 'usesrole', 'usestask',
                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
                'group' => array(),
            ));
    }

    /**
     * @param string package name
     * @param string|false channel name, false if this is a uri
     * @param string|false uri name, false if this is a channel
     * @param string|false minimum version required
     * @param string|false maximum version allowed
     * @param string|false recommended installation version
     * @param array|false versions to exclude from installation
     * @param string extension this package provides, if any
     * @param bool if true, tells the installer to ignore the default optional dependency group
     *             when installing this package
     * @param bool if true, tells the installer to negate this dependency (conflicts)
     * @return array
     * @access private
     */
    function _constructDep($name, $channel, $uri, $min, $max, $recommended, $exclude,
                           $providesextension = false, $nodefault = false,
                           $conflicts = false)
    {
        $dep =
            array(
                'name' => $name,
            );
        if ($channel) {
            $dep['channel'] = $channel;
        } elseif ($uri) {
            $dep['uri'] = $uri;
        }
        if ($min) {
            $dep['min'] = $min;
        }
        if ($max) {
            $dep['max'] = $max;
        }
        if ($recommended) {
            $dep['recommended'] = $recommended;
        }
        if ($exclude) {
            if (is_array($exclude) && count($exclude) == 1) {
                $exclude = $exclude[0];
            }
            $dep['exclude'] = $exclude;
        }
        if ($conflicts) {
            $dep['conflicts'] = '';
        }
        if ($nodefault) {
            $dep['nodefault'] = '';
        }
        if ($providesextension) {
            $dep['providesextension'] = $providesextension;
        }
        return $dep;
    }

    /**
     * @param package|subpackage
     * @param string group name
     * @param string package name
     * @param string package channel
     * @param string minimum version
     * @param string maximum version
     * @param string recommended version
     * @param array|false optional excluded versions
     * @param string extension this package provides, if any
     * @param bool if true, tells the installer to ignore the default optional dependency group
     *             when installing this package
     * @return bool false if the dependency group has not been initialized with
     *              {@link addDependencyGroup()}, or a subpackage is added with
     *              a providesextension
     */
    function addGroupPackageDepWithChannel($type, $groupname, $name, $channel, $min = false,
                                      $max = false, $recommended = false, $exclude = false,
                                      $providesextension = false, $nodefault = false)
    {
        if ($type == 'subpackage' && $providesextension) {
            return false; // subpackages must be php packages
        }
        $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude,
            $providesextension, $nodefault);
        return $this->_addGroupDependency($type, $dep, $groupname);
    }

    /**
     * @param package|subpackage
     * @param string group name
     * @param string package name
     * @param string package uri
     * @param string extension this package provides, if any
     * @param bool if true, tells the installer to ignore the default optional dependency group
     *             when installing this package
     * @return bool false if the dependency group has not been initialized with
     *              {@link addDependencyGroup()}
     */
    function addGroupPackageDepWithURI($type, $groupname, $name, $uri, $providesextension = false,
                                       $nodefault = false)
    {
        if ($type == 'subpackage' && $providesextension) {
            return false; // subpackages must be php packages
        }
        $dep = $this->_constructDep($name, false, $uri, false, false, false, false,
            $providesextension, $nodefault);
        return $this->_addGroupDependency($type, $dep, $groupname);
    }

    /**
     * @param string group name (must be pre-existing)
     * @param string extension name
     * @param string minimum version allowed
     * @param string maximum version allowed
     * @param string recommended version
     * @param array incompatible versions
     */
    function addGroupExtensionDep($groupname, $name, $min = false, $max = false,
                                         $recommended = false, $exclude = false)
    {
        $this->_isValid = 0;
        $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude);
        return $this->_addGroupDependency('extension', $dep, $groupname);
    }

    /**
     * @param package|subpackage|extension
     * @param array dependency contents
     * @param string name of the dependency group to add this to
     * @return boolean
     * @access private
     */
    function _addGroupDependency($type, $dep, $groupname)
    {
        $arr = array('subpackage', 'extension');
        if ($type != 'package') {
            array_shift($arr);
        }
        if ($type == 'extension') {
            array_shift($arr);
        }
        if (!isset($this->_packageInfo['dependencies']['group'])) {
            return false;
        } else {
            if (!isset($this->_packageInfo['dependencies']['group'][0])) {
                if ($this->_packageInfo['dependencies']['group']['attribs']['name'] == $groupname) {
                    $this->_packageInfo['dependencies']['group'] = $this->_mergeTag(
                        $this->_packageInfo['dependencies']['group'], $dep,
                        array(
                            $type => $arr
                        ));
                    $this->_isValid = 0;
                    return true;
                } else {
                    return false;
                }
            } else {
                foreach ($this->_packageInfo['dependencies']['group'] as $i => $group) {
                    if ($group['attribs']['name'] == $groupname) {
                    $this->_packageInfo['dependencies']['group'][$i] = $this->_mergeTag(
                        $this->_packageInfo['dependencies']['group'][$i], $dep,
                        array(
                            $type => $arr
                        ));
                        $this->_isValid = 0;
                        return true;
                    }
                }
                return false;
            }
        }
    }

    /**
     * @param optional|required
     * @param string package name
     * @param string package channel
     * @param string minimum version
     * @param string maximum version
     * @param string recommended version
     * @param string extension this package provides, if any
     * @param bool if true, tells the installer to ignore the default optional dependency group
     *             when installing this package
     * @param array|false optional excluded versions
     */
    function addPackageDepWithChannel($type, $name, $channel, $min = false, $max = false,
                                      $recommended = false, $exclude = false,
                                      $providesextension = false, $nodefault = false)
    {
        if (!in_array($type, array('optional', 'required'), true)) {
            $type = 'required';
        }
        $this->_isValid = 0;
        $arr = array('optional', 'group');
        if ($type != 'required') {
            array_shift($arr);
        }
        $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude,
            $providesextension, $nodefault);
        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
            array(
                'dependencies' => array('providesextension', 'usesrole', 'usestask',
                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
                $type => $arr,
                'package' => array('subpackage', 'extension', 'os', 'arch')
            ));
    }

    /**
     * @param optional|required
     * @param string name of the package
     * @param string uri of the package
     * @param string extension this package provides, if any
     * @param bool if true, tells the installer to ignore the default optional dependency group
     *             when installing this package
     */
    function addPackageDepWithUri($type, $name, $uri, $providesextension = false,
                                  $nodefault = false)
    {
        $this->_isValid = 0;
        $arr = array('optional', 'group');
        if ($type != 'required') {
            array_shift($arr);
        }
        $dep = $this->_constructDep($name, false, $uri, false, false, false, false,
            $providesextension, $nodefault);
        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
            array(
                'dependencies' => array('providesextension', 'usesrole', 'usestask',
                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
                $type => $arr,
                'package' => array('subpackage', 'extension', 'os', 'arch')
            ));
    }

    /**
     * @param optional|required optional, required
     * @param string package name
     * @param string package channel
     * @param string minimum version
     * @param string maximum version
     * @param string recommended version
     * @param array incompatible versions
     * @param bool if true, tells the installer to ignore the default optional dependency group
     *             when installing this package
     */
    function addSubpackageDepWithChannel($type, $name, $channel, $min = false, $max = false,
                                         $recommended = false, $exclude = false,
                                         $nodefault = false)
    {
        $this->_isValid = 0;
        $arr = array('optional', 'group');
        if ($type != 'required') {
            array_shift($arr);
        }
        $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude,
            $nodefault);
        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
            array(
                'dependencies' => array('providesextension', 'usesrole', 'usestask',
                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
                $type => $arr,
                'subpackage' => array('extension', 'os', 'arch')
            ));
    }

    /**
     * @param optional|required optional, required
     * @param string package name
     * @param string package uri for download
     * @param bool if true, tells the installer to ignore the default optional dependency group
     *             when installing this package
     */
    function addSubpackageDepWithUri($type, $name, $uri, $nodefault = false)
    {
        $this->_isValid = 0;
        $arr = array('optional', 'group');
        if ($type != 'required') {
            array_shift($arr);
        }
        $dep = $this->_constructDep($name, false, $uri, false, false, false, false, $nodefault);
        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
            array(
                'dependencies' => array('providesextension', 'usesrole', 'usestask',
                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
                $type => $arr,
                'subpackage' => array('extension', 'os', 'arch')
            ));
    }

    /**
     * @param optional|required optional, required
     * @param string extension name
     * @param string minimum version
     * @param string maximum version
     * @param string recommended version
     * @param array incompatible versions
     */
    function addExtensionDep($type, $name, $min = false, $max = false, $recommended = false,
                             $exclude = false)
    {
        $this->_isValid = 0;
        $arr = array('optional', 'group');
        if ($type != 'required') {
            array_shift($arr);
        }
        $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude);
        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
            array(
                'dependencies' => array('providesextension', 'usesrole', 'usestask',
                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
                $type => $arr,
                'extension' => array('os', 'arch')
            ));
    }

    /**
     * @param string Operating system name
     * @param boolean true if this package cannot be installed on this OS
     */
    function addOsDep($name, $conflicts = false)
    {
        $this->_isValid = 0;
        $dep = array('name' => $name);
        if ($conflicts) {
            $dep['conflicts'] = '';
        }
        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
            array(
                'dependencies' => array('providesextension', 'usesrole', 'usestask',
                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
                'required' => array('optional', 'group'),
                'os' => array('arch')
            ));
    }

    /**
     * @param string Architecture matching pattern
     * @param boolean true if this package cannot be installed on this architecture
     */
    function addArchDep($pattern, $conflicts = false)
    {
        $this->_isValid = 0;
        $dep = array('pattern' => $pattern);
        if ($conflicts) {
            $dep['conflicts'] = '';
        }
        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
            array(
                'dependencies' => array('providesextension', 'usesrole', 'usestask',
                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
                'required' => array('optional', 'group'),
                'arch' => array()
            ));
    }

    /**
     * Set the kind of package, and erase all release tags
     *
     * - a php package is a PEAR-style package
     * - an extbin package is a PECL-style extension binary
     * - an extsrc package is a PECL-style source for a binary
     * - an zendextbin package is a PECL-style zend extension binary
     * - an zendextsrc package is a PECL-style source for a zend extension binary
     * - a bundle package is a collection of other pre-packaged packages
     * @param php|extbin|extsrc|zendextsrc|zendextbin|bundle
     * @return bool success
     */
    function setPackageType($type)
    {
        $this->_isValid = 0;
        if (!in_array($type, array('php', 'extbin', 'extsrc', 'zendextsrc',
                                   'zendextbin', 'bundle'))) {
            return false;
        }
        if (in_array($type, array('zendextsrc', 'zendextbin'))) {
            $this->_setPackageVersion2_1();
        }
        if ($type != 'bundle') {
            $type .= 'release';
        }
        foreach (array('phprelease', 'extbinrelease', 'extsrcrelease',
                       'zendextsrcrelease', 'zendextbinrelease', 'bundle') as $test) {
            unset($this->_packageInfo[$test]);
        }
        if (!isset($this->_packageInfo[$type])) {
            // ensure that the release tag is set up
            $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('changelog'),
                array(), $type);
        }
        $this->_packageInfo[$type] = array();
        return true;
    }

    /**
     * @return bool true if package type is set up
     */
    function addRelease()
    {
        if ($type = $this->getPackageType()) {
            if ($type != 'bundle') {
                $type .= 'release';
            }
            $this->_packageInfo = $this->_mergeTag($this->_packageInfo, array(),
                array($type => array('changelog')));
            return true;
        }
        return false;
    }

    /**
     * Get the current release tag in order to add to it
     * @param bool returns only releases that have installcondition if true
     * @return array|null
     */
    function &_getCurrentRelease($strict = true)
    {
        if ($p = $this->getPackageType()) {
            if ($strict) {
                if ($p == 'extsrc' || $p == 'zendextsrc') {
                    $a = null;
                    return $a;
                }
            }
            if ($p != 'bundle') {
                $p .= 'release';
            }
            if (isset($this->_packageInfo[$p][0])) {
                return $this->_packageInfo[$p][count($this->_packageInfo[$p]) - 1];
            } else {
                return $this->_packageInfo[$p];
            }
        } else {
            $a = null;
            return $a;
        }
    }

    /**
     * Add a file to the current release that should be installed under a different name
     * @param string <contents> path to file
     * @param string name the file should be installed as
     */
    function addInstallAs($path, $as)
    {
        $r = &$this->_getCurrentRelease();
        if ($r === null) {
            return false;
        }
        $this->_isValid = 0;
        $r = $this->_mergeTag($r, array('attribs' => array('name' => $path, 'as' => $as)),
            array(
                'filelist' => array(),
                'install' => array('ignore')
            ));
    }

    /**
     * Add a file to the current release that should be ignored
     * @param string <contents> path to file
     * @return bool success of operation
     */
    function addIgnore($path)
    {
        $r = &$this->_getCurrentRelease();
        if ($r === null) {
            return false;
        }
        $this->_isValid = 0;
        $r = $this->_mergeTag($r, array('attribs' => array('name' => $path)),
            array(
                'filelist' => array(),
                'ignore' => array()
            ));
    }

    /**
     * Add an extension binary package for this extension source code release
     *
     * Note that the package must be from the same channel as the extension source package
     * @param string
     */
    function addBinarypackage($package)
    {
        if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
            return false;
        }
        $r = &$this->_getCurrentRelease(false);
        if ($r === null) {
            return false;
        }
        $this->_isValid = 0;
        $r = $this->_mergeTag($r, $package,
            array(
                'binarypackage' => array('filelist'),
            ));
    }

    /**
     * Add a configureoption to an extension source package
     * @param string
     * @param string
     * @param string
     */
    function addConfigureOption($name, $prompt, $default = null)
    {
        if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
            return false;
        }
        $r = &$this->_getCurrentRelease(false);
        if ($r === null) {
            return false;
        }
        $opt = array('attribs' => array('name' => $name, 'prompt' => $prompt));
        if ($default !== null) {
            $opt['attribs']['default'] = $default;
        }
        $this->_isValid = 0;
        $r = $this->_mergeTag($r, $opt,
            array(
                'configureoption' => array('binarypackage', 'filelist'),
            ));
    }

    /**
     * Set an installation condition based on php version for the current release set
     * @param string minimum version
     * @param string maximum version
     * @param false|array incompatible versions of PHP
     */
    function setPhpInstallCondition($min, $max, $exclude = false)
    {
        $r = &$this->_getCurrentRelease();
        if ($r === null) {
            return false;
        }
        $this->_isValid = 0;
        if (isset($r['installconditions']['php'])) {
            unset($r['installconditions']['php']);
        }
        $dep = array('min' => $min, 'max' => $max);
        if ($exclude) {
            if (is_array($exclude) && count($exclude) == 1) {
                $exclude = $exclude[0];
            }
            $dep['exclude'] = $exclude;
        }
        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
            $r = $this->_mergeTag($r, $dep,
                array(
                    'installconditions' => array('configureoption', 'binarypackage',
                        'filelist'),
                    'php' => array('extension', 'os', 'arch')
                ));
        } else {
            $r = $this->_mergeTag($r, $dep,
                array(
                    'installconditions' => array('filelist'),
                    'php' => array('extension', 'os', 'arch')
                ));
        }
    }

    /**
     * @param optional|required optional, required
     * @param string extension name
     * @param string minimum version
     * @param string maximum version
     * @param string recommended version
     * @param array incompatible versions
     */
    function addExtensionInstallCondition($name, $min = false, $max = false, $recommended = false,
                                          $exclude = false)
    {
        $r = &$this->_getCurrentRelease();
        if ($r === null) {
            return false;
        }
        $this->_isValid = 0;
        $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude);
        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
            $r = $this->_mergeTag($r, $dep,
                array(
                    'installconditions' => array('configureoption', 'binarypackage',
                        'filelist'),
                    'extension' => array('os', 'arch')
                ));
        } else {
            $r = $this->_mergeTag($r, $dep,
                array(
                    'installconditions' => array('filelist'),
                    'extension' => array('os', 'arch')
                ));
        }
    }

    /**
     * Set an installation condition based on operating system for the current release set
     * @param string OS name
     * @param bool whether this OS is incompatible with the current release
     */
    function setOsInstallCondition($name, $conflicts = false)
    {
        $r = &$this->_getCurrentRelease();
        if ($r === null) {
            return false;
        }
        $this->_isValid = 0;
        if (isset($r['installconditions']['os'])) {
            unset($r['installconditions']['os']);
        }
        $dep = array('name' => $name);
        if ($conflicts) {
            $dep['conflicts'] = '';
        }
        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
            $r = $this->_mergeTag($r, $dep,
                array(
                    'installconditions' => array('configureoption', 'binarypackage',
                        'filelist'),
                    'os' => array('arch')
                ));
        } else {
            $r = $this->_mergeTag($r, $dep,
                array(
                    'installconditions' => array('filelist'),
                    'os' => array('arch')
                ));
        }
    }

    /**
     * Set an installation condition based on architecture for the current release set
     * @param string architecture pattern
     * @param bool whether this arch is incompatible with the current release
     */
    function setArchInstallCondition($pattern, $conflicts = false)
    {
        $r = &$this->_getCurrentRelease();
        if ($r === null) {
            return false;
        }
        $this->_isValid = 0;
        if (isset($r['installconditions']['arch'])) {
            unset($r['installconditions']['arch']);
        }
        $dep = array('pattern' => $pattern);
        if ($conflicts) {
            $dep['conflicts'] = '';
        }
        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
            $r = $this->_mergeTag($r, $dep,
                array(
                    'installconditions' => array('configureoption', 'binarypackage',
                        'filelist'),
                    'arch' => array()
                ));
        } else {
            $r = $this->_mergeTag($r, $dep,
                array(
                    'installconditions' => array('filelist'),
                    'arch' => array()
                ));
        }
    }

    /**
     * For extension binary releases, this is used to specify either the
     * static URI to a source package, or the package name and channel of the extsrc/zendextsrc
     * package it is based on.
     * @param string Package name, or full URI to source package (extsrc/zendextsrc type)
     */
    function setSourcePackage($packageOrUri)
    {
        $this->_isValid = 0;
        if (isset($this->_packageInfo['channel'])) {
            $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('phprelease',
                'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
                'bundle', 'changelog'),
                $packageOrUri, 'srcpackage');
        } else {
            $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('phprelease',
                'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
                'bundle', 'changelog'), $packageOrUri, 'srcuri');
        }
    }

    /**
     * Generate a valid change log entry from the current package.xml
     * @param string|false
     */
    function generateChangeLogEntry($notes = false)
    {
        return array(
            'version' => 
                array(
                    'release' => $this->getVersion('release'),
                    'api' => $this->getVersion('api'),
                    ),
            'stability' =>
                $this->getStability(),
            'date' => $this->getDate(),
            'license' => $this->getLicense(true),
            'notes' => $notes ? $notes : $this->getNotes()
            );
    }

    /**
     * @param string release version to set change log notes for
     * @param array output of {@link generateChangeLogEntry()}
     */
    function setChangelogEntry($releaseversion, $contents)
    {
        if (!isset($this->_packageInfo['changelog'])) {
            $this->_packageInfo['changelog']['release'] = $contents;
            return;
        }
        if (!isset($this->_packageInfo['changelog']['release'][0])) {
            if ($this->_packageInfo['changelog']['release']['version']['release'] == $releaseversion) {
                $this->_packageInfo['changelog']['release'] = array(
                    $this->_packageInfo['changelog']['release']);
            } else {
                $this->_packageInfo['changelog']['release'] = array(
                    $this->_packageInfo['changelog']['release']);
                return $this->_packageInfo['changelog']['release'][] = $contents;
            }
        }
        foreach($this->_packageInfo['changelog']['release'] as $index => $changelog) {
            if (isset($changelog['version']) &&
                  strnatcasecmp($changelog['version']['release'], $releaseversion) == 0) {
                $curlog = $index;
            }
        }
        if (isset($curlog)) {
            $this->_packageInfo['changelog']['release'][$curlog] = $contents;
        } else {
            $this->_packageInfo['changelog']['release'][] = $contents;
        }
    }

    /**
     * Remove the changelog entirely
     */
    function clearChangeLog()
    {
        unset($this->_packageInfo['changelog']);
    }
}
?>PEAR-1.7.2/PEAR/PackageFile/v2/Validator.php100644   1750   1750      246506 11014312742  13411 <?php
/**
 * PEAR_PackageFile_v2, package.xml version 2.0, read/write version
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Validator.php,v 1.106 2008/03/28 22:23:41 dufuz Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a8
 */
/**
 * Private validation class used by PEAR_PackageFile_v2 - do not use directly, its
 * sole purpose is to split up the PEAR/PackageFile/v2.php file to make it smaller
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a8
 * @access private
 */
class PEAR_PackageFile_v2_Validator
{
    /**
     * @var array
     */
    var $_packageInfo;
    /**
     * @var PEAR_PackageFile_v2
     */
    var $_pf;
    /**
     * @var PEAR_ErrorStack
     */
    var $_stack;
    /**
     * @var int
     */
    var $_isValid = 0;
    /**
     * @var int
     */
    var $_filesValid = 0;
    /**
     * @var int
     */
    var $_curState = 0;
    /**
     * @param PEAR_PackageFile_v2
     * @param int
     */
    function validate(&$pf, $state = PEAR_VALIDATE_NORMAL)
    {
        $this->_pf = &$pf;
        $this->_curState = $state;
        $this->_packageInfo = $this->_pf->getArray();
        $this->_isValid = $this->_pf->_isValid;
        $this->_filesValid = $this->_pf->_filesValid;
        $this->_stack = &$pf->_stack;
        $this->_stack->getErrors(true);
        if (($this->_isValid & $state) == $state) {
            return true;
        }
        if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) {
            return false;
        }
        if (!isset($this->_packageInfo['attribs']['version']) ||
              ($this->_packageInfo['attribs']['version'] != '2.0' &&
               $this->_packageInfo['attribs']['version'] != '2.1')
        ) {
            $this->_noPackageVersion();
        }
        $structure =
        array(
            'name',
            'channel|uri',
            '*extends', // can't be multiple, but this works fine
            'summary',
            'description',
            '+lead', // these all need content checks
            '*developer',
            '*contributor',
            '*helper',
            'date',
            '*time',
            'version',
            'stability',
            'license->?uri->?filesource',
            'notes',
            'contents', //special validation needed
            '*compatible',
            'dependencies', //special validation needed
            '*usesrole',
            '*usestask', // reserve these for 1.4.0a1 to implement
                         // this will allow a package.xml to gracefully say it
                         // needs a certain package installed in order to implement a role or task
            '*providesextension',
            '*srcpackage|*srcuri',
            '+phprelease|+extsrcrelease|+extbinrelease|' .
                '+zendextsrcrelease|+zendextbinrelease|bundle', //special validation needed
            '*changelog',
        );
        $test = $this->_packageInfo;
        if (isset($test['dependencies']) &&
              isset($test['dependencies']['required']) &&
              isset($test['dependencies']['required']['pearinstaller']) &&
              isset($test['dependencies']['required']['pearinstaller']['min']) &&
              version_compare('1.7.2',
                $test['dependencies']['required']['pearinstaller']['min'], '<')
        ) {
            $this->_pearVersionTooLow($test['dependencies']['required']['pearinstaller']['min']);
            return false;
        }
        // ignore post-installation array fields
        if (array_key_exists('filelist', $test)) {
            unset($test['filelist']);
        }
        if (array_key_exists('_lastmodified', $test)) {
            unset($test['_lastmodified']);
        }
        if (array_key_exists('#binarypackage', $test)) {
            unset($test['#binarypackage']);
        }
        if (array_key_exists('old', $test)) {
            unset($test['old']);
        }
        if (array_key_exists('_lastversion', $test)) {
            unset($test['_lastversion']);
        }
        if (!$this->_stupidSchemaValidate($structure, $test, '<package>')) {
            return false;
        }
        if (empty($this->_packageInfo['name'])) {
            $this->_tagCannotBeEmpty('name');
        }
        $test = isset($this->_packageInfo['uri']) ? 'uri' :'channel';
        if (empty($this->_packageInfo[$test])) {
            $this->_tagCannotBeEmpty($test);
        }
        if (is_array($this->_packageInfo['license']) &&
              (!isset($this->_packageInfo['license']['_content']) ||
              empty($this->_packageInfo['license']['_content']))) {
            $this->_tagCannotBeEmpty('license');
        } elseif (empty($this->_packageInfo['license'])) {
            $this->_tagCannotBeEmpty('license');
        }
        if (empty($this->_packageInfo['summary'])) {
            $this->_tagCannotBeEmpty('summary');
        }
        if (empty($this->_packageInfo['description'])) {
            $this->_tagCannotBeEmpty('description');
        }
        if (empty($this->_packageInfo['date'])) {
            $this->_tagCannotBeEmpty('date');
        }
        if (empty($this->_packageInfo['notes'])) {
            $this->_tagCannotBeEmpty('notes');
        }
        if (isset($this->_packageInfo['time']) && empty($this->_packageInfo['time'])) {
            $this->_tagCannotBeEmpty('time');
        }
        if (isset($this->_packageInfo['dependencies'])) {
            $this->_validateDependencies();
        }
        if (isset($this->_packageInfo['compatible'])) {
            $this->_validateCompatible();
        }
        if (!isset($this->_packageInfo['bundle'])) {
            if (empty($this->_packageInfo['contents'])) {
                $this->_tagCannotBeEmpty('contents');
            }
            if (!isset($this->_packageInfo['contents']['dir'])) {
                $this->_filelistMustContainDir('contents');
                return false;
            }
            if (isset($this->_packageInfo['contents']['file'])) {
                $this->_filelistCannotContainFile('contents');
                return false;
            }
        }
        $this->_validateMaintainers();
        $this->_validateStabilityVersion();
        $fail = false;
        if (array_key_exists('usesrole', $this->_packageInfo)) {
            $roles = $this->_packageInfo['usesrole'];
            if (!is_array($roles) || !isset($roles[0])) {
                $roles = array($roles);
            }
            foreach ($roles as $role) {
                if (!isset($role['role'])) {
                    $this->_usesroletaskMustHaveRoleTask('usesrole', 'role');
                    $fail = true;
                } else {
                    if (!isset($role['channel'])) {
                        if (!isset($role['uri'])) {
                            $this->_usesroletaskMustHaveChannelOrUri($role['role'], 'usesrole');
                            $fail = true;
                        }
                    } elseif (!isset($role['package'])) {
                        $this->_usesroletaskMustHavePackage($role['role'], 'usesrole');
                        $fail = true;
                    }
                }
            }
        }
        if (array_key_exists('usestask', $this->_packageInfo)) {
            $roles = $this->_packageInfo['usestask'];
            if (!is_array($roles) || !isset($roles[0])) {
                $roles = array($roles);
            }
            foreach ($roles as $role) {
                if (!isset($role['task'])) {
                    $this->_usesroletaskMustHaveRoleTask('usestask', 'task');
                    $fail = true;
                } else {
                    if (!isset($role['channel'])) {
                        if (!isset($role['uri'])) {
                            $this->_usesroletaskMustHaveChannelOrUri($role['task'], 'usestask');
                            $fail = true;
                        }
                    } elseif (!isset($role['package'])) {
                        $this->_usesroletaskMustHavePackage($role['task'], 'usestask');
                        $fail = true;
                    }
                }
            }
        }
        if ($fail) {
            return false;
        }
        $list = $this->_packageInfo['contents'];
        if (isset($list['dir']) && is_array($list['dir']) && isset($list['dir'][0])) {
            $this->_multipleToplevelDirNotAllowed();
            return $this->_isValid = 0;
        }
        $this->_validateFilelist();
        $this->_validateRelease();
        if (!$this->_stack->hasErrors()) {
            $chan = $this->_pf->_registry->getChannel($this->_pf->getChannel(), true);
            if (PEAR::isError($chan)) {
                $this->_unknownChannel($this->_pf->getChannel());
            } else {
                $valpack = $chan->getValidationPackage();
                // for channel validator packages, always use the default PEAR validator.
                // otherwise, they can't be installed or packaged
                $validator = $chan->getValidationObject($this->_pf->getPackage());
                if (!$validator) {
                    $this->_stack->push(__FUNCTION__, 'error',
                        array_merge(
                            array('channel' => $chan->getName(),
                                  'package' => $this->_pf->getPackage()),
                              $valpack
                            ),
                        'package "%channel%/%package%" cannot be properly validated without ' .
                        'validation package "%channel%/%name%-%version%"');
                    return $this->_isValid = 0;
                }
                $validator->setPackageFile($this->_pf);
                $validator->validate($state);
                $failures = $validator->getFailures();
                foreach ($failures['errors'] as $error) {
                    $this->_stack->push(__FUNCTION__, 'error', $error,
                        'Channel validator error: field "%field%" - %reason%');
                }
                foreach ($failures['warnings'] as $warning) {
                    $this->_stack->push(__FUNCTION__, 'warning', $warning,
                        'Channel validator warning: field "%field%" - %reason%');
                }
            }
        }
        $this->_pf->_isValid = $this->_isValid = !$this->_stack->hasErrors('error');
        if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$this->_filesValid) {
            if ($this->_pf->getPackageType() == 'bundle') {
                if ($this->_analyzeBundledPackages()) {
                    $this->_filesValid = $this->_pf->_filesValid = true;
                } else {
                    $this->_pf->_isValid = $this->_isValid = 0;
                }
            } else {
                if (!$this->_analyzePhpFiles()) {
                    $this->_pf->_isValid = $this->_isValid = 0;
                } else {
                    $this->_filesValid = $this->_pf->_filesValid = true;
                }
            }
        }
        if ($this->_isValid) {
            return $this->_pf->_isValid = $this->_isValid = $state;
        }
        return $this->_pf->_isValid = $this->_isValid = 0;
    }

    function _stupidSchemaValidate($structure, $xml, $root)
    {
        if (!is_array($xml)) {
            $xml = array();
        }
        $keys = array_keys($xml);
        reset($keys);
        $key = current($keys);
        while ($key == 'attribs' || $key == '_contents') {
            $key = next($keys);
        }
        $unfoundtags = $optionaltags = array();
        $ret = true;
        $mismatch = false;
        foreach ($structure as $struc) {
            if ($key) {
                $tag = $xml[$key];
            }
            $test = $this->_processStructure($struc);
            if (isset($test['choices'])) {
                $loose = true;
                foreach ($test['choices'] as $choice) {
                    if ($key == $choice['tag']) {
                        $key = next($keys);
                        while ($key == 'attribs' || $key == '_contents') {
                            $key = next($keys);
                        }
                        $unfoundtags = $optionaltags = array();
                        $mismatch = false;
                        if ($key && $key != $choice['tag'] && isset($choice['multiple'])) {
                            $unfoundtags[] = $choice['tag'];
                            $optionaltags[] = $choice['tag'];
                            if ($key) {
                                $mismatch = true;
                            }
                        }
                        $ret &= $this->_processAttribs($choice, $tag, $root);
                        continue 2;
                    } else {
                        $unfoundtags[] = $choice['tag'];
                        $mismatch = true;
                    }
                    if (!isset($choice['multiple']) || $choice['multiple'] != '*') {
                        $loose = false;
                    } else {
                        $optionaltags[] = $choice['tag'];
                    }
                }
                if (!$loose) {
                    $this->_invalidTagOrder($unfoundtags, $key, $root);
                    return false;
                }
            } else {
                if ($key != $test['tag']) {
                    if (isset($test['multiple']) && $test['multiple'] != '*') {
                        $unfoundtags[] = $test['tag'];
                        $this->_invalidTagOrder($unfoundtags, $key, $root);
                        return false;
                    } else {
                        if ($key) {
                            $mismatch = true;
                        }
                        $unfoundtags[] = $test['tag'];
                        $optionaltags[] = $test['tag'];
                    }
                    if (!isset($test['multiple'])) {
                        $this->_invalidTagOrder($unfoundtags, $key, $root);
                        return false;
                    }
                    continue;
                } else {
                    $unfoundtags = $optionaltags = array();
                    $mismatch = false;
                }
                $key = next($keys);
                while ($key == 'attribs' || $key == '_contents') {
                    $key = next($keys);
                }
                if ($key && $key != $test['tag'] && isset($test['multiple'])) {
                    $unfoundtags[] = $test['tag'];
                    $optionaltags[] = $test['tag'];
                    $mismatch = true;
                }
                $ret &= $this->_processAttribs($test, $tag, $root);
                continue;
            }
        }
        if (!$mismatch && count($optionaltags)) {
            // don't error out on any optional tags
            $unfoundtags = array_diff($unfoundtags, $optionaltags);
        }
        if (count($unfoundtags)) {
            $this->_invalidTagOrder($unfoundtags, $key, $root);
        } elseif ($key) {
            // unknown tags
            $this->_invalidTagOrder('*no tags allowed here*', $key, $root);
            while ($key = next($keys)) {
                $this->_invalidTagOrder('*no tags allowed here*', $key, $root);
            }
        }
        return $ret;
    }

    function _processAttribs($choice, $tag, $context)
    {
        if (isset($choice['attribs'])) {
            if (!is_array($tag)) {
                $tag = array($tag);
            }
            $tags = $tag;
            if (!isset($tags[0])) {
                $tags = array($tags);
            }
            $ret = true;
            foreach ($tags as $i => $tag) {
                if (!is_array($tag) || !isset($tag['attribs'])) {
                    foreach ($choice['attribs'] as $attrib) {
                        if ($attrib{0} != '?') {
                            $ret &= $this->_tagHasNoAttribs($choice['tag'],
                                $context);
                            continue 2;
                        }
                    }
                }
                foreach ($choice['attribs'] as $attrib) {
                    if ($attrib{0} != '?') {
                        if (!isset($tag['attribs'][$attrib])) {
                            $ret &= $this->_tagMissingAttribute($choice['tag'],
                                $attrib, $context);
                        }
                    }
                }
            }
            return $ret;
        }
        return true;
    }

    function _processStructure($key)
    {
        $ret = array();
        if (count($pieces = explode('|', $key)) > 1) {
            $ret['choices'] = array();
            foreach ($pieces as $piece) {
                $ret['choices'][] = $this->_processStructure($piece);
            }
            return $ret;
        }
        $multi = $key{0};
        if ($multi == '+' || $multi == '*') {
            $ret['multiple'] = $key{0};
            $key = substr($key, 1);
        }
        if (count($attrs = explode('->', $key)) > 1) {
            $ret['tag'] = array_shift($attrs);
            $ret['attribs'] = $attrs;
        } else {
            $ret['tag'] = $key;
        }
        return $ret;
    }

    function _validateStabilityVersion()
    {
        $structure = array('release', 'api');
        $a = $this->_stupidSchemaValidate($structure, $this->_packageInfo['version'], '<version>');
        $a &= $this->_stupidSchemaValidate($structure, $this->_packageInfo['stability'], '<stability>');
        if ($a) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                  $this->_packageInfo['version']['release'])) {
                $this->_invalidVersion('release', $this->_packageInfo['version']['release']);
            }
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                  $this->_packageInfo['version']['api'])) {
                $this->_invalidVersion('api', $this->_packageInfo['version']['api']);
            }
            if (!in_array($this->_packageInfo['stability']['release'],
                  array('snapshot', 'devel', 'alpha', 'beta', 'stable'))) {
                $this->_invalidState('release', $this->_packageInfo['stability']['release']);
            }
            if (!in_array($this->_packageInfo['stability']['api'],
                  array('devel', 'alpha', 'beta', 'stable'))) {
                $this->_invalidState('api', $this->_packageInfo['stability']['api']);
            }
        }
    }

    function _validateMaintainers()
    {
        $structure =
            array(
                'name',
                'user',
                'email',
                'active',
            );
        foreach (array('lead', 'developer', 'contributor', 'helper') as $type) {
            if (!isset($this->_packageInfo[$type])) {
                continue;
            }
            if (isset($this->_packageInfo[$type][0])) {
                foreach ($this->_packageInfo[$type] as $lead) {
                    $this->_stupidSchemaValidate($structure, $lead, '<' . $type . '>');
                }
            } else {
                $this->_stupidSchemaValidate($structure, $this->_packageInfo[$type],
                    '<' . $type . '>');
            }
        }
    }

    function _validatePhpDep($dep, $installcondition = false)
    {
        $structure = array(
            'min',
            '*max',
            '*exclude',
        );
        $type = $installcondition ? '<installcondition><php>' : '<dependencies><required><php>';
        $this->_stupidSchemaValidate($structure, $dep, $type);
        if (isset($dep['min'])) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
                  $dep['min'])) {
                $this->_invalidVersion($type . '<min>', $dep['min']);
            }
        }
        if (isset($dep['max'])) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
                  $dep['max'])) {
                $this->_invalidVersion($type . '<max>', $dep['max']);
            }
        }
        if (isset($dep['exclude'])) {
            if (!is_array($dep['exclude'])) {
                $dep['exclude'] = array($dep['exclude']);
            }
            foreach ($dep['exclude'] as $exclude) {
                if (!preg_match(
                     '/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
                     $exclude)) {
                    $this->_invalidVersion($type . '<exclude>', $exclude);
                }
            }
        }
    }

    function _validatePearinstallerDep($dep)
    {
        $structure = array(
            'min',
            '*max',
            '*recommended',
            '*exclude',
        );
        $this->_stupidSchemaValidate($structure, $dep, '<dependencies><required><pearinstaller>');
        if (isset($dep['min'])) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                  $dep['min'])) {
                $this->_invalidVersion('<dependencies><required><pearinstaller><min>',
                    $dep['min']);
            }
        }
        if (isset($dep['max'])) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                  $dep['max'])) {
                $this->_invalidVersion('<dependencies><required><pearinstaller><max>',
                    $dep['max']);
            }
        }
        if (isset($dep['recommended'])) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                  $dep['recommended'])) {
                $this->_invalidVersion('<dependencies><required><pearinstaller><recommended>',
                    $dep['recommended']);
            }
        }
        if (isset($dep['exclude'])) {
            if (!is_array($dep['exclude'])) {
                $dep['exclude'] = array($dep['exclude']);
            }
            foreach ($dep['exclude'] as $exclude) {
                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                      $exclude)) {
                    $this->_invalidVersion('<dependencies><required><pearinstaller><exclude>',
                        $exclude);
                }
            }
        }
    }

    function _validatePackageDep($dep, $group, $type = '<package>')
    {
        if (isset($dep['uri'])) {
            if (isset($dep['conflicts'])) {
                $structure = array(
                    'name',
                    'uri',
                    'conflicts',
                    '*providesextension',
                );
            } else {
                $structure = array(
                    'name',
                    'uri',
                    '*providesextension',
                );
            }
        } else {
            if (isset($dep['conflicts'])) {
                $structure = array(
                    'name',
                    'channel',
                    '*min',
                    '*max',
                    '*exclude',
                    'conflicts',
                    '*providesextension',
                );
            } else {
                $structure = array(
                    'name',
                    'channel',
                    '*min',
                    '*max',
                    '*recommended',
                    '*exclude',
                    '*nodefault',
                    '*providesextension',
                );
            }
        }
        if (isset($dep['name'])) {
            $type .= '<name>' . $dep['name'] . '</name>';
        }
        $this->_stupidSchemaValidate($structure, $dep, '<dependencies>' . $group . $type);
        if (isset($dep['uri']) && (isset($dep['min']) || isset($dep['max']) ||
              isset($dep['recommended']) || isset($dep['exclude']))) {
            $this->_uriDepsCannotHaveVersioning('<dependencies>' . $group . $type);
        }
        if (isset($dep['channel']) && strtolower($dep['channel']) == '__uri') {
            $this->_DepchannelCannotBeUri('<dependencies>' . $group . $type);
        }
        if (isset($dep['min'])) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                  $dep['min'])) {
                $this->_invalidVersion('<dependencies>' . $group . $type . '<min>', $dep['min']);
            }
        }
        if (isset($dep['max'])) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                  $dep['max'])) {
                $this->_invalidVersion('<dependencies>' . $group . $type . '<max>', $dep['max']);
            }
        }
        if (isset($dep['recommended'])) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                  $dep['recommended'])) {
                $this->_invalidVersion('<dependencies>' . $group . $type . '<recommended>',
                    $dep['recommended']);
            }
        }
        if (isset($dep['exclude'])) {
            if (!is_array($dep['exclude'])) {
                $dep['exclude'] = array($dep['exclude']);
            }
            foreach ($dep['exclude'] as $exclude) {
                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                      $exclude)) {
                    $this->_invalidVersion('<dependencies>' . $group . $type . '<exclude>',
                        $exclude);
                }
            }
        }
    }

    function _validateSubpackageDep($dep, $group)
    {
        $this->_validatePackageDep($dep, $group, '<subpackage>');
        if (isset($dep['providesextension'])) {
            $this->_subpackageCannotProvideExtension(isset($dep['name']) ? $dep['name'] : '');
        }
        if (isset($dep['conflicts'])) {
            $this->_subpackagesCannotConflict(isset($dep['name']) ? $dep['name'] : '');
        }
    }

    function _validateExtensionDep($dep, $group = false, $installcondition = false)
    {
        if (isset($dep['conflicts'])) {
            $structure = array(
                'name',
                '*min',
                '*max',
                '*exclude',
                'conflicts',
            );
        } else {
            $structure = array(
                'name',
                '*min',
                '*max',
                '*recommended',
                '*exclude',
            );
        }
        if ($installcondition) {
            $type = '<installcondition><extension>';
        } else {
            $type = '<dependencies>' . $group . '<extension>';
        }
        if (isset($dep['name'])) {
            $type .= '<name>' . $dep['name'] . '</name>';
        }
        $this->_stupidSchemaValidate($structure, $dep, $type);
        if (isset($dep['min'])) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                  $dep['min'])) {
                $this->_invalidVersion(substr($type, 1) . '<min', $dep['min']);
            }
        }
        if (isset($dep['max'])) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                  $dep['max'])) {
                $this->_invalidVersion(substr($type, 1) . '<max', $dep['max']);
            }
        }
        if (isset($dep['recommended'])) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                  $dep['recommended'])) {
                $this->_invalidVersion(substr($type, 1) . '<recommended', $dep['recommended']);
            }
        }
        if (isset($dep['exclude'])) {
            if (!is_array($dep['exclude'])) {
                $dep['exclude'] = array($dep['exclude']);
            }
            foreach ($dep['exclude'] as $exclude) {
                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                      $exclude)) {
                    $this->_invalidVersion(substr($type, 1) . '<exclude', $exclude);
                }
            }
        }
    }

    function _validateOsDep($dep, $installcondition = false)
    {
        $structure = array(
            'name',
            '*conflicts',
        );
        $type = $installcondition ? '<installcondition><os>' : '<dependencies><required><os>';
        if ($this->_stupidSchemaValidate($structure, $dep, $type)) {
            if ($dep['name'] == '*') {
                if (array_key_exists('conflicts', $dep)) {
                    $this->_cannotConflictWithAllOs($type);
                }
            }
        }
    }

    function _validateArchDep($dep, $installcondition = false)
    {
        $structure = array(
            'pattern',
            '*conflicts',
        );
        $type = $installcondition ? '<installcondition><arch>' : '<dependencies><required><arch>';
        $this->_stupidSchemaValidate($structure, $dep, $type);
    }

    function _validateInstallConditions($cond, $release)
    {
        $structure = array(
            '*php',
            '*extension',
            '*os',
            '*arch',
        );
        if (!$this->_stupidSchemaValidate($structure,
              $cond, $release)) {
            return false;
        }
        foreach (array('php', 'extension', 'os', 'arch') as $type) {
            if (isset($cond[$type])) {
                $iter = $cond[$type];
                if (!is_array($iter) || !isset($iter[0])) {
                    $iter = array($iter);
                }
                foreach ($iter as $package) {
                    if ($type == 'extension') {
                        $this->{"_validate{$type}Dep"}($package, false, true);
                    } else {
                        $this->{"_validate{$type}Dep"}($package, true);
                    }
                }
            }
        }
    }

    function _validateDependencies()
    {
        $structure = array(
            'required',
            '*optional',
            '*group->name->hint'
        );
        if (!$this->_stupidSchemaValidate($structure,
              $this->_packageInfo['dependencies'], '<dependencies>')) {
            return false;
        }
        foreach (array('required', 'optional') as $simpledep) {
            if (isset($this->_packageInfo['dependencies'][$simpledep])) {
                if ($simpledep == 'optional') {
                    $structure = array(
                        '*package',
                        '*subpackage',
                        '*extension',
                    );
                } else {
                    $structure = array(
                        'php',
                        'pearinstaller',
                        '*package',
                        '*subpackage',
                        '*extension',
                        '*os',
                        '*arch',
                    );
                }
                if ($this->_stupidSchemaValidate($structure,
                      $this->_packageInfo['dependencies'][$simpledep],
                      "<dependencies><$simpledep>")) {
                    foreach (array('package', 'subpackage', 'extension') as $type) {
                        if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) {
                            $iter = $this->_packageInfo['dependencies'][$simpledep][$type];
                            if (!isset($iter[0])) {
                                $iter = array($iter);
                            }
                            foreach ($iter as $package) {
                                if ($type != 'extension') {
                                    if (isset($package['uri'])) {
                                        if (isset($package['channel'])) {
                                            $this->_UrlOrChannel($type,
                                                $package['name']);
                                        }
                                    } else {
                                        if (!isset($package['channel'])) {
                                            $this->_NoChannel($type, $package['name']);
                                        }
                                    }
                                }
                                $this->{"_validate{$type}Dep"}($package, "<$simpledep>");
                            }
                        }
                    }
                    if ($simpledep == 'optional') {
                        continue;
                    }
                    foreach (array('php', 'pearinstaller', 'os', 'arch') as $type) {
                        if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) {
                            $iter = $this->_packageInfo['dependencies'][$simpledep][$type];
                            if (!isset($iter[0])) {
                                $iter = array($iter);
                            }
                            foreach ($iter as $package) {
                                $this->{"_validate{$type}Dep"}($package);
                            }
                        }
                    }
                }
            }
        }
        if (isset($this->_packageInfo['dependencies']['group'])) {
            $groups = $this->_packageInfo['dependencies']['group'];
            if (!isset($groups[0])) {
                $groups = array($groups);
            }
            $structure = array(
                '*package',
                '*subpackage',
                '*extension',
            );
            foreach ($groups as $group) {
                if ($this->_stupidSchemaValidate($structure, $group, '<group>')) {
                    if (!PEAR_Validate::validGroupName($group['attribs']['name'])) {
                        $this->_invalidDepGroupName($group['attribs']['name']);
                    }
                    foreach (array('package', 'subpackage', 'extension') as $type) {
                        if (isset($group[$type])) {
                            $iter = $group[$type];
                            if (!isset($iter[0])) {
                                $iter = array($iter);
                            }
                            foreach ($iter as $package) {
                                if ($type != 'extension') {
                                    if (isset($package['uri'])) {
                                        if (isset($package['channel'])) {
                                            $this->_UrlOrChannelGroup($type,
                                                $package['name'],
                                                $group['name']);
                                        }
                                    } else {
                                        if (!isset($package['channel'])) {
                                            $this->_NoChannelGroup($type,
                                                $package['name'],
                                                $group['name']);
                                        }
                                    }
                                }
                                $this->{"_validate{$type}Dep"}($package, '<group name="' .
                                    $group['attribs']['name'] . '">');
                            }
                        }
                    }
                }
            }
        }
    }

    function _validateCompatible()
    {
        $compat = $this->_packageInfo['compatible'];
        if (!isset($compat[0])) {
            $compat = array($compat);
        }
        $required = array('name', 'channel', 'min', 'max', '*exclude');
        foreach ($compat as $package) {
            $type = '<compatible>';
            if (is_array($package) && array_key_exists('name', $package)) {
                $type .= '<name>' . $package['name'] . '</name>';
            }
            $this->_stupidSchemaValidate($required, $package, $type);
            if (is_array($package) && array_key_exists('min', $package)) {
                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                      $package['min'])) {
                    $this->_invalidVersion(substr($type, 1) . '<min', $package['min']);
                }
            }
            if (is_array($package) && array_key_exists('max', $package)) {
                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                      $package['max'])) {
                    $this->_invalidVersion(substr($type, 1) . '<max', $package['max']);
                }
            }
            if (is_array($package) && array_key_exists('exclude', $package)) {
                if (!is_array($package['exclude'])) {
                    $package['exclude'] = array($package['exclude']);
                }
                foreach ($package['exclude'] as $exclude) {
                    if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                          $exclude)) {
                        $this->_invalidVersion(substr($type, 1) . '<exclude', $exclude);
                    }
                }
            }
        }
    }

    function _validateBundle($list)
    {
        if (!is_array($list) || !isset($list['bundledpackage'])) {
            return $this->_NoBundledPackages();
        }
        if (!is_array($list['bundledpackage']) || !isset($list['bundledpackage'][0])) {
            return $this->_AtLeast2BundledPackages();
        }
        foreach ($list['bundledpackage'] as $package) {
            if (!is_string($package)) {
                $this->_bundledPackagesMustBeFilename();
            }
        }
    }

    function _validateFilelist($list = false, $allowignore = false, $dirs = '')
    {
        $iscontents = false;
        if (!$list) {
            $iscontents = true;
            $list = $this->_packageInfo['contents'];
            if (isset($this->_packageInfo['bundle'])) {
                return $this->_validateBundle($list);
            }
        }
        if ($allowignore) {
            $struc = array(
                '*install->name->as',
                '*ignore->name'
            );
        } else {
            $struc = array(
                '*dir->name->?baseinstalldir',
                '*file->name->role->?baseinstalldir->?md5sum'
            );
            if (isset($list['dir']) && isset($list['file'])) {
                // stave off validation errors without requiring a set order.
                $_old = $list;
                if (isset($list['attribs'])) {
                    $list = array('attribs' => $_old['attribs']);
                }
                $list['dir'] = $_old['dir'];
                $list['file'] = $_old['file'];
            }
        }
        if (!isset($list['attribs']) || !isset($list['attribs']['name'])) {
            $unknown = $allowignore ? '<filelist>' : '<dir name="*unknown*">';
            $dirname = $iscontents ? '<contents>' : $unknown;
        } else {
            $dirname = '<dir name="' . $list['attribs']['name'] . '">';
            if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
                          str_replace('\\', '/', $list['attribs']['name']))) {
                // file contains .. parent directory or . cur directory
                $this->_invalidDirName($list['attribs']['name']);
            }
        }
        $res = $this->_stupidSchemaValidate($struc, $list, $dirname);
        if ($allowignore && $res) {
            $ignored_or_installed = array();
            $this->_pf->getFilelist();
            $fcontents = $this->_pf->getContents();
            $filelist = array();
            if (!isset($fcontents['dir']['file'][0])) {
                $fcontents['dir']['file'] = array($fcontents['dir']['file']);
            }
            foreach ($fcontents['dir']['file'] as $file) {
                $filelist[$file['attribs']['name']] = true;
            }
            if (isset($list['install'])) {
                if (!isset($list['install'][0])) {
                    $list['install'] = array($list['install']);
                }
                foreach ($list['install'] as $file) {
                    if (!isset($filelist[$file['attribs']['name']])) {
                        $this->_notInContents($file['attribs']['name'], 'install');
                        continue;
                    }
                    if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) {
                        $this->_multipleInstallAs($file['attribs']['name']);
                    }
                    if (!isset($ignored_or_installed[$file['attribs']['name']])) {
                        $ignored_or_installed[$file['attribs']['name']] = array();
                    }
                    $ignored_or_installed[$file['attribs']['name']][] = 1;
                    if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
                                  str_replace('\\', '/', $file['attribs']['as']))) {
                        // file contains .. parent directory or . cur directory references
                        $this->_invalidFileInstallAs($file['attribs']['name'],
                            $file['attribs']['as']);
                    }
                }
            }
            if (isset($list['ignore'])) {
                if (!isset($list['ignore'][0])) {
                    $list['ignore'] = array($list['ignore']);
                }
                foreach ($list['ignore'] as $file) {
                    if (!isset($filelist[$file['attribs']['name']])) {
                        $this->_notInContents($file['attribs']['name'], 'ignore');
                        continue;
                    }
                    if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) {
                        $this->_ignoreAndInstallAs($file['attribs']['name']);
                    }
                }
            }
        }
        if (!$allowignore && isset($list['file'])) {
            if (is_string($list['file'])) {
                $this->_oldStyleFileNotAllowed();
                return false;
            }
            if (!isset($list['file'][0])) {
                // single file
                $list['file'] = array($list['file']);
            }
            foreach ($list['file'] as $i => $file)
            {
                if (isset($file['attribs']) && isset($file['attribs']['name'])) {
                    if ($file['attribs']['name']{0} == '.' &&
                          $file['attribs']['name']{1} == '/') {
                        // name is something like "./doc/whatever.txt"
                        $this->_invalidFileName($file['attribs']['name'], $dirname);
                    }
                    if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
                                  str_replace('\\', '/', $file['attribs']['name']))) {
                        // file contains .. parent directory or . cur directory
                        $this->_invalidFileName($file['attribs']['name'], $dirname);
                    }
                }
                if (isset($file['attribs']) && isset($file['attribs']['role'])) {
                    if (!$this->_validateRole($file['attribs']['role'])) {
                        if (isset($this->_packageInfo['usesrole'])) {
                            $roles = $this->_packageInfo['usesrole'];
                            if (!isset($roles[0])) {
                                $roles = array($roles);
                            }
                            foreach ($roles as $role) {
                                if ($role['role'] = $file['attribs']['role']) {
                                    $msg = 'This package contains role "%role%" and requires ' .
                                        'package "%package%" to be used';
                                    if (isset($role['uri'])) {
                                        $params = array('role' => $role['role'],
                                            'package' => $role['uri']);
                                    } else {
                                        $params = array('role' => $role['role'],
                                            'package' => $this->_pf->_registry->
                                            parsedPackageNameToString(array('package' =>
                                                $role['package'], 'channel' => $role['channel']),
                                                true));
                                    }
                                    $this->_stack->push('_mustInstallRole', 'error', $params, $msg);
                                }
                            }
                        }
                        $this->_invalidFileRole($file['attribs']['name'],
                            $dirname, $file['attribs']['role']);
                    }
                }
                if (!isset($file['attribs'])) {
                    continue;
                }
                $save = $file['attribs'];
                if ($dirs) {
                    $save['name'] = $dirs . '/' . $save['name'];
                }
                unset($file['attribs']);
                if (count($file) && $this->_curState != PEAR_VALIDATE_DOWNLOADING) { // has tasks
                    foreach ($file as $task => $value) {
                        if ($tagClass = $this->_pf->getTask($task)) {
                            if (!is_array($value) || !isset($value[0])) {
                                $value = array($value);
                            }
                            foreach ($value as $v) {
                                $ret = call_user_func(array($tagClass, 'validateXml'),
                                    $this->_pf, $v, $this->_pf->_config, $save);
                                if (is_array($ret)) {
                                    $this->_invalidTask($task, $ret, isset($save['name']) ?
                                        $save['name'] : '');
                                }
                            }
                        } else {
                            if (isset($this->_packageInfo['usestask'])) {
                                $roles = $this->_packageInfo['usestask'];
                                if (!isset($roles[0])) {
                                    $roles = array($roles);
                                }
                                foreach ($roles as $role) {
                                    if ($role['task'] = $task) {
                                        $msg = 'This package contains task "%task%" and requires ' .
                                            'package "%package%" to be used';
                                        if (isset($role['uri'])) {
                                            $params = array('task' => $role['task'],
                                                'package' => $role['uri']);
                                        } else {
                                            $params = array('task' => $role['task'],
                                                'package' => $this->_pf->_registry->
                                                parsedPackageNameToString(array('package' =>
                                                    $role['package'], 'channel' => $role['channel']),
                                                    true));
                                        }
                                        $this->_stack->push('_mustInstallTask', 'error',
                                            $params, $msg);
                                    }
                                }
                            }
                            $this->_unknownTask($task, $save['name']);
                        }
                    }
                }
            }
        }
        if (isset($list['ignore'])) {
            if (!$allowignore) {
                $this->_ignoreNotAllowed('ignore');
            }
        }
        if (isset($list['install'])) {
            if (!$allowignore) {
                $this->_ignoreNotAllowed('install');
            }
        }
        if (isset($list['file'])) {
            if ($allowignore) {
                $this->_fileNotAllowed('file');
            }
        }
        if (isset($list['dir'])) {
            if ($allowignore) {
                $this->_fileNotAllowed('dir');
            } else {
                if (!isset($list['dir'][0])) {
                    $list['dir'] = array($list['dir']);
                }
                foreach ($list['dir'] as $dir) {
                    if (isset($dir['attribs']) && isset($dir['attribs']['name'])) {
                        if ($dir['attribs']['name'] == '/' ||
                              !isset($this->_packageInfo['contents']['dir']['dir'])) {
                            // always use nothing if the filelist has already been flattened
                            $newdirs = '';
                        } elseif ($dirs == '') {
                            $newdirs = $dir['attribs']['name'];
                        } else {
                            $newdirs = $dirs . '/' . $dir['attribs']['name'];
                        }
                    } else {
                        $newdirs = $dirs;
                    }
                    $this->_validateFilelist($dir, $allowignore, $newdirs);
                }
            }
        }
    }

    function _validateRelease()
    {
        if (isset($this->_packageInfo['phprelease'])) {
            $release = 'phprelease';
            if (isset($this->_packageInfo['providesextension'])) {
                $this->_cannotProvideExtension($release);
            }
            if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
                $this->_cannotHaveSrcpackage($release);
            }
            $releases = $this->_packageInfo['phprelease'];
            if (!is_array($releases)) {
                return true;
            }
            if (!isset($releases[0])) {
                $releases = array($releases);
            }
            foreach ($releases as $rel) {
                $this->_stupidSchemaValidate(array(
                    '*installconditions',
                    '*filelist',
                ), $rel, '<phprelease>');
            }
        }
        foreach (array('', 'zend') as $prefix) {
            $releasetype = $prefix . 'extsrcrelease';
            if (isset($this->_packageInfo[$releasetype])) {
                $release = $releasetype;
                if (!isset($this->_packageInfo['providesextension'])) {
                    $this->_mustProvideExtension($release);
                }
                if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
                    $this->_cannotHaveSrcpackage($release);
                }
                $releases = $this->_packageInfo[$releasetype];
                if (!is_array($releases)) {
                    return true;
                }
                if (!isset($releases[0])) {
                    $releases = array($releases);
                }
                foreach ($releases as $rel) {
                    $this->_stupidSchemaValidate(array(
                        '*installconditions',
                        '*configureoption->name->prompt->?default',
                        '*binarypackage',
                        '*filelist',
                    ), $rel, '<' . $releasetype . '>');
                    if (isset($rel['binarypackage'])) {
                        if (!is_array($rel['binarypackage']) || !isset($rel['binarypackage'][0])) {
                            $rel['binarypackage'] = array($rel['binarypackage']);
                        }
                        foreach ($rel['binarypackage'] as $bin) {
                            if (!is_string($bin)) {
                                $this->_binaryPackageMustBePackagename();
                            }
                        }
                    }
                }
            }
            $releasetype = 'extbinrelease';
            if (isset($this->_packageInfo[$releasetype])) {
                $release = $releasetype;
                if (!isset($this->_packageInfo['providesextension'])) {
                    $this->_mustProvideExtension($release);
                }
                if (isset($this->_packageInfo['channel']) &&
                      !isset($this->_packageInfo['srcpackage'])) {
                    $this->_mustSrcPackage($release);
                }
                if (isset($this->_packageInfo['uri']) && !isset($this->_packageInfo['srcuri'])) {
                    $this->_mustSrcuri($release);
                }
                $releases = $this->_packageInfo[$releasetype];
                if (!is_array($releases)) {
                    return true;
                }
                if (!isset($releases[0])) {
                    $releases = array($releases);
                }
                foreach ($releases as $rel) {
                    $this->_stupidSchemaValidate(array(
                        '*installconditions',
                        '*filelist',
                    ), $rel, '<' . $releasetype . '>');
                }
            }
        }
        if (isset($this->_packageInfo['bundle'])) {
            $release = 'bundle';
            if (isset($this->_packageInfo['providesextension'])) {
                $this->_cannotProvideExtension($release);
            }
            if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
                $this->_cannotHaveSrcpackage($release);
            }
            $releases = $this->_packageInfo['bundle'];
            if (!is_array($releases) || !isset($releases[0])) {
                $releases = array($releases);
            }
            foreach ($releases as $rel) {
                $this->_stupidSchemaValidate(array(
                    '*installconditions',
                    '*filelist',
                ), $rel, '<bundle>');
            }
        }
        foreach ($releases as $rel) {
            if (is_array($rel) && array_key_exists('installconditions', $rel)) {
                $this->_validateInstallConditions($rel['installconditions'],
                    "<$release><installconditions>");
            }
            if (is_array($rel) && array_key_exists('filelist', $rel)) {
                if ($rel['filelist']) {

                    $this->_validateFilelist($rel['filelist'], true);
                }
            }
        }
    }

    /**
     * This is here to allow role extension through plugins
     * @param string
     */
    function _validateRole($role)
    {
        return in_array($role, PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType()));
    }

    function _pearVersionTooLow($version)
    {
        $this->_stack->push(__FUNCTION__, 'error',
            array('version' => $version),
            'This package.xml requires PEAR version %version% to parse properly, we are ' .
            'version 1.7.2');
    }

    function _invalidTagOrder($oktags, $actual, $root)
    {
        $this->_stack->push(__FUNCTION__, 'error',
            array('oktags' => $oktags, 'actual' => $actual, 'root' => $root),
            'Invalid tag order in %root%, found <%actual%> expected one of "%oktags%"');
    }

    function _ignoreNotAllowed($type)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
            '<%type%> is not allowed inside global <contents>, only inside ' .
            '<phprelease>/<extbinrelease>/<zendextbinrelease>, use <dir> and <file> only');
    }

    function _fileNotAllowed($type)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
            '<%type%> is not allowed inside release <filelist>, only inside ' .
            '<contents>, use <ignore> and <install> only');
    }

    function _oldStyleFileNotAllowed()
    {
        $this->_stack->push(__FUNCTION__, 'error', array(),
            'Old-style <file>name</file> is not allowed.  Use' .
            '<file name="name" role="role"/>');
    }

    function _tagMissingAttribute($tag, $attr, $context)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag,
            'attribute' => $attr, 'context' => $context),
            'tag <%tag%> in context "%context%" has no attribute "%attribute%"');
    }

    function _tagHasNoAttribs($tag, $context)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag,
            'context' => $context),
            'tag <%tag%> has no attributes in context "%context%"');
    }

    function _invalidInternalStructure()
    {
        $this->_stack->push(__FUNCTION__, 'exception', array(),
            'internal array was not generated by compatible parser, or extreme parser error, cannot continue');
    }

    function _invalidFileRole($file, $dir, $role)
    {
        $this->_stack->push(__FUNCTION__, 'error', array(
            'file' => $file, 'dir' => $dir, 'role' => $role,
            'roles' => PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType())),
            'File "%file%" in directory "%dir%" has invalid role "%role%", should be one of %roles%');
    }

    function _invalidFileName($file, $dir)
    {
        $this->_stack->push(__FUNCTION__, 'error', array(
            'file' => $file),
            'File "%file%" in directory "%dir%" cannot begin with "./" or contain ".."');
    }

    function _invalidFileInstallAs($file, $as)
    {
        $this->_stack->push(__FUNCTION__, 'error', array(
            'file' => $file, 'as' => $as),
            'File "%file%" <install as="%as%"/> cannot contain "./" or contain ".."');
    }

    function _invalidDirName($dir)
    {
        $this->_stack->push(__FUNCTION__, 'error', array(
            'dir' => $file),
            'Directory "%dir%" cannot begin with "./" or contain ".."');
    }

    function _filelistCannotContainFile($filelist)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist),
            '<%tag%> can only contain <dir>, contains <file>.  Use ' .
            '<dir name="/"> as the first dir element');
    }

    function _filelistMustContainDir($filelist)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist),
            '<%tag%> must contain <dir>.  Use <dir name="/"> as the ' .
            'first dir element');
    }

    function _tagCannotBeEmpty($tag)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag),
            '<%tag%> cannot be empty (<%tag%/>)');
    }

    function _UrlOrChannel($type, $name)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
            'name' => $name),
            'Required dependency <%type%> "%name%" can have either url OR ' .
            'channel attributes, and not both');
    }

    function _NoChannel($type, $name)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
            'name' => $name),
            'Required dependency <%type%> "%name%" must have either url OR ' .
            'channel attributes');
    }

    function _UrlOrChannelGroup($type, $name, $group)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
            'name' => $name, 'group' => $group),
            'Group "%group%" dependency <%type%> "%name%" can have either url OR ' .
            'channel attributes, and not both');
    }

    function _NoChannelGroup($type, $name, $group)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
            'name' => $name, 'group' => $group),
            'Group "%group%" dependency <%type%> "%name%" must have either url OR ' .
            'channel attributes');
    }

    function _unknownChannel($channel)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('channel' => $channel),
            'Unknown channel "%channel%"');
    }

    function _noPackageVersion()
    {
        $this->_stack->push(__FUNCTION__, 'error', array(),
            'package.xml <package> tag has no version attribute, or version is not 2.0');
    }

    function _NoBundledPackages()
    {
        $this->_stack->push(__FUNCTION__, 'error', array(),
            'No <bundledpackage> tag was found in <contents>, required for bundle packages');
    }

    function _AtLeast2BundledPackages()
    {
        $this->_stack->push(__FUNCTION__, 'error', array(),
            'At least 2 packages must be bundled in a bundle package');
    }

    function _ChannelOrUri($name)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
            'Bundled package "%name%" can have either a uri or a channel, not both');
    }

    function _noChildTag($child, $tag)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('child' => $child, 'tag' => $tag),
            'Tag <%tag%> is missing child tag <%child%>');
    }

    function _invalidVersion($type, $value)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value),
            'Version type <%type%> is not a valid version (%value%)');
    }

    function _invalidState($type, $value)
    {
        $states = array('stable', 'beta', 'alpha', 'devel');
        if ($type != 'api') {
            $states[] = 'snapshot';
        }
        if (strtolower($value) == 'rc') {
            $this->_stack->push(__FUNCTION__, 'error',
                array('version' => $this->_packageInfo['version']['release']),
                'RC is not a state, it is a version postfix, try %version%RC1, stability beta');
        }
        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value,
            'types' => $states),
            'Stability type <%type%> is not a valid stability (%value%), must be one of ' .
            '%types%');
    }

    function _invalidTask($task, $ret, $file)
    {
        switch ($ret[0]) {
            case PEAR_TASK_ERROR_MISSING_ATTRIB :
                $info = array('attrib' => $ret[1], 'task' => $task, 'file' => $file);
                $msg = 'task <%task%> is missing attribute "%attrib%" in file %file%';
            break;
            case PEAR_TASK_ERROR_NOATTRIBS :
                $info = array('task' => $task, 'file' => $file);
                $msg = 'task <%task%> has no attributes in file %file%';
            break;
            case PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE :
                $info = array('attrib' => $ret[1], 'values' => $ret[3],
                    'was' => $ret[2], 'task' => $task, 'file' => $file);
                $msg = 'task <%task%> attribute "%attrib%" has the wrong value "%was%" '.
                    'in file %file%, expecting one of "%values%"';
            break;
            case PEAR_TASK_ERROR_INVALID :
                $info = array('reason' => $ret[1], 'task' => $task, 'file' => $file);
                $msg = 'task <%task%> in file %file% is invalid because of "%reason%"';
            break;
        }
        $this->_stack->push(__FUNCTION__, 'error', $info, $msg);
    }

    function _unknownTask($task, $file)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('task' => $task, 'file' => $file),
            'Unknown task "%task%" passed in file <file name="%file%">');
    }

    function _subpackageCannotProvideExtension($name)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
            'Subpackage dependency "%name%" cannot use <providesextension>, ' .
            'only package dependencies can use this tag');
    }

    function _subpackagesCannotConflict($name)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
            'Subpackage dependency "%name%" cannot use <conflicts/>, ' .
            'only package dependencies can use this tag');
    }

    function _cannotProvideExtension($release)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
            '<%release%> packages cannot use <providesextension>, only extbinrelease, extsrcrelease, zendextsrcrelease, and zendextbinrelease can provide a PHP extension');
    }

    function _mustProvideExtension($release)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
            '<%release%> packages must use <providesextension> to indicate which PHP extension is provided');
    }

    function _cannotHaveSrcpackage($release)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
            '<%release%> packages cannot specify a source code package, only extension binaries may use the <srcpackage> tag');
    }

    function _mustSrcPackage($release)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
            '<extbinrelease>/<zendextbinrelease> packages must specify a source code package with <srcpackage>');
    }

    function _mustSrcuri($release)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
            '<extbinrelease>/<zendextbinrelease> packages must specify a source code package with <srcuri>');
    }

    function _uriDepsCannotHaveVersioning($type)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
            '%type%: dependencies with a <uri> tag cannot have any versioning information');
    }

    function _conflictingDepsCannotHaveVersioning($type)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
            '%type%: conflicting dependencies cannot have versioning info, use <exclude> to ' .
            'exclude specific versions of a dependency');
    }

    function _DepchannelCannotBeUri($type)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
            '%type%: channel cannot be __uri, this is a pseudo-channel reserved for uri ' .
            'dependencies only');
    }

    function _bundledPackagesMustBeFilename()
    {
        $this->_stack->push(__FUNCTION__, 'error', array(),
            '<bundledpackage> tags must contain only the filename of a package release ' .
            'in the bundle');
    }

    function _binaryPackageMustBePackagename()
    {
        $this->_stack->push(__FUNCTION__, 'error', array(),
            '<binarypackage> tags must contain the name of a package that is ' .
            'a compiled version of this extsrc/zendextsrc package');
    }

    function _fileNotFound($file)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
            'File "%file%" in package.xml does not exist');
    }

    function _notInContents($file, $tag)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file, 'tag' => $tag),
            '<%tag% name="%file%"> is invalid, file is not in <contents>');
    }

    function _cannotValidateNoPathSet()
    {
        $this->_stack->push(__FUNCTION__, 'error', array(),
            'Cannot validate files, no path to package file is set (use setPackageFile())');
    }

    function _usesroletaskMustHaveChannelOrUri($role, $tag)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag),
            '<%tag%> for role "%role%" must contain either <uri>, or <channel> and <package>');
    }

    function _usesroletaskMustHavePackage($role, $tag)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag),
            '<%tag%> for role "%role%" must contain <package>');
    }

    function _usesroletaskMustHaveRoleTask($tag, $type)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag, 'type' => $type),
            '<%tag%> must contain <%type%> defining the %type% to be used');
    }

    function _cannotConflictWithAllOs($type)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag),
            '%tag% cannot conflict with all OSes');
    }

    function _invalidDepGroupName($name)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
            'Invalid dependency group name "%name%"');
    }

    function _multipleToplevelDirNotAllowed()
    {
        $this->_stack->push(__FUNCTION__, 'error', array(),
            'Multiple top-level <dir> tags are not allowed.  Enclose them ' .
                'in a <dir name="/">');
    }

    function _multipleInstallAs($file)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
            'Only one <install> tag is allowed for file "%file%"');
    }

    function _ignoreAndInstallAs($file)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
            'Cannot have both <ignore> and <install> tags for file "%file%"');
    }

    function _analyzeBundledPackages()
    {
        if (!$this->_isValid) {
            return false;
        }
        if (!$this->_pf->getPackageType() == 'bundle') {
            return false;
        }
        if (!isset($this->_pf->_packageFile)) {
            return false;
        }
        $dir_prefix = dirname($this->_pf->_packageFile);
        $common = new PEAR_Common;
        $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') :
            array($common, 'log');
        $info = $this->_pf->getContents();
        $info = $info['bundledpackage'];
        if (!is_array($info)) {
            $info = array($info);
        }
        $pkg = &new PEAR_PackageFile($this->_pf->_config);
        foreach ($info as $package) {
            if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $package)) {
                $this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $package);
                $this->_isValid = 0;
                continue;
            }
            call_user_func_array($log, array(1, "Analyzing bundled package $package"));
            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
            $ret = $pkg->fromAnyFile($dir_prefix . DIRECTORY_SEPARATOR . $package,
                PEAR_VALIDATE_NORMAL);
            PEAR::popErrorHandling();
            if (PEAR::isError($ret)) {
                call_user_func_array($log, array(0, "ERROR: package $package is not a valid " .
                    'package'));
                $inf = $ret->getUserInfo();
                if (is_array($inf)) {
                    foreach ($inf as $err) {
                        call_user_func_array($log, array(1, $err['message']));
                    }
                }
                return false;
            }
        }
        return true;
    }

    function _analyzePhpFiles()
    {
        if (!$this->_isValid) {
            return false;
        }
        if (!isset($this->_pf->_packageFile)) {
            $this->_cannotValidateNoPathSet();
            return false;
        }
        $dir_prefix = dirname($this->_pf->_packageFile);
        $common = new PEAR_Common;
        $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') :
            array(&$common, 'log');
        $info = $this->_pf->getContents();
        if (!$info || !isset($info['dir']['file'])) {
            $this->_tagCannotBeEmpty('contents><dir');
            return false;
        }
        $info = $info['dir']['file'];
        if (isset($info['attribs'])) {
            $info = array($info);
        }
        $provides = array();
        foreach ($info as $fa) {
            $fa = $fa['attribs'];
            $file = $fa['name'];
            if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $file)) {
                $this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $file);
                $this->_isValid = 0;
                continue;
            }
            if (in_array($fa['role'], PEAR_Installer_Role::getPhpRoles()) && $dir_prefix) {
                call_user_func_array($log, array(1, "Analyzing $file"));
                $srcinfo = $this->analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file);
                if ($srcinfo) {
                    $provides = array_merge($provides, $this->_buildProvidesArray($srcinfo));
                }
            }
        }
        $this->_packageName = $pn = $this->_pf->getPackage();
        $pnl = strlen($pn);
        foreach ($provides as $key => $what) {
            if (isset($what['explicit']) || !$what) {
                // skip conformance checks if the provides entry is
                // specified in the package.xml file
                continue;
            }
            extract($what);
            if ($type == 'class') {
                if (!strncasecmp($name, $pn, $pnl)) {
                    continue;
                }
                $this->_stack->push(__FUNCTION__, 'warning',
                    array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn),
                    'in %file%: %type% "%name%" not prefixed with package name "%package%"');
            } elseif ($type == 'function') {
                if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) {
                    continue;
                }
                $this->_stack->push(__FUNCTION__, 'warning',
                    array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn),
                    'in %file%: %type% "%name%" not prefixed with package name "%package%"');
            }
        }
        return $this->_isValid;
    }

    /**
     * Analyze the source code of the given PHP file
     *
     * @param  string Filename of the PHP file
     * @param  boolean whether to analyze $file as the file contents
     * @return mixed
     */
    function analyzeSourceCode($file, $string = false)
    {
        if (!function_exists("token_get_all")) {
            $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
                'Parser error: token_get_all() function must exist to analyze source code, PHP may have been compiled with --disable-tokenizer');
            return false;
        }
        if (!defined('T_DOC_COMMENT')) {
            define('T_DOC_COMMENT', T_COMMENT);
        }
        if (!defined('T_INTERFACE')) {
            define('T_INTERFACE', -1);
        }
        if (!defined('T_IMPLEMENTS')) {
            define('T_IMPLEMENTS', -1);
        }
        if ($string) {
            $contents = $file;
        } else {
            if (!$fp = @fopen($file, "r")) {
                return false;
            }
            fclose($fp);
            $contents = file_get_contents($file);
        }

        // Silence this function so we can catch PHP Warnings and show our own custom message
        $tokens = @token_get_all($contents);
        if (isset($php_errormsg)) {
            $pn = $this->_pf->getPackage();
            $this->_stack->push(__FUNCTION__, 'warning',
                    array('file' => $file, 'package' => $pn),
                    'in %file%: Could not process file for unkown reasons,' .
                    ' possibly a PHP parse error in %file% from %package%');

        }
/*
        for ($i = 0; $i < sizeof($tokens); $i++) {
            @list($token, $data) = $tokens[$i];
            if (is_string($token)) {
                var_dump($token);
            } else {
                print token_name($token) . ' ';
                var_dump(rtrim($data));
            }
        }
*/
        $look_for = 0;
        $paren_level = 0;
        $bracket_level = 0;
        $brace_level = 0;
        $lastphpdoc = '';
        $current_class = '';
        $current_interface = '';
        $current_class_level = -1;
        $current_function = '';
        $current_function_level = -1;
        $declared_classes = array();
        $declared_interfaces = array();
        $declared_functions = array();
        $declared_methods = array();
        $used_classes = array();
        $used_functions = array();
        $extends = array();
        $implements = array();
        $nodeps = array();
        $inquote = false;
        $interface = false;
        for ($i = 0; $i < sizeof($tokens); $i++) {
            if (is_array($tokens[$i])) {
                list($token, $data) = $tokens[$i];
            } else {
                $token = $tokens[$i];
                $data = '';
            }
            if ($inquote) {
                if ($token != '"' && $token != T_END_HEREDOC) {
                    continue;
                } else {
                    $inquote = false;
                    continue;
                }
            }
            switch ($token) {
                case T_WHITESPACE :
                    continue;
                case ';':
                    if ($interface) {
                        $current_function = '';
                        $current_function_level = -1;
                    }
                    break;
                case '"':
                case T_START_HEREDOC:
                    $inquote = true;
                    break;
                case T_CURLY_OPEN:
                case T_DOLLAR_OPEN_CURLY_BRACES:
                case '{': $brace_level++; continue 2;
                case '}':
                    $brace_level--;
                    if ($current_class_level == $brace_level) {
                        $current_class = '';
                        $current_class_level = -1;
                    }
                    if ($current_function_level == $brace_level) {
                        $current_function = '';
                        $current_function_level = -1;
                    }
                    continue 2;
                case '[': $bracket_level++; continue 2;
                case ']': $bracket_level--; continue 2;
                case '(': $paren_level++;   continue 2;
                case ')': $paren_level--;   continue 2;
                case T_INTERFACE:
                    $interface = true;
                case T_CLASS:
                    if (($current_class_level != -1) || ($current_function_level != -1)) {
                        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
                        'Parser error: invalid PHP found in file "%file%"');
                        return false;
                    }
                case T_FUNCTION:
                case T_NEW:
                case T_EXTENDS:
                case T_IMPLEMENTS:
                    $look_for = $token;
                    continue 2;
                case T_STRING:
                    if (version_compare(zend_version(), '2.0', '<')) {
                        if (in_array(strtolower($data),
                            array('public', 'private', 'protected', 'abstract',
                                  'interface', 'implements', 'throw')
                                 )) {
                            $this->_stack->push(__FUNCTION__, 'warning', array(
                                'file' => $file),
                                'Error, PHP5 token encountered in %file%,' .
                                ' analysis should be in PHP5');
                        }
                    }
                    if ($look_for == T_CLASS) {
                        $current_class = $data;
                        $current_class_level = $brace_level;
                        $declared_classes[] = $current_class;
                    } elseif ($look_for == T_INTERFACE) {
                        $current_interface = $data;
                        $current_class_level = $brace_level;
                        $declared_interfaces[] = $current_interface;
                    } elseif ($look_for == T_IMPLEMENTS) {
                        $implements[$current_class] = $data;
                    } elseif ($look_for == T_EXTENDS) {
                        $extends[$current_class] = $data;
                    } elseif ($look_for == T_FUNCTION) {
                        if ($current_class) {
                            $current_function = "$current_class::$data";
                            $declared_methods[$current_class][] = $data;
                        } elseif ($current_interface) {
                            $current_function = "$current_interface::$data";
                            $declared_methods[$current_interface][] = $data;
                        } else {
                            $current_function = $data;
                            $declared_functions[] = $current_function;
                        }
                        $current_function_level = $brace_level;
                        $m = array();
                    } elseif ($look_for == T_NEW) {
                        $used_classes[$data] = true;
                    }
                    $look_for = 0;
                    continue 2;
                case T_VARIABLE:
                    $look_for = 0;
                    continue 2;
                case T_DOC_COMMENT:
                case T_COMMENT:
                    if (preg_match('!^/\*\*\s!', $data)) {
                        $lastphpdoc = $data;
                        if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
                            $nodeps = array_merge($nodeps, $m[1]);
                        }
                    }
                    continue 2;
                case T_DOUBLE_COLON:
                    if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) {
                        $this->_stack->push(__FUNCTION__, 'warning', array('file' => $file),
                            'Parser error: invalid PHP found in file "%file%"');
                        return false;
                    }
                    $class = $tokens[$i - 1][1];
                    if (strtolower($class) != 'parent') {
                        $used_classes[$class] = true;
                    }
                    continue 2;
            }
        }
        return array(
            "source_file" => $file,
            "declared_classes" => $declared_classes,
            "declared_interfaces" => $declared_interfaces,
            "declared_methods" => $declared_methods,
            "declared_functions" => $declared_functions,
            "used_classes" => array_diff(array_keys($used_classes), $nodeps),
            "inheritance" => $extends,
            "implements" => $implements,
            );
    }

    /**
     * Build a "provides" array from data returned by
     * analyzeSourceCode().  The format of the built array is like
     * this:
     *
     *  array(
     *    'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
     *    ...
     *  )
     *
     *
     * @param array $srcinfo array with information about a source file
     * as returned by the analyzeSourceCode() method.
     *
     * @return void
     *
     * @access private
     *
     */
    function _buildProvidesArray($srcinfo)
    {
        if (!$this->_isValid) {
            return array();
        }
        $providesret = array();
        $file = basename($srcinfo['source_file']);
        $pn = $this->_pf->getPackage();
        $pnl = strlen($pn);
        foreach ($srcinfo['declared_classes'] as $class) {
            $key = "class;$class";
            if (isset($providesret[$key])) {
                continue;
            }
            $providesret[$key] =
                array('file'=> $file, 'type' => 'class', 'name' => $class);
            if (isset($srcinfo['inheritance'][$class])) {
                $providesret[$key]['extends'] =
                    $srcinfo['inheritance'][$class];
            }
        }
        foreach ($srcinfo['declared_methods'] as $class => $methods) {
            foreach ($methods as $method) {
                $function = "$class::$method";
                $key = "function;$function";
                if ($method{0} == '_' || !strcasecmp($method, $class) ||
                    isset($providesret[$key])) {
                    continue;
                }
                $providesret[$key] =
                    array('file'=> $file, 'type' => 'function', 'name' => $function);
            }
        }

        foreach ($srcinfo['declared_functions'] as $function) {
            $key = "function;$function";
            if ($function{0} == '_' || isset($providesret[$key])) {
                continue;
            }
            if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
                $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
            }
            $providesret[$key] =
                array('file'=> $file, 'type' => 'function', 'name' => $function);
        }
        return $providesret;
    }
}
?>PEAR-1.7.2/PEAR/PackageFile/v1.php100644   1750   1750      144516 11014312742  11461 <?php
/**
 * PEAR_PackageFile_v1, package.xml version 1.0
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: v1.php,v 1.74 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * For error handling
 */
require_once 'PEAR/ErrorStack.php';

/**
 * Error code if parsing is attempted with no xml extension
 */
define('PEAR_PACKAGEFILE_ERROR_NO_XML_EXT', 3);

/**
 * Error code if creating the xml parser resource fails
 */
define('PEAR_PACKAGEFILE_ERROR_CANT_MAKE_PARSER', 4);

/**
 * Error code used for all sax xml parsing errors
 */
define('PEAR_PACKAGEFILE_ERROR_PARSER_ERROR', 5);

/**
 * Error code used when there is no name
 */
define('PEAR_PACKAGEFILE_ERROR_NO_NAME', 6);

/**
 * Error code when a package name is not valid
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_NAME', 7);

/**
 * Error code used when no summary is parsed
 */
define('PEAR_PACKAGEFILE_ERROR_NO_SUMMARY', 8);

/**
 * Error code for summaries that are more than 1 line
 */
define('PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY', 9);

/**
 * Error code used when no description is present
 */
define('PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION', 10);

/**
 * Error code used when no license is present
 */
define('PEAR_PACKAGEFILE_ERROR_NO_LICENSE', 11);

/**
 * Error code used when a <version> version number is not present
 */
define('PEAR_PACKAGEFILE_ERROR_NO_VERSION', 12);

/**
 * Error code used when a <version> version number is invalid
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_VERSION', 13);

/**
 * Error code when release state is missing
 */
define('PEAR_PACKAGEFILE_ERROR_NO_STATE', 14);

/**
 * Error code when release state is invalid
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_STATE', 15);

/**
 * Error code when release state is missing
 */
define('PEAR_PACKAGEFILE_ERROR_NO_DATE', 16);

/**
 * Error code when release state is invalid
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_DATE', 17);

/**
 * Error code when no release notes are found
 */
define('PEAR_PACKAGEFILE_ERROR_NO_NOTES', 18);

/**
 * Error code when no maintainers are found
 */
define('PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS', 19);

/**
 * Error code when a maintainer has no handle
 */
define('PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE', 20);

/**
 * Error code when a maintainer has no handle
 */
define('PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE', 21);

/**
 * Error code when a maintainer has no name
 */
define('PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME', 22);

/**
 * Error code when a maintainer has no email
 */
define('PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL', 23);

/**
 * Error code when a maintainer has no handle
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_MAINTROLE', 24);

/**
 * Error code when a dependency is not a PHP dependency, but has no name
 */
define('PEAR_PACKAGEFILE_ERROR_NO_DEPNAME', 25);

/**
 * Error code when a dependency has no type (pkg, php, etc.)
 */
define('PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE', 26);

/**
 * Error code when a dependency has no relation (lt, ge, has, etc.)
 */
define('PEAR_PACKAGEFILE_ERROR_NO_DEPREL', 27);

/**
 * Error code when a dependency is not a 'has' relation, but has no version
 */
define('PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION', 28);

/**
 * Error code when a dependency has an invalid relation
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPREL', 29);

/**
 * Error code when a dependency has an invalid type
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPTYPE', 30);

/**
 * Error code when a dependency has an invalid optional option
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL', 31);

/**
 * Error code when a dependency is a pkg dependency, and has an invalid package name
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPNAME', 32);

/**
 * Error code when a dependency has a channel="foo" attribute, and foo is not a registered channel
 */
define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_DEPCHANNEL', 33);

/**
 * Error code when rel="has" and version attribute is present.
 */
define('PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED', 34);

/**
 * Error code when type="php" and dependency name is present
 */
define('PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED', 35);

/**
 * Error code when a configure option has no name
 */
define('PEAR_PACKAGEFILE_ERROR_NO_CONFNAME', 36);

/**
 * Error code when a configure option has no name
 */
define('PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT', 37);

/**
 * Error code when a file in the filelist has an invalid role
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE', 38);

/**
 * Error code when a file in the filelist has no role
 */
define('PEAR_PACKAGEFILE_ERROR_NO_FILEROLE', 39);

/**
 * Error code when analyzing a php source file that has parse errors
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE', 40);

/**
 * Error code when analyzing a php source file reveals a source element
 * without a package name prefix
 */
define('PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX', 41);

/**
 * Error code when an unknown channel is specified
 */
define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_CHANNEL', 42);

/**
 * Error code when no files are found in the filelist
 */
define('PEAR_PACKAGEFILE_ERROR_NO_FILES', 43);

/**
 * Error code when a file is not valid php according to _analyzeSourceCode()
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_FILE', 44);

/**
 * Error code when the channel validator returns an error or warning
 */
define('PEAR_PACKAGEFILE_ERROR_CHANNELVAL', 45);

/**
 * Error code when a php5 package is packaged in php4 (analysis doesn't work)
 */
define('PEAR_PACKAGEFILE_ERROR_PHP5', 46);

/**
 * Error code when a file is listed in package.xml but does not exist
 */
define('PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND', 47);

/**
 * Error code when a <dep type="php" rel="not"... is encountered (use rel="ne")
 */
define('PEAR_PACKAGEFILE_PHP_NO_NOT', 48);

/**
 * Error code when a package.xml contains non-ISO-8859-1 characters
 */
define('PEAR_PACKAGEFILE_ERROR_NON_ISO_CHARS', 49);

/**
 * Error code when a dependency is not a 'has' relation, but has no version
 */
define('PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION', 50);

/**
 * Error code when a package has no lead developer
 */
define('PEAR_PACKAGEFILE_ERROR_NO_LEAD', 51);

/**
 * Error code when a filename begins with "."
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME', 52);
/**
 * package.xml encapsulator
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_PackageFile_v1
{
    /**
     * @access private
     * @var PEAR_ErrorStack
     * @access private
     */
    var $_stack;

    /**
     * A registry object, used to access the package name validation regex for non-standard channels
     * @var PEAR_Registry
     * @access private
     */
    var $_registry;

    /**
     * An object that contains a log method that matches PEAR_Common::log's signature
     * @var object
     * @access private
     */
    var $_logger;

    /**
     * Parsed package information
     * @var array
     * @access private
     */
    var $_packageInfo;

    /**
     * path to package.xml
     * @var string
     * @access private
     */
    var $_packageFile;

    /**
     * path to package .tgz or false if this is a local/extracted package.xml
     * @var string
     * @access private
     */
    var $_archiveFile;

    /**
     * @var int
     * @access private
     */
    var $_isValid = 0;

    /**
     * Determines whether this packagefile was initialized only with partial package info
     *
     * If this package file was constructed via parsing REST, it will only contain
     *
     * - package name
     * - channel name
     * - dependencies 
     * @var boolean
     * @access private
     */
    var $_incomplete = true;

    /**
     * @param bool determines whether to return a PEAR_Error object, or use the PEAR_ErrorStack
     * @param string Name of Error Stack class to use.
     */
    function PEAR_PackageFile_v1()
    {
        $this->_stack = &new PEAR_ErrorStack('PEAR_PackageFile_v1');
        $this->_stack->setErrorMessageTemplate($this->_getErrorMessage());
        $this->_isValid = 0;
    }

    function installBinary($installer)
    {
        return false;
    }

    function isExtension($name)
    {
        return false;
    }

    function setConfig(&$config)
    {
        $this->_config = &$config;
        $this->_registry = &$config->getRegistry();
    }

    function setRequestedGroup()
    {
        // placeholder
    }

    /**
     * For saving in the registry.
     *
     * Set the last version that was installed
     * @param string
     */
    function setLastInstalledVersion($version)
    {
        $this->_packageInfo['_lastversion'] = $version;
    }

    /**
     * @return string|false
     */
    function getLastInstalledVersion()
    {
        if (isset($this->_packageInfo['_lastversion'])) {
            return $this->_packageInfo['_lastversion'];
        }
        return false;
    }

    function getInstalledBinary()
    {
        return false;
    }

    function listPostinstallScripts()
    {
        return false;
    }

    function initPostinstallScripts()
    {
        return false;
    }

    function setLogger(&$logger)
    {
        if ($logger && (!is_object($logger) || !method_exists($logger, 'log'))) {
            return PEAR::raiseError('Logger must be compatible with PEAR_Common::log');
        }
        $this->_logger = &$logger;
    }

    function setPackagefile($file, $archive = false)
    {
        $this->_packageFile = $file;
        $this->_archiveFile = $archive ? $archive : $file;
    }

    function getPackageFile()
    {
        return isset($this->_packageFile) ? $this->_packageFile : false;
    }

    function getPackageType()
    {
        return 'php';
    }

    function getArchiveFile()
    {
        return $this->_archiveFile;
    }

    function packageInfo($field)
    {
        if (!is_string($field) || empty($field) ||
            !isset($this->_packageInfo[$field])) {
            return false;
        }
        return $this->_packageInfo[$field];
    }

    function setDirtree($path)
    {
        if (!isset($this->_packageInfo['dirtree'])) {
            $this->_packageInfo['dirtree'] = array();
        }
        $this->_packageInfo['dirtree'][$path] = true;
    }

    function getDirtree()
    {
        if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) {
            return $this->_packageInfo['dirtree'];
        }
        return false;
    }

    function resetDirtree()
    {
        unset($this->_packageInfo['dirtree']);
    }

    function fromArray($pinfo)
    {
        $this->_incomplete = false;
        $this->_packageInfo = $pinfo;
    }

    function isIncomplete()
    {
        return $this->_incomplete;
    }

    function getChannel()
    {
        return 'pear.php.net';
    }

    function getUri()
    {
        return false;
    }

    function getTime()
    {
        return false;
    }

    function getExtends()
    {
        if (isset($this->_packageInfo['extends'])) {
            return $this->_packageInfo['extends'];
        }
        return false;
    }

    /**
     * @return array
     */
    function toArray()
    {
        if (!$this->validate(PEAR_VALIDATE_NORMAL)) {
            return false;
        }
        return $this->getArray();
    }

    function getArray()
    {
        return $this->_packageInfo;
    }

    function getName()
    {
        return $this->getPackage();
    }

    function getPackage()
    {
        if (isset($this->_packageInfo['package'])) {
            return $this->_packageInfo['package'];
        }
        return false;
    }

    /**
     * WARNING - don't use this unless you know what you are doing
     */
    function setRawPackage($package)
    {
        $this->_packageInfo['package'] = $package;
    }

    function setPackage($package)
    {
        $this->_packageInfo['package'] = $package;
        $this->_isValid = false;
    }

    function getVersion()
    {
        if (isset($this->_packageInfo['version'])) {
            return $this->_packageInfo['version'];
        }
        return false;
    }

    function setVersion($version)
    {
        $this->_packageInfo['version'] = $version;
        $this->_isValid = false;
    }

    function clearMaintainers()
    {
        unset($this->_packageInfo['maintainers']);
    }

    function getMaintainers()
    {
        if (isset($this->_packageInfo['maintainers'])) {
            return $this->_packageInfo['maintainers'];
        }
        return false;
    }

    /**
     * Adds a new maintainer - no checking of duplicates is performed, use
     * updatemaintainer for that purpose.
     */
    function addMaintainer($role, $handle, $name, $email)
    {
        $this->_packageInfo['maintainers'][] =
            array('handle' => $handle, 'role' => $role, 'email' => $email, 'name' => $name);
        $this->_isValid = false;
    }

    function updateMaintainer($role, $handle, $name, $email)
    {
        $found = false;
        if (!isset($this->_packageInfo['maintainers']) ||
              !is_array($this->_packageInfo['maintainers'])) {
            return $this->addMaintainer($role, $handle, $name, $email);
        }
        foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) {
            if ($maintainer['handle'] == $handle) {
                $found = $i;
                break;
            }
        }
        if ($found !== false) {
            unset($this->_packageInfo['maintainers'][$found]);
            $this->_packageInfo['maintainers'] =
                array_values($this->_packageInfo['maintainers']);
        }
        $this->addMaintainer($role, $handle, $name, $email);
    }

    function deleteMaintainer($handle)
    {
        $found = false;
        foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) {
            if ($maintainer['handle'] == $handle) {
                $found = $i;
                break;
            }
        }
        if ($found !== false) {
            unset($this->_packageInfo['maintainers'][$found]);
            $this->_packageInfo['maintainers'] =
                array_values($this->_packageInfo['maintainers']);
            return true;
        }
        return false;
    }

    function getState()
    {
        if (isset($this->_packageInfo['release_state'])) {
            return $this->_packageInfo['release_state'];
        }
        return false;
    }

    function setRawState($state)
    {
        $this->_packageInfo['release_state'] = $state;
    }

    function setState($state)
    {
        $this->_packageInfo['release_state'] = $state;
        $this->_isValid = false;
    }

    function getDate()
    {
        if (isset($this->_packageInfo['release_date'])) {
            return $this->_packageInfo['release_date'];
        }
        return false;
    }

    function setDate($date)
    {
        $this->_packageInfo['release_date'] = $date;
        $this->_isValid = false;
    }

    function getLicense()
    {
        if (isset($this->_packageInfo['release_license'])) {
            return $this->_packageInfo['release_license'];
        }
        return false;
    }

    function setLicense($date)
    {
        $this->_packageInfo['release_license'] = $date;
        $this->_isValid = false;
    }

    function getSummary()
    {
        if (isset($this->_packageInfo['summary'])) {
            return $this->_packageInfo['summary'];
        }
        return false;
    }

    function setSummary($summary)
    {
        $this->_packageInfo['summary'] = $summary;
        $this->_isValid = false;
    }

    function getDescription()
    {
        if (isset($this->_packageInfo['description'])) {
            return $this->_packageInfo['description'];
        }
        return false;
    }

    function setDescription($desc)
    {
        $this->_packageInfo['description'] = $desc;
        $this->_isValid = false;
    }

    function getNotes()
    {
        if (isset($this->_packageInfo['release_notes'])) {
            return $this->_packageInfo['release_notes'];
        }
        return false;
    }

    function setNotes($notes)
    {
        $this->_packageInfo['release_notes'] = $notes;
        $this->_isValid = false;
    }

    function getDeps()
    {
        if (isset($this->_packageInfo['release_deps'])) {
            return $this->_packageInfo['release_deps'];
        }
        return false;
    }

    /**
     * Reset dependencies prior to adding new ones
     */
    function clearDeps()
    {
        unset($this->_packageInfo['release_deps']);
    }

    function addPhpDep($version, $rel)
    {
        $this->_isValid = false;
        $this->_packageInfo['release_deps'][] =
            array('type' => 'php',
                  'rel' => $rel,
                  'version' => $version);
    }

    function addPackageDep($name, $version, $rel, $optional = 'no')
    {
        $this->_isValid = false;
        $dep =
            array('type' => 'pkg',
                  'name' => $name,
                  'rel' => $rel,
                  'optional' => $optional);
        if ($rel != 'has' && $rel != 'not') {
            $dep['version'] = $version;
        }
        $this->_packageInfo['release_deps'][] = $dep;
    }

    function addExtensionDep($name, $version, $rel, $optional = 'no')
    {
        $this->_isValid = false;
        $this->_packageInfo['release_deps'][] =
            array('type' => 'ext',
                  'name' => $name,
                  'rel' => $rel,
                  'version' => $version,
                  'optional' => $optional);
    }

    /**
     * WARNING - do not use this function directly unless you know what you're doing
     */
    function setDeps($deps)
    {
        $this->_packageInfo['release_deps'] = $deps;
    }

    function hasDeps()
    {
        return isset($this->_packageInfo['release_deps']) &&
            count($this->_packageInfo['release_deps']);
    }

    function getDependencyGroup($group)
    {
        return false;
    }

    function isCompatible($pf)
    {
        return false;
    }

    function isSubpackageOf($p)
    {
        return $p->isSubpackage($this);
    }

    function isSubpackage($p)
    {
        return false;
    }

    function dependsOn($package, $channel)
    {
        if (strtolower($channel) != 'pear.php.net') {
            return false;
        }
        if (!($deps = $this->getDeps())) {
            return false;
        }
        foreach ($deps as $dep) {
            if ($dep['type'] != 'pkg') {
                continue;
            }
            if (strtolower($dep['name']) == strtolower($package)) {
                return true;
            }
        }
        return false;
    }

    function getConfigureOptions()
    {
        if (isset($this->_packageInfo['configure_options'])) {
            return $this->_packageInfo['configure_options'];
        }
        return false;
    }

    function hasConfigureOptions()
    {
        return isset($this->_packageInfo['configure_options']) &&
            count($this->_packageInfo['configure_options']);
    }

    function addConfigureOption($name, $prompt, $default = false)
    {
        $o = array('name' => $name, 'prompt' => $prompt);
        if ($default !== false) {
            $o['default'] = $default;
        }
        if (!isset($this->_packageInfo['configure_options'])) {
            $this->_packageInfo['configure_options'] = array();
        }
        $this->_packageInfo['configure_options'][] = $o;
    }

    function clearConfigureOptions()
    {
        unset($this->_packageInfo['configure_options']);
    }

    function getProvides()
    {
        if (isset($this->_packageInfo['provides'])) {
            return $this->_packageInfo['provides'];
        }
        return false;
    }

    function getProvidesExtension()
    {
        return false;
    }

    function addFile($dir, $file, $attrs)
    {
        $dir = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $dir);
        if ($dir == '/' || $dir == '') {
            $dir = '';
        } else {
            $dir .= '/';
        }
        $file = $dir . $file;
        $file = preg_replace('![\\/]+!', '/', $file);
        $this->_packageInfo['filelist'][$file] = $attrs;
    }

    function getInstallationFilelist()
    {
        return $this->getFilelist();
    }

    function getFilelist()
    {
        if (isset($this->_packageInfo['filelist'])) {
            return $this->_packageInfo['filelist'];
        }
        return false;
    }

    function setFileAttribute($file, $attr, $value)
    {
        $this->_packageInfo['filelist'][$file][$attr] = $value;
    }

    function resetFilelist()
    {
        $this->_packageInfo['filelist'] = array();
    }

    function setInstalledAs($file, $path)
    {
        if ($path) {
            return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
        }
        unset($this->_packageInfo['filelist'][$file]['installed_as']);
    }

    function installedFile($file, $atts)
    {
        if (isset($this->_packageInfo['filelist'][$file])) {
            $this->_packageInfo['filelist'][$file] =
                array_merge($this->_packageInfo['filelist'][$file], $atts);
        } else {
            $this->_packageInfo['filelist'][$file] = $atts;
        }
    }

    function getChangelog()
    {
        if (isset($this->_packageInfo['changelog'])) {
            return $this->_packageInfo['changelog'];
        }
        return false;
    }

    function getPackagexmlVersion()
    {
        return '1.0';
    }

    /**
     * Wrapper to {@link PEAR_ErrorStack::getErrors()}
     * @param boolean determines whether to purge the error stack after retrieving
     * @return array
     */
    function getValidationWarnings($purge = true)
    {
        return $this->_stack->getErrors($purge);
    }

    // }}}
    /**
     * Validation error.  Also marks the object contents as invalid
     * @param error code
     * @param array error information
     * @access private
     */
    function _validateError($code, $params = array())
    {
        $this->_stack->push($code, 'error', $params, false, false, debug_backtrace());
        $this->_isValid = false;
    }

    /**
     * Validation warning.  Does not mark the object contents invalid.
     * @param error code
     * @param array error information
     * @access private
     */
    function _validateWarning($code, $params = array())
    {
        $this->_stack->push($code, 'warning', $params, false, false, debug_backtrace());
    }

    /**
     * @param integer error code
     * @access protected
     */
    function _getErrorMessage()
    {
        return array(
                PEAR_PACKAGEFILE_ERROR_NO_NAME =>
                    'Missing Package Name',
                PEAR_PACKAGEFILE_ERROR_NO_SUMMARY =>
                    'No summary found',
                PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY =>
                    'Summary should be on one line',
                PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION =>
                    'Missing description',
                PEAR_PACKAGEFILE_ERROR_NO_LICENSE =>
                    'Missing license',
                PEAR_PACKAGEFILE_ERROR_NO_VERSION =>
                    'No release version found',
                PEAR_PACKAGEFILE_ERROR_NO_STATE =>
                    'No release state found',
                PEAR_PACKAGEFILE_ERROR_NO_DATE =>
                    'No release date found',
                PEAR_PACKAGEFILE_ERROR_NO_NOTES =>
                    'No release notes found',
                PEAR_PACKAGEFILE_ERROR_NO_LEAD =>
                    'Package must have at least one lead maintainer',
                PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS =>
                    'No maintainers found, at least one must be defined',
                PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE =>
                    'Maintainer %index% has no handle (user ID at channel server)',
                PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE =>
                    'Maintainer %index% has no role',
                PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME =>
                    'Maintainer %index% has no name',
                PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL =>
                    'Maintainer %index% has no email',
                PEAR_PACKAGEFILE_ERROR_NO_DEPNAME =>
                    'Dependency %index% is not a php dependency, and has no name',
                PEAR_PACKAGEFILE_ERROR_NO_DEPREL =>
                    'Dependency %index% has no relation (rel)',
                PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE =>
                    'Dependency %index% has no type',
                PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED =>
                    'PHP Dependency %index% has a name attribute of "%name%" which will be' .
                        ' ignored!',
                PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION =>
                    'Dependency %index% is not a rel="has" or rel="not" dependency, ' .
                        'and has no version',
                PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION =>
                    'Dependency %index% is a type="php" dependency, ' .
                        'and has no version',
                PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED =>
                    'Dependency %index% is a rel="%rel%" dependency, versioning is ignored',
                PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL =>
                    'Dependency %index% has invalid optional value "%opt%", should be yes or no',
                PEAR_PACKAGEFILE_PHP_NO_NOT =>
                    'Dependency %index%: php dependencies cannot use "not" rel, use "ne"' .
                        ' to exclude specific versions',
                PEAR_PACKAGEFILE_ERROR_NO_CONFNAME =>
                    'Configure Option %index% has no name',
                PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT =>
                    'Configure Option %index% has no prompt',
                PEAR_PACKAGEFILE_ERROR_NO_FILES =>
                    'No files in <filelist> section of package.xml',
                PEAR_PACKAGEFILE_ERROR_NO_FILEROLE =>
                    'File "%file%" has no role, expecting one of "%roles%"',
                PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE =>
                    'File "%file%" has invalid role "%role%", expecting one of "%roles%"',
                PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME =>
                    'File "%file%" cannot start with ".", cannot package or install',
                PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE =>
                    'Parser error: invalid PHP found in file "%file%"',
                PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX =>
                    'in %file%: %type% "%name%" not prefixed with package name "%package%"',
                PEAR_PACKAGEFILE_ERROR_INVALID_FILE =>
                    'Parser error: invalid PHP file "%file%"',
                PEAR_PACKAGEFILE_ERROR_CHANNELVAL =>
                    'Channel validator error: field "%field%" - %reason%',
                PEAR_PACKAGEFILE_ERROR_PHP5 =>
                    'Error, PHP5 token encountered in %file%, analysis should be in PHP5',
                PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND =>
                    'File "%file%" in package.xml does not exist',
                PEAR_PACKAGEFILE_ERROR_NON_ISO_CHARS =>
                    'Package.xml contains non-ISO-8859-1 characters, and may not validate',
            );
    }

    /**
     * Validate XML package definition file.
     *
     * @access public
     * @return boolean
     */
    function validate($state = PEAR_VALIDATE_NORMAL, $nofilechecking = false)
    {
        if (($this->_isValid & $state) == $state) {
            return true;
        }
        $this->_isValid = true;
        $info = $this->_packageInfo;
        if (empty($info['package'])) {
            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NAME);
            $this->_packageName = $pn = 'unknown';
        } else {
            $this->_packageName = $pn = $info['package'];
        }

        if (empty($info['summary'])) {
            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_SUMMARY);
        } elseif (strpos(trim($info['summary']), "\n") !== false) {
            $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY,
                array('summary' => $info['summary']));
        }
        if (empty($info['description'])) {
            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION);
        }
        if (empty($info['release_license'])) {
            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LICENSE);
        }
        if (empty($info['version'])) {
            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_VERSION);
        }
        if (empty($info['release_state'])) {
            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_STATE);
        }
        if (empty($info['release_date'])) {
            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DATE);
        }
        if (empty($info['release_notes'])) {
            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NOTES);
        }
        if (empty($info['maintainers'])) {
            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS);
        } else {
            $haslead = false;
            $i = 1;
            foreach ($info['maintainers'] as $m) {
                if (empty($m['handle'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE,
                        array('index' => $i));
                }
                if (empty($m['role'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE,
                        array('index' => $i, 'roles' => PEAR_Common::getUserRoles()));
                } elseif ($m['role'] == 'lead') {
                    $haslead = true;
                }
                if (empty($m['name'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME,
                        array('index' => $i));
                }
                if (empty($m['email'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL,
                        array('index' => $i));
                }
                $i++;
            }
            if (!$haslead) {
                $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LEAD);
            }
        }
        if (!empty($info['release_deps'])) {
            $i = 1;
            foreach ($info['release_deps'] as $d) {
                if (!isset($d['type']) || empty($d['type'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE,
                        array('index' => $i, 'types' => PEAR_Common::getDependencyTypes()));
                    continue;
                }
                if (!isset($d['rel']) || empty($d['rel'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPREL,
                        array('index' => $i, 'rels' => PEAR_Common::getDependencyRelations()));
                    continue;
                }
                if (!empty($d['optional'])) {
                    if (!in_array($d['optional'], array('yes', 'no'))) {
                        $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL,
                            array('index' => $i, 'opt' => $d['optional']));
                    }
                }
                if ($d['rel'] != 'has' && $d['rel'] != 'not' && empty($d['version'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION,
                        array('index' => $i));
                } elseif (($d['rel'] == 'has' || $d['rel'] == 'not') && !empty($d['version'])) {
                    $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED,
                        array('index' => $i, 'rel' => $d['rel']));
                }
                if ($d['type'] == 'php' && !empty($d['name'])) {
                    $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED,
                        array('index' => $i, 'name' => $d['name']));
                } elseif ($d['type'] != 'php' && empty($d['name'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPNAME,
                        array('index' => $i));
                }
                if ($d['type'] == 'php' && empty($d['version'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION,
                        array('index' => $i));
                }
                if (($d['rel'] == 'not') && ($d['type'] == 'php')) {
                    $this->_validateError(PEAR_PACKAGEFILE_PHP_NO_NOT,
                        array('index' => $i));
                }
                $i++;
            }
        }
        if (!empty($info['configure_options'])) {
            $i = 1;
            foreach ($info['configure_options'] as $c) {
                if (empty($c['name'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFNAME,
                        array('index' => $i));
                }
                if (empty($c['prompt'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT,
                        array('index' => $i));
                }
                $i++;
            }
        }
        if (empty($info['filelist'])) {
            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILES);
            $errors[] = 'no files';
        } else {
            foreach ($info['filelist'] as $file => $fa) {
                if (empty($fa['role'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILEROLE,
                        array('file' => $file, 'roles' => PEAR_Common::getFileRoles()));
                    continue;
                } elseif (!in_array($fa['role'], PEAR_Common::getFileRoles())) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE,
                        array('file' => $file, 'role' => $fa['role'], 'roles' => PEAR_Common::getFileRoles()));
                }
                if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~', str_replace('\\', '/', $file))) {
                    // file contains .. parent directory or . cur directory references
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
                        array('file' => $file));
                }
                if (isset($fa['install-as']) &&
                      preg_match('~/\.\.?(/|\\z)|^\.\.?/~', 
                                 str_replace('\\', '/', $fa['install-as']))) {
                    // install-as contains .. parent directory or . cur directory references
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
                        array('file' => $file . ' [installed as ' . $fa['install-as'] . ']'));
                }
                if (isset($fa['baseinstalldir']) &&
                      preg_match('~/\.\.?(/|\\z)|^\.\.?/~', 
                                 str_replace('\\', '/', $fa['baseinstalldir']))) {
                    // install-as contains .. parent directory or . cur directory references
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
                        array('file' => $file . ' [baseinstalldir ' . $fa['baseinstalldir'] . ']'));
                }
            }
        }
        if (isset($this->_registry) && $this->_isValid) {
            $chan = $this->_registry->getChannel('pear.php.net');
            if (PEAR::isError($chan)) {
                $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $chan->getMessage());
                return $this->_isValid = 0;
            }
            $validator = $chan->getValidationObject();
            $validator->setPackageFile($this);
            $validator->validate($state);
            $failures = $validator->getFailures();
            foreach ($failures['errors'] as $error) {
                $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $error);
            }
            foreach ($failures['warnings'] as $warning) {
                $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $warning);
            }
        }
        if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$nofilechecking) {
            if ($this->_analyzePhpFiles()) {
                $this->_isValid = true;
            }
        }
        if ($this->_isValid) {
            return $this->_isValid = $state;
        }
        return $this->_isValid = 0;
    }

    function _analyzePhpFiles()
    {
        if (!$this->_isValid) {
            return false;
        }
        if (!isset($this->_packageFile)) {
            return false;
        }
        $dir_prefix = dirname($this->_packageFile);
        $common = new PEAR_Common;
        $log = isset($this->_logger) ? array(&$this->_logger, 'log') :
            array($common, 'log');
        $info = $this->getFilelist();
        foreach ($info as $file => $fa) {
            if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $file)) {
                $this->_validateError(PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND,
                    array('file' => realpath($dir_prefix) . DIRECTORY_SEPARATOR . $file));
                continue;
            }
            if ($fa['role'] == 'php' && $dir_prefix) {
                call_user_func_array($log, array(1, "Analyzing $file"));
                $srcinfo = $this->_analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file);
                if ($srcinfo) {
                    $this->_buildProvidesArray($srcinfo);
                }
            }
        }
        $this->_packageName = $pn = $this->getPackage();
        $pnl = strlen($pn);
        if (isset($this->_packageInfo['provides'])) {
            foreach ((array) $this->_packageInfo['provides'] as $key => $what) {
                if (isset($what['explicit'])) {
                    // skip conformance checks if the provides entry is
                    // specified in the package.xml file
                    continue;
                }
                extract($what);
                if ($type == 'class') {
                    if (!strncasecmp($name, $pn, $pnl)) {
                        continue;
                    }
                    $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX,
                        array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn));
                } elseif ($type == 'function') {
                    if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) {
                        continue;
                    }
                    $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX,
                        array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn));
                }
            }
        }
        return $this->_isValid;
    }

    /**
     * Get the default xml generator object
     *
     * @return PEAR_PackageFile_Generator_v1
     */
    function &getDefaultGenerator()
    {
        if (!class_exists('PEAR_PackageFile_Generator_v1')) {
            require_once 'PEAR/PackageFile/Generator/v1.php';
        }
        $a = &new PEAR_PackageFile_Generator_v1($this);
        return $a;
    }

    /**
     * Get the contents of a file listed within the package.xml
     * @param string
     * @return string
     */
    function getFileContents($file)
    {
        if ($this->_archiveFile == $this->_packageFile) { // unpacked
            $dir = dirname($this->_packageFile);
            $file = $dir . DIRECTORY_SEPARATOR . $file;
            $file = str_replace(array('/', '\\'),
                array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file);
            if (file_exists($file) && is_readable($file)) {
                return implode('', file($file));
            }
        } else { // tgz
            if (!class_exists('Archive_Tar')) {
                require_once 'Archive/Tar.php';
            }
            $tar = &new Archive_Tar($this->_archiveFile);
            $tar->pushErrorHandling(PEAR_ERROR_RETURN);
            if ($file != 'package.xml' && $file != 'package2.xml') {
                $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file;
            }
            $file = $tar->extractInString($file);
            $tar->popErrorHandling();
            if (PEAR::isError($file)) {
                return PEAR::raiseError("Cannot locate file '$file' in archive");
            }
            return $file;
        }
    }

    // {{{ analyzeSourceCode()
    /**
     * Analyze the source code of the given PHP file
     *
     * @param  string Filename of the PHP file
     * @return mixed
     * @access private
     */
    function _analyzeSourceCode($file)
    {
        if (!function_exists("token_get_all")) {
            return false;
        }
        if (!defined('T_DOC_COMMENT')) {
            define('T_DOC_COMMENT', T_COMMENT);
        }
        if (!defined('T_INTERFACE')) {
            define('T_INTERFACE', -1);
        }
        if (!defined('T_IMPLEMENTS')) {
            define('T_IMPLEMENTS', -1);
        }
        if (!$fp = @fopen($file, "r")) {
            return false;
        }
        fclose($fp);
        $contents = file_get_contents($file);
        $tokens = token_get_all($contents);
/*
        for ($i = 0; $i < sizeof($tokens); $i++) {
            @list($token, $data) = $tokens[$i];
            if (is_string($token)) {
                var_dump($token);
            } else {
                print token_name($token) . ' ';
                var_dump(rtrim($data));
            }
        }
*/
        $look_for = 0;
        $paren_level = 0;
        $bracket_level = 0;
        $brace_level = 0;
        $lastphpdoc = '';
        $current_class = '';
        $current_interface = '';
        $current_class_level = -1;
        $current_function = '';
        $current_function_level = -1;
        $declared_classes = array();
        $declared_interfaces = array();
        $declared_functions = array();
        $declared_methods = array();
        $used_classes = array();
        $used_functions = array();
        $extends = array();
        $implements = array();
        $nodeps = array();
        $inquote = false;
        $interface = false;
        for ($i = 0; $i < sizeof($tokens); $i++) {
            if (is_array($tokens[$i])) {
                list($token, $data) = $tokens[$i];
            } else {
                $token = $tokens[$i];
                $data = '';
            }
            if ($inquote) {
                if ($token != '"' && $token != T_END_HEREDOC) {
                    continue;
                } else {
                    $inquote = false;
                    continue;
                }
            }
            switch ($token) {
                case T_WHITESPACE :
                    continue;
                case ';':
                    if ($interface) {
                        $current_function = '';
                        $current_function_level = -1;
                    }
                    break;
                case '"':
                case T_START_HEREDOC:
                    $inquote = true;
                    break;
                case T_CURLY_OPEN:
                case T_DOLLAR_OPEN_CURLY_BRACES:
                case '{': $brace_level++; continue 2;
                case '}':
                    $brace_level--;
                    if ($current_class_level == $brace_level) {
                        $current_class = '';
                        $current_class_level = -1;
                    }
                    if ($current_function_level == $brace_level) {
                        $current_function = '';
                        $current_function_level = -1;
                    }
                    continue 2;
                case '[': $bracket_level++; continue 2;
                case ']': $bracket_level--; continue 2;
                case '(': $paren_level++;   continue 2;
                case ')': $paren_level--;   continue 2;
                case T_INTERFACE:
                    $interface = true;
                case T_CLASS:
                    if (($current_class_level != -1) || ($current_function_level != -1)) {
                        $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE,
                            array('file' => $file));
                        return false;
                    }
                case T_FUNCTION:
                case T_NEW:
                case T_EXTENDS:
                case T_IMPLEMENTS:
                    $look_for = $token;
                    continue 2;
                case T_STRING:
                    if (version_compare(zend_version(), '2.0', '<')) {
                        if (in_array(strtolower($data),
                            array('public', 'private', 'protected', 'abstract',
                                  'interface', 'implements', 'throw') 
                                 )) {
                            $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_PHP5,
                                array($file));
                        }
                    }
                    if ($look_for == T_CLASS) {
                        $current_class = $data;
                        $current_class_level = $brace_level;
                        $declared_classes[] = $current_class;
                    } elseif ($look_for == T_INTERFACE) {
                        $current_interface = $data;
                        $current_class_level = $brace_level;
                        $declared_interfaces[] = $current_interface;
                    } elseif ($look_for == T_IMPLEMENTS) {
                        $implements[$current_class] = $data;
                    } elseif ($look_for == T_EXTENDS) {
                        $extends[$current_class] = $data;
                    } elseif ($look_for == T_FUNCTION) {
                        if ($current_class) {
                            $current_function = "$current_class::$data";
                            $declared_methods[$current_class][] = $data;
                        } elseif ($current_interface) {
                            $current_function = "$current_interface::$data";
                            $declared_methods[$current_interface][] = $data;
                        } else {
                            $current_function = $data;
                            $declared_functions[] = $current_function;
                        }
                        $current_function_level = $brace_level;
                        $m = array();
                    } elseif ($look_for == T_NEW) {
                        $used_classes[$data] = true;
                    }
                    $look_for = 0;
                    continue 2;
                case T_VARIABLE:
                    $look_for = 0;
                    continue 2;
                case T_DOC_COMMENT:
                case T_COMMENT:
                    if (preg_match('!^/\*\*\s!', $data)) {
                        $lastphpdoc = $data;
                        if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
                            $nodeps = array_merge($nodeps, $m[1]);
                        }
                    }
                    continue 2;
                case T_DOUBLE_COLON:
                    if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) {
                        $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE,
                            array('file' => $file));
                        return false;
                    }
                    $class = $tokens[$i - 1][1];
                    if (strtolower($class) != 'parent') {
                        $used_classes[$class] = true;
                    }
                    continue 2;
            }
        }
        return array(
            "source_file" => $file,
            "declared_classes" => $declared_classes,
            "declared_interfaces" => $declared_interfaces,
            "declared_methods" => $declared_methods,
            "declared_functions" => $declared_functions,
            "used_classes" => array_diff(array_keys($used_classes), $nodeps),
            "inheritance" => $extends,
            "implements" => $implements,
            );
    }

    /**
     * Build a "provides" array from data returned by
     * analyzeSourceCode().  The format of the built array is like
     * this:
     *
     *  array(
     *    'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
     *    ...
     *  )
     *
     *
     * @param array $srcinfo array with information about a source file
     * as returned by the analyzeSourceCode() method.
     *
     * @return void
     *
     * @access private
     *
     */
    function _buildProvidesArray($srcinfo)
    {
        if (!$this->_isValid) {
            return false;
        }
        $file = basename($srcinfo['source_file']);
        $pn = $this->getPackage();
        $pnl = strlen($pn);
        foreach ($srcinfo['declared_classes'] as $class) {
            $key = "class;$class";
            if (isset($this->_packageInfo['provides'][$key])) {
                continue;
            }
            $this->_packageInfo['provides'][$key] =
                array('file'=> $file, 'type' => 'class', 'name' => $class);
            if (isset($srcinfo['inheritance'][$class])) {
                $this->_packageInfo['provides'][$key]['extends'] =
                    $srcinfo['inheritance'][$class];
            }
        }
        foreach ($srcinfo['declared_methods'] as $class => $methods) {
            foreach ($methods as $method) {
                $function = "$class::$method";
                $key = "function;$function";
                if ($method{0} == '_' || !strcasecmp($method, $class) ||
                    isset($this->_packageInfo['provides'][$key])) {
                    continue;
                }
                $this->_packageInfo['provides'][$key] =
                    array('file'=> $file, 'type' => 'function', 'name' => $function);
            }
        }

        foreach ($srcinfo['declared_functions'] as $function) {
            $key = "function;$function";
            if ($function{0} == '_' || isset($this->_packageInfo['provides'][$key])) {
                continue;
            }
            if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
                $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
            }
            $this->_packageInfo['provides'][$key] =
                array('file'=> $file, 'type' => 'function', 'name' => $function);
        }
    }

    // }}}
}
?>
PEAR-1.7.2/PEAR/PackageFile/v2.php100644   1750   1750      210330 11014312742  11446 <?php
/**
 * PEAR_PackageFile_v2, package.xml version 2.0
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: v2.php,v 1.143 2008/05/13 05:28:51 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * For error handling
 */
require_once 'PEAR/ErrorStack.php';
/**
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_PackageFile_v2
{

    /**
     * Parsed package information
     * @var array
     * @access private
     */
    var $_packageInfo = array();

    /**
     * path to package .tgz or false if this is a local/extracted package.xml
     * @var string|false
     * @access private
     */
    var $_archiveFile;

    /**
     * path to package .xml or false if this is an abstract parsed-from-string xml
     * @var string|false
     * @access private
     */
    var $_packageFile;

    /**
     * This is used by file analysis routines to log progress information
     * @var PEAR_Common
     * @access protected
     */
    var $_logger;

    /**
     * This is set to the highest validation level that has been validated
     *
     * If the package.xml is invalid or unknown, this is set to 0.  If
     * normal validation has occurred, this is set to PEAR_VALIDATE_NORMAL.  If
     * downloading/installation validation has occurred it is set to PEAR_VALIDATE_DOWNLOADING
     * or INSTALLING, and so on up to PEAR_VALIDATE_PACKAGING.  This allows validation
     * "caching" to occur, which is particularly important for package validation, so
     * that PHP files are not validated twice
     * @var int
     * @access private
     */
    var $_isValid = 0;

    /**
     * True if the filelist has been validated
     * @param bool
     */
    var $_filesValid = false;

    /**
     * @var PEAR_Registry
     * @access protected
     */
    var $_registry;

    /**
     * @var PEAR_Config
     * @access protected
     */
    var $_config;

    /**
     * Optional Dependency group requested for installation
     * @var string
     * @access private
     */
    var $_requestedGroup = false;

    /**
     * @var PEAR_ErrorStack
     * @access protected
     */
    var $_stack;

    /**
     * Namespace prefix used for tasks in this package.xml - use tasks: whenever possible
     */
    var $_tasksNs;

    /**
     * Determines whether this packagefile was initialized only with partial package info
     *
     * If this package file was constructed via parsing REST, it will only contain
     *
     * - package name
     * - channel name
     * - dependencies
     * @var boolean
     * @access private
     */
    var $_incomplete = true;

    /**
     * @var PEAR_PackageFile_v2_Validator
     */
    var $_v2Validator;

    /**
     * The constructor merely sets up the private error stack
     */
    function PEAR_PackageFile_v2()
    {
        $this->_stack = new PEAR_ErrorStack('PEAR_PackageFile_v2', false, null);
        $this->_isValid = false;
    }

    /**
     * To make unit-testing easier
     * @param PEAR_Frontend_*
     * @param array options
     * @param PEAR_Config
     * @return PEAR_Downloader
     * @access protected
     */
    function &getPEARDownloader(&$i, $o, &$c)
    {
        $z = &new PEAR_Downloader($i, $o, $c);
        return $z;
    }

    /**
     * To make unit-testing easier
     * @param PEAR_Config
     * @param array options
     * @param array package name as returned from {@link PEAR_Registry::parsePackageName()}
     * @param int PEAR_VALIDATE_* constant
     * @return PEAR_Dependency2
     * @access protected
     */
    function &getPEARDependency2(&$c, $o, $p, $s = PEAR_VALIDATE_INSTALLING)
    {
        if (!class_exists('PEAR_Dependency2')) {
            require_once 'PEAR/Dependency2.php';
        }
        $z = &new PEAR_Dependency2($c, $o, $p, $s);
        return $z;
    }

    function getInstalledBinary()
    {
        return isset($this->_packageInfo['#binarypackage']) ? $this->_packageInfo['#binarypackage'] :
            false;
    }

    /**
     * Installation of source package has failed, attempt to download and install the
     * binary version of this package.
     * @param PEAR_Installer
     * @return array|false
     */
    function installBinary(&$installer)
    {
        if (!OS_WINDOWS) {
            $a = false;
            return $a;
        }
        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
            $releasetype = $this->getPackageType() . 'release';
            if (!is_array($installer->getInstallPackages())) {
                $a = false;
                return $a;
            }
            foreach ($installer->getInstallPackages() as $p) {
                if ($p->isExtension($this->_packageInfo['providesextension'])) {
                    if ($p->getPackageType() != 'extsrc' && $p->getPackageType() != 'zendextsrc') {
                        $a = false;
                        return $a; // the user probably downloaded it separately
                    }
                }
            }
            if (isset($this->_packageInfo[$releasetype]['binarypackage'])) {
                $installer->log(0, 'Attempting to download binary version of extension "' .
                    $this->_packageInfo['providesextension'] . '"');
                $params = $this->_packageInfo[$releasetype]['binarypackage'];
                if (!is_array($params) || !isset($params[0])) {
                    $params = array($params);
                }
                if (isset($this->_packageInfo['channel'])) {
                    foreach ($params as $i => $param) {
                        $params[$i] = array('channel' => $this->_packageInfo['channel'],
                            'package' => $param, 'version' => $this->getVersion());
                    }
                }
                $dl = &$this->getPEARDownloader($installer->ui, $installer->getOptions(),
                    $installer->config);
                $verbose = $dl->config->get('verbose');
                $dl->config->set('verbose', -1);
                foreach ($params as $param) {
                    PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                    $ret = $dl->download(array($param));
                    PEAR::popErrorHandling();
                    if (is_array($ret) && count($ret)) {
                        break;
                    }
                }
                $dl->config->set('verbose', $verbose);
                if (is_array($ret)) {
                    if (count($ret) == 1) {
                        $pf = $ret[0]->getPackageFile();
                        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                        $err = $installer->install($ret[0]);
                        PEAR::popErrorHandling();
                        if (is_array($err)) {
                            $this->_packageInfo['#binarypackage'] = $ret[0]->getPackage();
                            // "install" self, so all dependencies will work transparently
                            $this->_registry->addPackage2($this);
                            $installer->log(0, 'Download and install of binary extension "' .
                                $this->_registry->parsedPackageNameToString(
                                    array('channel' => $pf->getChannel(),
                                          'package' => $pf->getPackage()), true) . '" successful');
                            $a = array($ret[0], $err);
                            return $a;
                        }
                        $installer->log(0, 'Download and install of binary extension "' .
                            $this->_registry->parsedPackageNameToString(
                                    array('channel' => $pf->getChannel(),
                                          'package' => $pf->getPackage()), true) . '" failed');
                    }
                }
            }
        }
        $a = false;
        return $a;
    }

    /**
     * @return string|false Extension name
     */
    function getProvidesExtension()
    {
        if (in_array($this->getPackageType(),
              array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
            if (isset($this->_packageInfo['providesextension'])) {
                return $this->_packageInfo['providesextension'];
            }
        }
        return false;
    }

    /**
     * @param string Extension name
     * @return bool
     */
    function isExtension($extension)
    {
        if (in_array($this->getPackageType(),
              array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
            return $this->_packageInfo['providesextension'] == $extension;
        }
        return false;
    }

    /**
     * Tests whether every part of the package.xml 1.0 is represented in
     * this package.xml 2.0
     * @param PEAR_PackageFile_v1
     * @return bool
     */
    function isEquivalent($pf1)
    {
        if (!$pf1) {
            return true;
        }
        if ($this->getPackageType() == 'bundle') {
            return false;
        }
        $this->_stack->getErrors(true);
        if (!$pf1->validate(PEAR_VALIDATE_NORMAL)) {
            return false;
        }
        $pass = true;
        if ($pf1->getPackage() != $this->getPackage()) {
            $this->_differentPackage($pf1->getPackage());
            $pass = false;
        }
        if ($pf1->getVersion() != $this->getVersion()) {
            $this->_differentVersion($pf1->getVersion());
            $pass = false;
        }
        if (trim($pf1->getSummary()) != $this->getSummary()) {
            $this->_differentSummary($pf1->getSummary());
            $pass = false;
        }
        if (preg_replace('/\s+/', '', $pf1->getDescription()) !=
              preg_replace('/\s+/', '', $this->getDescription())) {
            $this->_differentDescription($pf1->getDescription());
            $pass = false;
        }
        if ($pf1->getState() != $this->getState()) {
            $this->_differentState($pf1->getState());
            $pass = false;
        }
        if (!strstr(preg_replace('/\s+/', '', $this->getNotes()),
              preg_replace('/\s+/', '', $pf1->getNotes()))) {
            $this->_differentNotes($pf1->getNotes());
            $pass = false;
        }
        $mymaintainers = $this->getMaintainers();
        $yourmaintainers = $pf1->getMaintainers();
        for ($i1 = 0; $i1 < count($yourmaintainers); $i1++) {
            $reset = false;
            for ($i2 = 0; $i2 < count($mymaintainers); $i2++) {
                if ($mymaintainers[$i2]['handle'] == $yourmaintainers[$i1]['handle']) {
                    if ($mymaintainers[$i2]['role'] != $yourmaintainers[$i1]['role']) {
                        $this->_differentRole($mymaintainers[$i2]['handle'],
                            $yourmaintainers[$i1]['role'], $mymaintainers[$i2]['role']);
                        $pass = false;
                    }
                    if ($mymaintainers[$i2]['email'] != $yourmaintainers[$i1]['email']) {
                        $this->_differentEmail($mymaintainers[$i2]['handle'],
                            $yourmaintainers[$i1]['email'], $mymaintainers[$i2]['email']);
                        $pass = false;
                    }
                    if ($mymaintainers[$i2]['name'] != $yourmaintainers[$i1]['name']) {
                        $this->_differentName($mymaintainers[$i2]['handle'],
                            $yourmaintainers[$i1]['name'], $mymaintainers[$i2]['name']);
                        $pass = false;
                    }
                    unset($mymaintainers[$i2]);
                    $mymaintainers = array_values($mymaintainers);
                    unset($yourmaintainers[$i1]);
                    $yourmaintainers = array_values($yourmaintainers);
                    $reset = true;
                    break;
                }
            }
            if ($reset) {
                $i1 = -1;
            }
        }
        $this->_unmatchedMaintainers($mymaintainers, $yourmaintainers);
        $filelist = $this->getFilelist();
        foreach ($pf1->getFilelist() as $file => $atts) {
            if (!isset($filelist[$file])) {
                $this->_missingFile($file);
                $pass = false;
            }
        }
        return $pass;
    }

    function _differentPackage($package)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('package' => $package,
            'self' => $this->getPackage()),
            'package.xml 1.0 package "%package%" does not match "%self%"');
    }

    function _differentVersion($version)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('version' => $version,
            'self' => $this->getVersion()),
            'package.xml 1.0 version "%version%" does not match "%self%"');
    }

    function _differentState($state)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('state' => $state,
            'self' => $this->getState()),
            'package.xml 1.0 state "%state%" does not match "%self%"');
    }

    function _differentRole($handle, $role, $selfrole)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
            'role' => $role, 'self' => $selfrole),
            'package.xml 1.0 maintainer "%handle%" role "%role%" does not match "%self%"');
    }

    function _differentEmail($handle, $email, $selfemail)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
            'email' => $email, 'self' => $selfemail),
            'package.xml 1.0 maintainer "%handle%" email "%email%" does not match "%self%"');
    }

    function _differentName($handle, $name, $selfname)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
            'name' => $name, 'self' => $selfname),
            'package.xml 1.0 maintainer "%handle%" name "%name%" does not match "%self%"');
    }

    function _unmatchedMaintainers($my, $yours)
    {
        if ($my) {
            array_walk($my, create_function('&$i, $k', '$i = $i["handle"];'));
            $this->_stack->push(__FUNCTION__, 'error', array('handles' => $my),
                'package.xml 2.0 has unmatched extra maintainers "%handles%"');
        }
        if ($yours) {
            array_walk($yours, create_function('&$i, $k', '$i = $i["handle"];'));
            $this->_stack->push(__FUNCTION__, 'error', array('handles' => $yours),
                'package.xml 1.0 has unmatched extra maintainers "%handles%"');
        }
    }

    function _differentNotes($notes)
    {
        $truncnotes = strlen($notes) < 25 ? $notes : substr($notes, 0, 24) . '...';
        $truncmynotes = strlen($this->getNotes()) < 25 ? $this->getNotes() :
            substr($this->getNotes(), 0, 24) . '...';
        $this->_stack->push(__FUNCTION__, 'error', array('notes' => $truncnotes,
            'self' => $truncmynotes),
            'package.xml 1.0 release notes "%notes%" do not match "%self%"');
    }

    function _differentSummary($summary)
    {
        $truncsummary = strlen($summary) < 25 ? $summary : substr($summary, 0, 24) . '...';
        $truncmysummary = strlen($this->getsummary()) < 25 ? $this->getSummary() :
            substr($this->getsummary(), 0, 24) . '...';
        $this->_stack->push(__FUNCTION__, 'error', array('summary' => $truncsummary,
            'self' => $truncmysummary),
            'package.xml 1.0 summary "%summary%" does not match "%self%"');
    }

    function _differentDescription($description)
    {
        $truncdescription = trim(strlen($description) < 25 ? $description : substr($description, 0, 24) . '...');
        $truncmydescription = trim(strlen($this->getDescription()) < 25 ? $this->getDescription() :
            substr($this->getdescription(), 0, 24) . '...');
        $this->_stack->push(__FUNCTION__, 'error', array('description' => $truncdescription,
            'self' => $truncmydescription),
            'package.xml 1.0 description "%description%" does not match "%self%"');
    }

    function _missingFile($file)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
            'package.xml 1.0 file "%file%" is not present in <contents>');
    }

    /**
     * WARNING - do not use this function unless you know what you're doing
     */
    function setRawState($state)
    {
        if (!isset($this->_packageInfo['stability'])) {
            $this->_packageInfo['stability'] = array();
        }
        $this->_packageInfo['stability']['release'] = $state;
    }

    /**
     * WARNING - do not use this function unless you know what you're doing
     */
    function setRawCompatible($compatible)
    {
        $this->_packageInfo['compatible'] = $compatible;
    }

    /**
     * WARNING - do not use this function unless you know what you're doing
     */
    function setRawPackage($package)
    {
        $this->_packageInfo['name'] = $package;
    }

    /**
     * WARNING - do not use this function unless you know what you're doing
     */
    function setRawChannel($channel)
    {
        $this->_packageInfo['channel'] = $channel;
    }

    function setRequestedGroup($group)
    {
        $this->_requestedGroup = $group;
    }

    function getRequestedGroup()
    {
        if (isset($this->_requestedGroup)) {
            return $this->_requestedGroup;
        }
        return false;
    }

    /**
     * For saving in the registry.
     *
     * Set the last version that was installed
     * @param string
     */
    function setLastInstalledVersion($version)
    {
        $this->_packageInfo['_lastversion'] = $version;
    }

    /**
     * @return string|false
     */
    function getLastInstalledVersion()
    {
        if (isset($this->_packageInfo['_lastversion'])) {
            return $this->_packageInfo['_lastversion'];
        }
        return false;
    }

    /**
     * Determines whether this package.xml has post-install scripts or not
     * @return array|false
     */
    function listPostinstallScripts()
    {
        $filelist = $this->getFilelist();
        $contents = $this->getContents();
        $contents = $contents['dir']['file'];
        if (!is_array($contents) || !isset($contents[0])) {
            $contents = array($contents);
        }
        $taskfiles = array();
        foreach ($contents as $file) {
            $atts = $file['attribs'];
            unset($file['attribs']);
            if (count($file)) {
                $taskfiles[$atts['name']] = $file;
            }
        }
        $common = new PEAR_Common;
        $common->debug = $this->_config->get('verbose');
        $this->_scripts = array();
        $ret = array();
        foreach ($taskfiles as $name => $tasks) {
            if (!isset($filelist[$name])) {
                // ignored files will not be in the filelist
                continue;
            }
            $atts = $filelist[$name];
            foreach ($tasks as $tag => $raw) {
                $task = $this->getTask($tag);
                $task = &new $task($this->_config, $common, PEAR_TASK_INSTALL);
                if ($task->isScript()) {
                    $ret[] = $filelist[$name]['installed_as'];
                }
            }
        }
        if (count($ret)) {
            return $ret;
        }
        return false;
    }

    /**
     * Initialize post-install scripts for running
     *
     * This method can be used to detect post-install scripts, as the return value
     * indicates whether any exist
     * @return bool
     */
    function initPostinstallScripts()
    {
        $filelist = $this->getFilelist();
        $contents = $this->getContents();
        $contents = $contents['dir']['file'];
        if (!is_array($contents) || !isset($contents[0])) {
            $contents = array($contents);
        }
        $taskfiles = array();
        foreach ($contents as $file) {
            $atts = $file['attribs'];
            unset($file['attribs']);
            if (count($file)) {
                $taskfiles[$atts['name']] = $file;
            }
        }
        $common = new PEAR_Common;
        $common->debug = $this->_config->get('verbose');
        $this->_scripts = array();
        foreach ($taskfiles as $name => $tasks) {
            if (!isset($filelist[$name])) {
                // file was not installed due to installconditions
                continue;
            }
            $atts = $filelist[$name];
            foreach ($tasks as $tag => $raw) {
                $taskname = $this->getTask($tag);
                $task = &new $taskname($this->_config, $common, PEAR_TASK_INSTALL);
                if (!$task->isScript()) {
                    continue; // scripts are only handled after installation
                }
                $lastversion = isset($this->_packageInfo['_lastversion']) ?
                    $this->_packageInfo['_lastversion'] : null;
                $task->init($raw, $atts, $lastversion);
                $res = $task->startSession($this, $atts['installed_as']);
                if (!$res) {
                    continue; // skip this file
                }
                if (PEAR::isError($res)) {
                    return $res;
                }
                $assign = &$task;
                $this->_scripts[] = &$assign;
            }
        }
        if (count($this->_scripts)) {
            return true;
        }
        return false;
    }

    function runPostinstallScripts()
    {
        if ($this->initPostinstallScripts()) {
            $ui = &PEAR_Frontend::singleton();
            if ($ui) {
                $ui->runPostinstallScripts($this->_scripts, $this);
            }
        }
    }


    /**
     * Convert a recursive set of <dir> and <file> tags into a single <dir> tag with
     * <file> tags.
     */
    function flattenFilelist()
    {
        if (isset($this->_packageInfo['bundle'])) {
            return;
        }
        $filelist = array();
        if (isset($this->_packageInfo['contents']['dir']['dir'])) {
            $this->_getFlattenedFilelist($filelist, $this->_packageInfo['contents']['dir']);
            if (!isset($filelist[1])) {
                $filelist = $filelist[0];
            }
            $this->_packageInfo['contents']['dir']['file'] = $filelist;
            unset($this->_packageInfo['contents']['dir']['dir']);
        } else {
            // else already flattened but check for baseinstalldir propagation
            if (isset($this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'])) {
                if (isset($this->_packageInfo['contents']['dir']['file'][0])) {
                    foreach ($this->_packageInfo['contents']['dir']['file'] as $i => $file) {
                        if (isset($file['attribs']['baseinstalldir'])) {
                            continue;
                        }
                        $this->_packageInfo['contents']['dir']['file'][$i]['attribs']['baseinstalldir']
                            = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
                    }
                } else {
                    if (!isset($this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir'])) {
                       $this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir']
                            = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
                    }
                }
            }
        }
    }

    /**
     * @param array the final flattened file list
     * @param array the current directory being processed
     * @param string|false any recursively inherited baeinstalldir attribute
     * @param string private recursion variable
     * @return array
     * @access protected
     */
    function _getFlattenedFilelist(&$files, $dir, $baseinstall = false, $path = '')
    {
        if (isset($dir['attribs']) && isset($dir['attribs']['baseinstalldir'])) {
            $baseinstall = $dir['attribs']['baseinstalldir'];
        }
        if (isset($dir['dir'])) {
            if (!isset($dir['dir'][0])) {
                $dir['dir'] = array($dir['dir']);
            }
            foreach ($dir['dir'] as $subdir) {
                if (!isset($subdir['attribs']) || !isset($subdir['attribs']['name'])) {
                    $name = '*unknown*';
                } else {
                    $name = $subdir['attribs']['name'];
                }
                $newpath = empty($path) ? $name :
                    $path . '/' . $name;
                $this->_getFlattenedFilelist($files, $subdir,
                    $baseinstall, $newpath);
            }
        }
        if (isset($dir['file'])) {
            if (!isset($dir['file'][0])) {
                $dir['file'] = array($dir['file']);
            }
            foreach ($dir['file'] as $file) {
                $attrs = $file['attribs'];
                $name = $attrs['name'];
                if ($baseinstall && !isset($attrs['baseinstalldir'])) {
                    $attrs['baseinstalldir'] = $baseinstall;
                }
                $attrs['name'] = empty($path) ? $name : $path . '/' . $name;
                $attrs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
                    $attrs['name']);
                $file['attribs'] = $attrs;
                $files[] = $file;
            }
        }
    }

    function setConfig(&$config)
    {
        $this->_config = &$config;
        $this->_registry = &$config->getRegistry();
    }

    function setLogger(&$logger)
    {
        if (!is_object($logger) || !method_exists($logger, 'log')) {
            return PEAR::raiseError('Logger must be compatible with PEAR_Common::log');
        }
        $this->_logger = &$logger;
    }

    /**
     * WARNING - do not use this function directly unless you know what you're doing
     */
    function setDeps($deps)
    {
        $this->_packageInfo['dependencies'] = $deps;
    }

    /**
     * WARNING - do not use this function directly unless you know what you're doing
     */
    function setCompatible($compat)
    {
        $this->_packageInfo['compatible'] = $compat;
    }

    function setPackagefile($file, $archive = false)
    {
        $this->_packageFile = $file;
        $this->_archiveFile = $archive ? $archive : $file;
    }

    /**
     * Wrapper to {@link PEAR_ErrorStack::getErrors()}
     * @param boolean determines whether to purge the error stack after retrieving
     * @return array
     */
    function getValidationWarnings($purge = true)
    {
        return $this->_stack->getErrors($purge);
    }

    function getPackageFile()
    {
        return $this->_packageFile;
    }

    function getArchiveFile()
    {
        return $this->_archiveFile;
    }


    /**
     * Directly set the array that defines this packagefile
     *
     * WARNING: no validation.  This should only be performed by internal methods
     * inside PEAR or by inputting an array saved from an existing PEAR_PackageFile_v2
     * @param array
     */
    function fromArray($pinfo)
    {
        unset($pinfo['old']);
        unset($pinfo['xsdversion']);
        $this->_incomplete = false;
        $this->_packageInfo = $pinfo;
    }

    function isIncomplete()
    {
        return $this->_incomplete;
    }

    /**
     * @return array
     */
    function toArray($forreg = false)
    {
        if (!$this->validate(PEAR_VALIDATE_NORMAL)) {
            return false;
        }
        return $this->getArray($forreg);
    }

    function getArray($forReg = false)
    {
        if ($forReg) {
            $arr = $this->_packageInfo;
            $arr['old'] = array();
            $arr['old']['version'] = $this->getVersion();
            $arr['old']['release_date'] = $this->getDate();
            $arr['old']['release_state'] = $this->getState();
            $arr['old']['release_license'] = $this->getLicense();
            $arr['old']['release_notes'] = $this->getNotes();
            $arr['old']['release_deps'] = $this->getDeps();
            $arr['old']['maintainers'] = $this->getMaintainers();
            $arr['xsdversion'] = '2.0';
            return $arr;
        } else {
            $info = $this->_packageInfo;
            unset($info['dirtree']);
            if (isset($info['_lastversion'])) {
                unset($info['_lastversion']);
            }
            if (isset($info['#binarypackage'])) {
                unset($info['#binarypackage']);
            }
            return $info;
        }
    }

    function packageInfo($field)
    {
        $arr = $this->getArray(true);
        if ($field == 'state') {
            return $arr['stability']['release'];
        }
        if ($field == 'api-version') {
            return $arr['version']['api'];
        }
        if ($field == 'api-state') {
            return $arr['stability']['api'];
        }
        if (isset($arr['old'][$field])) {
            if (!is_string($arr['old'][$field])) {
                return null;
            }
            return $arr['old'][$field];
        }
        if (isset($arr[$field])) {
            if (!is_string($arr[$field])) {
                return null;
            }
            return $arr[$field];
        }
        return null;
    }

    function getName()
    {
        return $this->getPackage();
    }

    function getPackage()
    {
        if (isset($this->_packageInfo['name'])) {
            return $this->_packageInfo['name'];
        }
        return false;
    }

    function getChannel()
    {
        if (isset($this->_packageInfo['uri'])) {
            return '__uri';
        }
        if (isset($this->_packageInfo['channel'])) {
            return strtolower($this->_packageInfo['channel']);
        }
        return false;
    }

    function getUri()
    {
        if (isset($this->_packageInfo['uri'])) {
            return $this->_packageInfo['uri'];
        }
        return false;
    }

    function getExtends()
    {
        if (isset($this->_packageInfo['extends'])) {
            return $this->_packageInfo['extends'];
        }
        return false;
    }

    function getSummary()
    {
        if (isset($this->_packageInfo['summary'])) {
            return $this->_packageInfo['summary'];
        }
        return false;
    }

    function getDescription()
    {
        if (isset($this->_packageInfo['description'])) {
            return $this->_packageInfo['description'];
        }
        return false;
    }

    function getMaintainers($raw = false)
    {
        if (!isset($this->_packageInfo['lead'])) {
            return false;
        }
        if ($raw) {
            $ret = array('lead' => $this->_packageInfo['lead']);
            (isset($this->_packageInfo['developer'])) ?
                $ret['developer'] = $this->_packageInfo['developer'] :null;
            (isset($this->_packageInfo['contributor'])) ?
                $ret['contributor'] = $this->_packageInfo['contributor'] :null;
            (isset($this->_packageInfo['helper'])) ?
                $ret['helper'] = $this->_packageInfo['helper'] :null;
            return $ret;
        } else {
            $ret = array();
            $leads = isset($this->_packageInfo['lead'][0]) ? $this->_packageInfo['lead'] :
                array($this->_packageInfo['lead']);
            foreach ($leads as $lead) {
                $s = $lead;
                $s['handle'] = $s['user'];
                unset($s['user']);
                $s['role'] = 'lead';
                $ret[] = $s;
            }
            if (isset($this->_packageInfo['developer'])) {
                $leads = isset($this->_packageInfo['developer'][0]) ?
                    $this->_packageInfo['developer'] :
                    array($this->_packageInfo['developer']);
                foreach ($leads as $maintainer) {
                    $s = $maintainer;
                    $s['handle'] = $s['user'];
                    unset($s['user']);
                    $s['role'] = 'developer';
                    $ret[] = $s;
                }
            }
            if (isset($this->_packageInfo['contributor'])) {
                $leads = isset($this->_packageInfo['contributor'][0]) ?
                    $this->_packageInfo['contributor'] :
                    array($this->_packageInfo['contributor']);
                foreach ($leads as $maintainer) {
                    $s = $maintainer;
                    $s['handle'] = $s['user'];
                    unset($s['user']);
                    $s['role'] = 'contributor';
                    $ret[] = $s;
                }
            }
            if (isset($this->_packageInfo['helper'])) {
                $leads = isset($this->_packageInfo['helper'][0]) ?
                    $this->_packageInfo['helper'] :
                    array($this->_packageInfo['helper']);
                foreach ($leads as $maintainer) {
                    $s = $maintainer;
                    $s['handle'] = $s['user'];
                    unset($s['user']);
                    $s['role'] = 'helper';
                    $ret[] = $s;
                }
            }
            return $ret;
        }
        return false;
    }

    function getLeads()
    {
        if (isset($this->_packageInfo['lead'])) {
            return $this->_packageInfo['lead'];
        }
        return false;
    }

    function getDevelopers()
    {
        if (isset($this->_packageInfo['developer'])) {
            return $this->_packageInfo['developer'];
        }
        return false;
    }

    function getContributors()
    {
        if (isset($this->_packageInfo['contributor'])) {
            return $this->_packageInfo['contributor'];
        }
        return false;
    }

    function getHelpers()
    {
        if (isset($this->_packageInfo['helper'])) {
            return $this->_packageInfo['helper'];
        }
        return false;
    }

    function setDate($date)
    {
        if (!isset($this->_packageInfo['date'])) {
            // ensure that the extends tag is set up in the right location
            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
                array('time', 'version',
                    'stability', 'license', 'notes', 'contents', 'compatible',
                    'dependencies', 'providesextension', 'srcpackage', 'srcuri',
                    'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
                    'zendextbinrelease', 'bundle', 'changelog'), array(), 'date');
        }
        $this->_packageInfo['date'] = $date;
        $this->_isValid = 0;
    }

    function setTime($time)
    {
        $this->_isValid = 0;
        if (!isset($this->_packageInfo['time'])) {
            // ensure that the time tag is set up in the right location
            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
                    array('version',
                    'stability', 'license', 'notes', 'contents', 'compatible',
                    'dependencies', 'providesextension', 'srcpackage', 'srcuri',
                    'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
                    'zendextbinrelease', 'bundle', 'changelog'), $time, 'time');
        }
        $this->_packageInfo['time'] = $time;
    }

    function getDate()
    {
        if (isset($this->_packageInfo['date'])) {
            return $this->_packageInfo['date'];
        }
        return false;
    }

    function getTime()
    {
        if (isset($this->_packageInfo['time'])) {
            return $this->_packageInfo['time'];
        }
        return false;
    }

    /**
     * @param package|api version category to return
     */
    function getVersion($key = 'release')
    {
        if (isset($this->_packageInfo['version'][$key])) {
            return $this->_packageInfo['version'][$key];
        }
        return false;
    }

    function getStability()
    {
        if (isset($this->_packageInfo['stability'])) {
            return $this->_packageInfo['stability'];
        }
        return false;
    }

    function getState($key = 'release')
    {
        if (isset($this->_packageInfo['stability'][$key])) {
            return $this->_packageInfo['stability'][$key];
        }
        return false;
    }

    function getLicense($raw = false)
    {
        if (isset($this->_packageInfo['license'])) {
            if ($raw) {
                return $this->_packageInfo['license'];
            }
            if (is_array($this->_packageInfo['license'])) {
                return $this->_packageInfo['license']['_content'];
            } else {
                return $this->_packageInfo['license'];
            }
        }
        return false;
    }

    function getLicenseLocation()
    {
        if (!isset($this->_packageInfo['license']) || !is_array($this->_packageInfo['license'])) {
            return false;
        }
        return $this->_packageInfo['license']['attribs'];
    }

    function getNotes()
    {
        if (isset($this->_packageInfo['notes'])) {
            return $this->_packageInfo['notes'];
        }
        return false;
    }

    /**
     * Return the <usesrole> tag contents, if any
     * @return array|false
     */
    function getUsesrole()
    {
        if (isset($this->_packageInfo['usesrole'])) {
            return $this->_packageInfo['usesrole'];
        }
        return false;
    }

    /**
     * Return the <usestask> tag contents, if any
     * @return array|false
     */
    function getUsestask()
    {
        if (isset($this->_packageInfo['usestask'])) {
            return $this->_packageInfo['usestask'];
        }
        return false;
    }

    /**
     * This should only be used to retrieve filenames and install attributes
     */
    function getFilelist($preserve = false)
    {
        if (isset($this->_packageInfo['filelist']) && !$preserve) {
            return $this->_packageInfo['filelist'];
        }
        $this->flattenFilelist();
        if ($contents = $this->getContents()) {
            $ret = array();
            if (!isset($contents['dir'])) {
                return false;
            }
            if (!isset($contents['dir']['file'][0])) {
                $contents['dir']['file'] = array($contents['dir']['file']);
            }
            foreach ($contents['dir']['file'] as $file) {
                $name = $file['attribs']['name'];
                if (!$preserve) {
                    $file = $file['attribs'];
                }
                $ret[$name] = $file;
            }
            if (!$preserve) {
                $this->_packageInfo['filelist'] = $ret;
            }
            return $ret;
        }
        return false;
    }

    /**
     * Return configure options array, if any
     *
     * @return array|false
     */
    function getConfigureOptions()
    {
        if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
            return false;
        }
        $releases = $this->getReleases();
        if (isset($releases[0])) {
            $releases = $releases[0];
        }
        if (isset($releases['configureoption'])) {
            if (!isset($releases['configureoption'][0])) {
                $releases['configureoption'] = array($releases['configureoption']);
            }
            for ($i = 0; $i < count($releases['configureoption']); $i++) {
                $releases['configureoption'][$i] = $releases['configureoption'][$i]['attribs'];
            }
            return $releases['configureoption'];
        }
        return false;
    }

    /**
     * This is only used at install-time, after all serialization
     * is over.
     */
    function resetFilelist()
    {
        $this->_packageInfo['filelist'] = array();
    }

    /**
     * Retrieve a list of files that should be installed on this computer
     * @return array
     */
    function getInstallationFilelist($forfilecheck = false)
    {
        $contents = $this->getFilelist(true);
        if (isset($contents['dir']['attribs']['baseinstalldir'])) {
            $base = $contents['dir']['attribs']['baseinstalldir'];
        }
        if (isset($this->_packageInfo['bundle'])) {
            return PEAR::raiseError(
                'Exception: bundles should be handled in download code only');
        }
        $release = $this->getReleases();
        if ($release) {
            if (!isset($release[0])) {
                if (!isset($release['installconditions']) && !isset($release['filelist'])) {
                    if ($forfilecheck) {
                        return $this->getFilelist();
                    }
                    return $contents;
                }
                $release = array($release);
            }
            $depchecker = &$this->getPEARDependency2($this->_config, array(),
                array('channel' => $this->getChannel(), 'package' => $this->getPackage()),
                PEAR_VALIDATE_INSTALLING);
            foreach ($release as $instance) {
                if (isset($instance['installconditions'])) {
                    $installconditions = $instance['installconditions'];
                    if (is_array($installconditions)) {
                        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                        foreach ($installconditions as $type => $conditions) {
                            if (!isset($conditions[0])) {
                                $conditions = array($conditions);
                            }
                            foreach ($conditions as $condition) {
                                $ret = $depchecker->{"validate{$type}Dependency"}($condition);
                                if (PEAR::isError($ret)) {
                                    PEAR::popErrorHandling();
                                    continue 3; // skip this release
                                }
                            }
                        }
                        PEAR::popErrorHandling();
                    }
                }
                // this is the release to use
                if (isset($instance['filelist'])) {
                    // ignore files
                    if (isset($instance['filelist']['ignore'])) {
                        $ignore = isset($instance['filelist']['ignore'][0]) ?
                            $instance['filelist']['ignore'] :
                            array($instance['filelist']['ignore']);
                        foreach ($ignore as $ig) {
                            unset ($contents[$ig['attribs']['name']]);
                        }
                    }
                    // install files as this name
                    if (isset($instance['filelist']['install'])) {
                        $installas = isset($instance['filelist']['install'][0]) ?
                            $instance['filelist']['install'] :
                            array($instance['filelist']['install']);
                        foreach ($installas as $as) {
                            $contents[$as['attribs']['name']]['attribs']['install-as'] =
                                $as['attribs']['as'];
                        }
                    }
                }
                if ($forfilecheck) {
                    foreach ($contents as $file => $attrs) {
                        $contents[$file] = $attrs['attribs'];
                    }
                }
                return $contents;
            }
        } else { // simple release - no installconditions or install-as
            if ($forfilecheck) {
                return $this->getFilelist();
            }
            return $contents;
        }
        // no releases matched
        return PEAR::raiseError('No releases in package.xml matched the existing operating ' .
            'system, extensions installed, or architecture, cannot install');
    }

    /**
     * This is only used at install-time, after all serialization
     * is over.
     * @param string file name
     * @param string installed path
     */
    function setInstalledAs($file, $path)
    {
        if ($path) {
            return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
        }
        unset($this->_packageInfo['filelist'][$file]['installed_as']);
    }

    function getInstalledLocation($file)
    {
        if (isset($this->_packageInfo['filelist'][$file]['installed_as'])) {
            return $this->_packageInfo['filelist'][$file]['installed_as'];
        }
        return false;
    }

    /**
     * This is only used at install-time, after all serialization
     * is over.
     */
    function installedFile($file, $atts)
    {
        if (isset($this->_packageInfo['filelist'][$file])) {
            $this->_packageInfo['filelist'][$file] =
                array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']);
        } else {
            $this->_packageInfo['filelist'][$file] = $atts['attribs'];
        }
    }

    /**
     * Retrieve the contents tag
     */
    function getContents()
    {
        if (isset($this->_packageInfo['contents'])) {
            return $this->_packageInfo['contents'];
        }
        return false;
    }

    /**
     * @param string full path to file
     * @param string attribute name
     * @param string attribute value
     * @param int risky but fast - use this to choose a file based on its position in the list
     *            of files.  Index is zero-based like PHP arrays.
     * @return bool success of operation
     */
    function setFileAttribute($filename, $attr, $value, $index = false)
    {
        $this->_isValid = 0;
        if (in_array($attr, array('role', 'name', 'baseinstalldir'))) {
            $this->_filesValid = false;
        }
        if ($index !== false &&
              isset($this->_packageInfo['contents']['dir']['file'][$index]['attribs'])) {
            $this->_packageInfo['contents']['dir']['file'][$index]['attribs'][$attr] = $value;
            return true;
        }
        if (!isset($this->_packageInfo['contents']['dir']['file'])) {
            return false;
        }
        $files = $this->_packageInfo['contents']['dir']['file'];
        if (!isset($files[0])) {
            $files = array($files);
            $ind = false;
        } else {
            $ind = true;
        }
        foreach ($files as $i => $file) {
            if (isset($file['attribs'])) {
                if ($file['attribs']['name'] == $filename) {
                    if ($ind) {
                        $this->_packageInfo['contents']['dir']['file'][$i]['attribs'][$attr] = $value;
                    } else {
                        $this->_packageInfo['contents']['dir']['file']['attribs'][$attr] = $value;
                    }
                    return true;
                }
            }
        }
        return false;
    }

    function setDirtree($path)
    {
        if (!isset($this->_packageInfo['dirtree'])) {
            $this->_packageInfo['dirtree'] = array();
        }
        $this->_packageInfo['dirtree'][$path] = true;
    }

    function getDirtree()
    {
        if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) {
            return $this->_packageInfo['dirtree'];
        }
        return false;
    }

    function resetDirtree()
    {
        unset($this->_packageInfo['dirtree']);
    }

    /**
     * Determines whether this package claims it is compatible with the version of
     * the package that has a recommended version dependency
     * @param PEAR_PackageFile_v2|PEAR_PackageFile_v1|PEAR_Downloader_Package
     * @return boolean
     */
    function isCompatible($pf)
    {
        if (!isset($this->_packageInfo['compatible'])) {
            return false;
        }
        if (!isset($this->_packageInfo['channel'])) {
            return false;
        }
        $me = $pf->getVersion();
        $compatible = $this->_packageInfo['compatible'];
        if (!isset($compatible[0])) {
            $compatible = array($compatible);
        }
        $found = false;
        foreach ($compatible as $info) {
            if (strtolower($info['name']) == strtolower($pf->getPackage())) {
                if (strtolower($info['channel']) == strtolower($pf->getChannel())) {
                    $found = true;
                    break;
                }
            }
        }
        if (!$found) {
            return false;
        }
        if (isset($info['exclude'])) {
            if (!isset($info['exclude'][0])) {
                $info['exclude'] = array($info['exclude']);
            }
            foreach ($info['exclude'] as $exclude) {
                if (version_compare($me, $exclude, '==')) {
                    return false;
                }
            }
        }
        if (version_compare($me, $info['min'], '>=') && version_compare($me, $info['max'], '<=')) {
            return true;
        }
        return false;
    }

    /**
     * @return array|false
     */
    function getCompatible()
    {
        if (isset($this->_packageInfo['compatible'])) {
            return $this->_packageInfo['compatible'];
        }
        return false;
    }

    function getDependencies()
    {
        if (isset($this->_packageInfo['dependencies'])) {
            return $this->_packageInfo['dependencies'];
        }
        return false;
    }

    function isSubpackageOf($p)
    {
        return $p->isSubpackage($this);
    }

    /**
     * Determines whether the passed in package is a subpackage of this package.
     *
     * No version checking is done, only name verification.
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @return bool
     */
    function isSubpackage($p)
    {
        $sub = array();
        if (isset($this->_packageInfo['dependencies']['required']['subpackage'])) {
            $sub = $this->_packageInfo['dependencies']['required']['subpackage'];
            if (!isset($sub[0])) {
                $sub = array($sub);
            }
        }
        if (isset($this->_packageInfo['dependencies']['optional']['subpackage'])) {
            $sub1 = $this->_packageInfo['dependencies']['optional']['subpackage'];
            if (!isset($sub1[0])) {
                $sub1 = array($sub1);
            }
            $sub = array_merge($sub, $sub1);
        }
        if (isset($this->_packageInfo['dependencies']['group'])) {
            $group = $this->_packageInfo['dependencies']['group'];
            if (!isset($group[0])) {
                $group = array($group);
            }
            foreach ($group as $deps) {
                if (isset($deps['subpackage'])) {
                    $sub2 = $deps['subpackage'];
                    if (!isset($sub2[0])) {
                        $sub2 = array($sub2);
                    }
                    $sub = array_merge($sub, $sub2);
                }
            }
        }
        foreach ($sub as $dep) {
            if (strtolower($dep['name']) == strtolower($p->getPackage())) {
                if (isset($dep['channel'])) {
                    if (strtolower($dep['channel']) == strtolower($p->getChannel())) {
                        return true;
                    }
                } else {
                    if ($dep['uri'] == $p->getURI()) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    function dependsOn($package, $channel)
    {
        if (!($deps = $this->getDependencies())) {
            return false;
        }
        foreach (array('package', 'subpackage') as $type) {
            foreach (array('required', 'optional') as $needed) {
                if (isset($deps[$needed][$type])) {
                    if (!isset($deps[$needed][$type][0])) {
                        $deps[$needed][$type] = array($deps[$needed][$type]);
                    }
                    foreach ($deps[$needed][$type] as $dep) {
                        $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
                        if (strtolower($dep['name']) == strtolower($package) &&
                              $depchannel == $channel) {
                            return true;
                        }
                    }
                }
            }
            if (isset($deps['group'])) {
                if (!isset($deps['group'][0])) {
                    $dep['group'] = array($deps['group']);
                }
                foreach ($deps['group'] as $group) {
                    if (isset($group[$type])) {
                        if (!is_array($group[$type])) {
                            $group[$type] = array($group[$type]);
                        }
                        foreach ($group[$type] as $dep) {
                            $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
                            if (strtolower($dep['name']) == strtolower($package) &&
                                  $depchannel == $channel) {
                                return true;
                            }
                        }
                    }
                }
            }
        }
        return false;
    }

    /**
     * Get the contents of a dependency group
     * @param string
     * @return array|false
     */
    function getDependencyGroup($name)
    {
        $name = strtolower($name);
        if (!isset($this->_packageInfo['dependencies']['group'])) {
            return false;
        }
        $groups = $this->_packageInfo['dependencies']['group'];
        if (!isset($groups[0])) {
            $groups = array($groups);
        }
        foreach ($groups as $group) {
            if (strtolower($group['attribs']['name']) == $name) {
                return $group;
            }
        }
        return false;
    }

    /**
     * Retrieve a partial package.xml 1.0 representation of dependencies
     *
     * a very limited representation of dependencies is returned by this method.
     * The <exclude> tag for excluding certain versions of a dependency is
     * completely ignored.  In addition, dependency groups are ignored, with the
     * assumption that all dependencies in dependency groups are also listed in
     * the optional group that work with all dependency groups
     * @param boolean return package.xml 2.0 <dependencies> tag
     * @return array|false
     */
    function getDeps($raw = false, $nopearinstaller = false)
    {
        if (isset($this->_packageInfo['dependencies'])) {
            if ($raw) {
                return $this->_packageInfo['dependencies'];
            }
            $ret = array();
            $map = array(
                'php' => 'php',
                'package' => 'pkg',
                'subpackage' => 'pkg',
                'extension' => 'ext',
                'os' => 'os',
                'pearinstaller' => 'pkg',
                );
            foreach (array('required', 'optional') as $type) {
                $optional = ($type == 'optional') ? 'yes' : 'no';
                if (!isset($this->_packageInfo['dependencies'][$type])
                    || empty($this->_packageInfo['dependencies'][$type])) {
                    continue;
                }
                foreach ($this->_packageInfo['dependencies'][$type] as $dtype => $deps) {
                    if ($dtype == 'pearinstaller' && $nopearinstaller) {
                        continue;
                    }
                    if (!isset($deps[0])) {
                        $deps = array($deps);
                    }
                    foreach ($deps as $dep) {
                        if (!isset($map[$dtype])) {
                            // no support for arch type
                            continue;
                        }
                        if ($dtype == 'pearinstaller') {
                            $dep['name'] = 'PEAR';
                            $dep['channel'] = 'pear.php.net';
                        }
                        $s = array('type' => $map[$dtype]);
                        if (isset($dep['channel'])) {
                            $s['channel'] = $dep['channel'];
                        }
                        if (isset($dep['uri'])) {
                            $s['uri'] = $dep['uri'];
                        }
                        if (isset($dep['name'])) {
                            $s['name'] = $dep['name'];
                        }
                        if (isset($dep['conflicts'])) {
                            $s['rel'] = 'not';
                        } else {
                            if (!isset($dep['min']) &&
                                  !isset($dep['max'])) {
                                $s['rel'] = 'has';
                                $s['optional'] = $optional;
                            } elseif (isset($dep['min']) &&
                                  isset($dep['max'])) {
                                $s['rel'] = 'ge';
                                $s1 = $s;
                                $s1['rel'] = 'le';
                                $s['version'] = $dep['min'];
                                $s1['version'] = $dep['max'];
                                if (isset($dep['channel'])) {
                                    $s1['channel'] = $dep['channel'];
                                }
                                if ($dtype != 'php') {
                                    $s['name'] = $dep['name'];
                                    $s1['name'] = $dep['name'];
                                }
                                $s['optional'] = $optional;
                                $s1['optional'] = $optional;
                                $ret[] = $s1;
                            } elseif (isset($dep['min'])) {
                                if (isset($dep['exclude']) &&
                                      $dep['exclude'] == $dep['min']) {
                                    $s['rel'] = 'gt';
                                } else {
                                    $s['rel'] = 'ge';
                                }
                                $s['version'] = $dep['min'];
                                $s['optional'] = $optional;
                                if ($dtype != 'php') {
                                    $s['name'] = $dep['name'];
                                }
                            } elseif (isset($dep['max'])) {
                                if (isset($dep['exclude']) &&
                                      $dep['exclude'] == $dep['max']) {
                                    $s['rel'] = 'lt';
                                } else {
                                    $s['rel'] = 'le';
                                }
                                $s['version'] = $dep['max'];
                                $s['optional'] = $optional;
                                if ($dtype != 'php') {
                                    $s['name'] = $dep['name'];
                                }
                            }
                        }
                        $ret[] = $s;
                    }
                }
            }
            if (count($ret)) {
                return $ret;
            }
        }
        return false;
    }

    /**
     * @return php|extsrc|extbin|zendextsrc|zendextbin|bundle|false
     */
    function getPackageType()
    {
        if (isset($this->_packageInfo['phprelease'])) {
            return 'php';
        }
        if (isset($this->_packageInfo['extsrcrelease'])) {
            return 'extsrc';
        }
        if (isset($this->_packageInfo['extbinrelease'])) {
            return 'extbin';
        }
        if (isset($this->_packageInfo['zendextsrcrelease'])) {
            return 'zendextsrc';
        }
        if (isset($this->_packageInfo['zendextbinrelease'])) {
            return 'zendextbin';
        }
        if (isset($this->_packageInfo['bundle'])) {
            return 'bundle';
        }
        return false;
    }

    /**
     * @return array|false
     */
    function getReleases()
    {
        $type = $this->getPackageType();
        if ($type != 'bundle') {
            $type .= 'release';
        }
        if ($this->getPackageType() && isset($this->_packageInfo[$type])) {
            return $this->_packageInfo[$type];
        }
        return false;
    }

    /**
     * @return array
     */
    function getChangelog()
    {
        if (isset($this->_packageInfo['changelog'])) {
            return $this->_packageInfo['changelog'];
        }
        return false;
    }

    function hasDeps()
    {
        return isset($this->_packageInfo['dependencies']);
    }

    function getPackagexmlVersion()
    {
        if (isset($this->_packageInfo['zendextsrcrelease'])) {
            return '2.1';
        }
        if (isset($this->_packageInfo['zendextbinrelease'])) {
            return '2.1';
        }
        return '2.0';
    }

    /**
     * @return array|false
     */
    function getSourcePackage()
    {
        if (isset($this->_packageInfo['extbinrelease']) ||
              isset($this->_packageInfo['zendextbinrelease'])) {
            return array('channel' => $this->_packageInfo['srcchannel'],
                         'package' => $this->_packageInfo['srcpackage']);
        }
        return false;
    }

    function getBundledPackages()
    {
        if (isset($this->_packageInfo['bundle'])) {
            return $this->_packageInfo['contents']['bundledpackage'];
        }
        return false;
    }

    function getLastModified()
    {
        if (isset($this->_packageInfo['_lastmodified'])) {
            return $this->_packageInfo['_lastmodified'];
        }
        return false;
    }

    /**
     * Get the contents of a file listed within the package.xml
     * @param string
     * @return string
     */
    function getFileContents($file)
    {
        if ($this->_archiveFile == $this->_packageFile) { // unpacked
            $dir = dirname($this->_packageFile);
            $file = $dir . DIRECTORY_SEPARATOR . $file;
            $file = str_replace(array('/', '\\'),
                array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file);
            if (file_exists($file) && is_readable($file)) {
                return implode('', file($file));
            }
        } else { // tgz
            $tar = &new Archive_Tar($this->_archiveFile);
            $tar->pushErrorHandling(PEAR_ERROR_RETURN);
            if ($file != 'package.xml' && $file != 'package2.xml') {
                $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file;
            }
            $file = $tar->extractInString($file);
            $tar->popErrorHandling();
            if (PEAR::isError($file)) {
                return PEAR::raiseError("Cannot locate file '$file' in archive");
            }
            return $file;
        }
    }

    function &getRW()
    {
        if (!class_exists('PEAR_PackageFile_v2_rw')) {
            require_once 'PEAR/PackageFile/v2/rw.php';
        }
        $a = new PEAR_PackageFile_v2_rw;
        foreach (get_object_vars($this) as $name => $unused) {
            if (!isset($this->$name)) {
                continue;
            }
            if ($name == '_config' || $name == '_logger'|| $name == '_registry' ||
                  $name == '_stack') {
                $a->$name = &$this->$name;
            } else {
                $a->$name = $this->$name;
            }
        }
        return $a;
    }

    function &getDefaultGenerator()
    {
        if (!class_exists('PEAR_PackageFile_Generator_v2')) {
            require_once 'PEAR/PackageFile/Generator/v2.php';
        }
        $a = &new PEAR_PackageFile_Generator_v2($this);
        return $a;
    }

    function analyzeSourceCode($file, $string = false)
    {
        if (!isset($this->_v2Validator) ||
              !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) {
            if (!class_exists('PEAR_PackageFile_v2_Validator')) {
                require_once 'PEAR/PackageFile/v2/Validator.php';
            }
            $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
        }
        return $this->_v2Validator->analyzeSourceCode($file, $string);
    }

    function validate($state = PEAR_VALIDATE_NORMAL)
    {
        if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) {
            return false;
        }
        if (!isset($this->_v2Validator) ||
              !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) {
            if (!class_exists('PEAR_PackageFile_v2_Validator')) {
                require_once 'PEAR/PackageFile/v2/Validator.php';
            }
            $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
        }
        if (isset($this->_packageInfo['xsdversion'])) {
            unset($this->_packageInfo['xsdversion']);
        }
        return $this->_v2Validator->validate($this, $state);
    }

    function getTasksNs()
    {
        if (!isset($this->_tasksNs)) {
            if (isset($this->_packageInfo['attribs'])) {
                foreach ($this->_packageInfo['attribs'] as $name => $value) {
                    if ($value == 'http://pear.php.net/dtd/tasks-1.0') {
                        $this->_tasksNs = str_replace('xmlns:', '', $name);
                        break;
                    }
                }
            }
        }
        return $this->_tasksNs;
    }

    /**
     * Determine whether a task name is a valid task.  Custom tasks may be defined
     * using subdirectories by putting a "-" in the name, as in <tasks:mycustom-task>
     *
     * Note that this method will auto-load the task class file and test for the existence
     * of the name with "-" replaced by "_" as in PEAR/Task/mycustom/task.php makes class
     * PEAR_Task_mycustom_task
     * @param string
     * @return boolean
     */
    function getTask($task)
    {
        $this->getTasksNs();
        // transform all '-' to '/' and 'tasks:' to '' so tasks:replace becomes replace
        $task = str_replace(array($this->_tasksNs . ':', '-'), array('', ' '), $task);
        $taskfile = str_replace(' ', '/', ucwords($task));
        $task = str_replace(array(' ', '/'), '_', ucwords($task));
        if (class_exists("PEAR_Task_$task")) {
            return "PEAR_Task_$task";
        }
        $fp = @fopen("PEAR/Task/$taskfile.php", 'r', true);
        if ($fp) {
            fclose($fp);
            require_once "PEAR/Task/$taskfile.php";
            return "PEAR_Task_$task";
        }
        return false;
    }

    /**
     * Key-friendly array_splice
     * @param tagname to splice a value in before
     * @param mixed the value to splice in
     * @param string the new tag name
     */
    function _ksplice($array, $key, $value, $newkey)
    {
        $offset = array_search($key, array_keys($array));
        $after = array_slice($array, $offset);
        $before = array_slice($array, 0, $offset);
        $before[$newkey] = $value;
        return array_merge($before, $after);
    }

    /**
     * @param array a list of possible keys, in the order they may occur
     * @param mixed contents of the new package.xml tag
     * @param string tag name
     * @access private
     */
    function _insertBefore($array, $keys, $contents, $newkey)
    {
        foreach ($keys as $key) {
            if (isset($array[$key])) {
                return $array = $this->_ksplice($array, $key, $contents, $newkey);
            }
        }
        $array[$newkey] = $contents;
        return $array;
    }

    /**
     * @param subsection of {@link $_packageInfo}
     * @param array|string tag contents
     * @param array format:
     * <pre>
     * array(
     *   tagname => array(list of tag names that follow this one),
     *   childtagname => array(list of child tag names that follow this one),
     * )
     * </pre>
     *
     * This allows construction of nested tags
     * @access private
     */
    function _mergeTag($manip, $contents, $order)
    {
        if (count($order)) {
            foreach ($order as $tag => $curorder) {
                if (!isset($manip[$tag])) {
                    // ensure that the tag is set up
                    $manip = $this->_insertBefore($manip, $curorder, array(), $tag);
                }
                if (count($order) > 1) {
                    $manip[$tag] = $this->_mergeTag($manip[$tag], $contents, array_slice($order, 1));
                    return $manip;
                }
            }
        } else {
            return $manip;
        }
        if (is_array($manip[$tag]) && !empty($manip[$tag]) && isset($manip[$tag][0])) {
            $manip[$tag][] = $contents;
        } else {
            if (!count($manip[$tag])) {
                $manip[$tag] = $contents;
            } else {
                $manip[$tag] = array($manip[$tag]);
                $manip[$tag][] = $contents;
            }
        }
        return $manip;
    }
}
?>
PEAR-1.7.2/PEAR/REST/10.php100644   1750   1750      101313 11014312742   7761 <?php
/**
 * PEAR_REST_10
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: 10.php,v 1.53 2008/04/11 01:16:40 dufuz Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a12
 */

/**
 * For downloading REST xml/txt files
 */
require_once 'PEAR/REST.php';

/**
 * Implement REST 1.0
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a12
 */
class PEAR_REST_10
{
    /**
     * @var PEAR_REST
     */
    var $_rest;
    function PEAR_REST_10($config, $options = array())
    {
        $this->_rest = &new PEAR_REST($config, $options);
    }

    /**
     * Retrieve information about a remote package to be downloaded from a REST server
     *
     * @param string $base The uri to prepend to all REST calls
     * @param array $packageinfo an array of format:
     * <pre>
     *  array(
     *   'package' => 'packagename',
     *   'channel' => 'channelname',
     *  ['state' => 'alpha' (or valid state),]
     *  -or-
     *  ['version' => '1.whatever']
     * </pre>
     * @param string $prefstate Current preferred_state config variable value
     * @param bool $installed the installed version of this package to compare against
     * @return array|false|PEAR_Error see {@link _returnDownloadURL()}
     */
    function getDownloadURL($base, $packageinfo, $prefstate, $installed, $channel = false)
    {
        $channel = $packageinfo['channel'];
        $package = $packageinfo['package'];
        $states = $this->betterStates($prefstate, true);
        if (!$states) {
            return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
        }
        $state   = isset($packageinfo['state'])   ? $packageinfo['state']   : null;
        $version = isset($packageinfo['version']) ? $packageinfo['version'] : null;
        $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . '/allreleases.xml', false, false, $channel);
        if (PEAR::isError($info)) {
            return PEAR::raiseError('No releases available for package "' .
                $channel . '/' . $package . '"');
        }
        if (!isset($info['r'])) {
            return false;
        }
        $release = $found = false;
        if (!is_array($info['r']) || !isset($info['r'][0])) {
            $info['r'] = array($info['r']);
        }
        foreach ($info['r'] as $release) {
            if (!isset($this->_rest->_options['force']) && ($installed &&
                  version_compare($release['v'], $installed, '<'))) {
                continue;
            }
            if (isset($state)) {
                // try our preferred state first
                if ($release['s'] == $state) {
                    $found = true;
                    break;
                }
                // see if there is something newer and more stable
                // bug #7221
                if (in_array($release['s'], $this->betterStates($state), true)) {
                    $found = true;
                    break;
                }
            } elseif (isset($version)) {
                if ($release['v'] == $version) {
                    $found = true;
                    break;
                }
            } else {
                if (in_array($release['s'], $states)) {
                    $found = true;
                    break;
                }
            }
        }
        return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel);
    }

    function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage,
                               $prefstate = 'stable', $installed = false, $channel = false)
    {
        $channel = $dependency['channel'];
        $package = $dependency['name'];
        $states = $this->betterStates($prefstate, true);
        if (!$states) {
            return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
        }
        $state   = isset($dependency['state'])   ? $dependency['state']   : null;
        $version = isset($dependency['version']) ? $dependency['version'] : null;
        $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . '/allreleases.xml', false, false, $channel);
        if (PEAR::isError($info)) {
            return PEAR::raiseError('Package "' . $deppackage['channel'] . '/' . $deppackage['package']
                . '" dependency "' . $channel . '/' . $package . '" has no releases');
        }
        if (!is_array($info) || !isset($info['r'])) {
            return false;
        }
        $exclude = array();
        $min = $max = $recommended = false;
        if ($xsdversion == '1.0') {
            switch ($dependency['rel']) {
                case 'ge' :
                    $min = $dependency['version'];
                break;
                case 'gt' :
                    $min = $dependency['version'];
                    $exclude = array($dependency['version']);
                break;
                case 'eq' :
                    $recommended = $dependency['version'];
                break;
                case 'lt' :
                    $max = $dependency['version'];
                    $exclude = array($dependency['version']);
                break;
                case 'le' :
                    $max = $dependency['version'];
                break;
                case 'ne' :
                    $exclude = array($dependency['version']);
                break;
            }
        } else {
            $min = isset($dependency['min']) ? $dependency['min'] : false;
            $max = isset($dependency['max']) ? $dependency['max'] : false;
            $recommended = isset($dependency['recommended']) ?
                $dependency['recommended'] : false;
            if (isset($dependency['exclude'])) {
                if (!isset($dependency['exclude'][0])) {
                    $exclude = array($dependency['exclude']);
                }
            }
        }
        $release = $found = false;
        if (!is_array($info['r']) || !isset($info['r'][0])) {
            $info['r'] = array($info['r']);
        }
        foreach ($info['r'] as $release) {
            if (!isset($this->_rest->_options['force']) && ($installed &&
                  version_compare($release['v'], $installed, '<'))) {
                continue;
            }
            if (in_array($release['v'], $exclude)) { // skip excluded versions
                continue;
            }
            // allow newer releases to say "I'm OK with the dependent package"
            if ($xsdversion == '2.0' && isset($release['co'])) {
                if (!is_array($release['co']) || !isset($release['co'][0])) {
                    $release['co'] = array($release['co']);
                }
                foreach ($release['co'] as $entry) {
                    if (isset($entry['x']) && !is_array($entry['x'])) {
                        $entry['x'] = array($entry['x']);
                    } elseif (!isset($entry['x'])) {
                        $entry['x'] = array();
                    }
                    if ($entry['c'] == $deppackage['channel'] &&
                          strtolower($entry['p']) == strtolower($deppackage['package']) &&
                          version_compare($deppackage['version'], $entry['min'], '>=') &&
                          version_compare($deppackage['version'], $entry['max'], '<=') &&
                          !in_array($release['v'], $entry['x'])) {
                        $recommended = $release['v'];
                        break;
                    }
                }
            }
            if ($recommended) {
                if ($release['v'] != $recommended) { // if we want a specific
                    // version, then skip all others
                    continue;
                } else {
                    if (!in_array($release['s'], $states)) {
                        // the stability is too low, but we must return the
                        // recommended version if possible
                        return $this->_returnDownloadURL($base, $package, $release, $info, true, false, $channel);
                    }
                }
            }
            if ($min && version_compare($release['v'], $min, 'lt')) { // skip too old versions
                continue;
            }
            if ($max && version_compare($release['v'], $max, 'gt')) { // skip too new versions
                continue;
            }
            if ($installed && version_compare($release['v'], $installed, '<')) {
                continue;
            }
            if (in_array($release['s'], $states)) { // if in the preferred state...
                $found = true; // ... then use it
                break;
            }
        }
        return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel);
    }

    /**
     * Take raw data and return the array needed for processing a download URL
     *
     * @param string $base REST base uri
     * @param string $package Package name
     * @param array $release an array of format array('v' => version, 's' => state)
     *                       describing the release to download
     * @param array $info list of all releases as defined by allreleases.xml
     * @param bool|null $found determines whether the release was found or this is the next
     *                    best alternative.  If null, then versions were skipped because
     *                    of PHP dependency
     * @return array|PEAR_Error
     * @access private
     */
    function _returnDownloadURL($base, $package, $release, $info, $found, $phpversion = false, $channel = false)
    {
        if (!$found) {
            $release = $info['r'][0];
        }
        $pinfo = $this->_rest->retrieveCacheFirst($base . 'p/' . strtolower($package) . '/' .
            'info.xml', false, false, $channel);
        if (PEAR::isError($pinfo)) {
            return PEAR::raiseError('Package "' . $package .
                '" does not have REST info xml available');
        }
        $releaseinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/' .
            $release['v'] . '.xml', false, false, $channel);
        if (PEAR::isError($releaseinfo)) {
            return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] .
                '" does not have REST xml available');
        }
        $packagexml = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/' .
            'deps.' . $release['v'] . '.txt', false, true, $channel);
        if (PEAR::isError($packagexml)) {
            return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] .
                '" does not have REST dependency information available');
        }
        $packagexml = unserialize($packagexml);
        if (!$packagexml) {
            $packagexml = array();
        }
        $allinfo = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
            '/allreleases.xml', false, false, $channel);
        if (!is_array($allinfo['r']) || !isset($allinfo['r'][0])) {
            $allinfo['r'] = array($allinfo['r']);
        }
        $compatible = false;
        foreach ($allinfo['r'] as $release) {
            if ($release['v'] != $releaseinfo['v']) {
                continue;
            }
            if (!isset($release['co'])) {
                break;
            }
            $compatible = array();
            if (!is_array($release['co']) || !isset($release['co'][0])) {
                $release['co'] = array($release['co']);
            }
            foreach ($release['co'] as $entry) {
                $comp = array();
                $comp['name'] = $entry['p'];
                $comp['channel'] = $entry['c'];
                $comp['min'] = $entry['min'];
                $comp['max'] = $entry['max'];
                if (isset($entry['x']) && !is_array($entry['x'])) {
                    $comp['exclude'] = $entry['x'];
                }
                $compatible[] = $comp;
            }
            if (count($compatible) == 1) {
                $compatible = $compatible[0];
            }
            break;
        }
        if (isset($pinfo['dc']) && isset($pinfo['dp'])) {
            if (is_array($pinfo['dp'])) {
                $deprecated = array('channel' => (string) $pinfo['dc'],
                                    'package' => trim($pinfo['dp']['_content']));
            } else {
                $deprecated = array('channel' => (string) $pinfo['dc'],
                                    'package' => trim($pinfo['dp']));
            }
        } else {
            $deprecated = false;
        }
        if ($found) {
            return
                array('version' => $releaseinfo['v'],
                      'info' => $packagexml,
                      'package' => $releaseinfo['p']['_content'],
                      'stability' => $releaseinfo['st'],
                      'url' => $releaseinfo['g'],
                      'compatible' => $compatible,
                      'deprecated' => $deprecated,
                );
        } else {
            return
                array('version' => $releaseinfo['v'],
                      'package' => $releaseinfo['p']['_content'],
                      'stability' => $releaseinfo['st'],
                      'info' => $packagexml,
                      'compatible' => $compatible,
                      'deprecated' => $deprecated,
                      'php' => $phpversion
                );
        }
    }

    function listPackages($base, $channel = false)
    {
        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
        if (PEAR::isError($packagelist)) {
            return $packagelist;
        }
        if (!is_array($packagelist) || !isset($packagelist['p'])) {
            return array();
        }
        if (!is_array($packagelist['p'])) {
            $packagelist['p'] = array($packagelist['p']);
        }
        return $packagelist['p'];
    }

    /**
     * List all categories of a REST server
     *
     * @param string $base base URL of the server
     * @return array of categorynames
     */
    function listCategories($base, $channel = false)
    {
        $categories = array();

        // c/categories.xml does not exist;
        // check for every package its category manually
        // This is SLOOOWWWW : ///
        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
        if (PEAR::isError($packagelist)) {
            return $packagelist;
        }
        if (!is_array($packagelist) || !isset($packagelist['p'])) {
            $ret = array();
            return $ret;
        }
        if (!is_array($packagelist['p'])) {
            $packagelist['p'] = array($packagelist['p']);
        }

        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        foreach ($packagelist['p'] as $package) {
                $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
                if (PEAR::isError($inf)) {
                    PEAR::popErrorHandling();
                    return $inf;
                }
                $cat = $inf['ca']['_content'];
                if (!isset($categories[$cat])) {
                    $categories[$cat] = $inf['ca'];
                }
        }
        return array_values($categories);
    }

    /**
     * List a category of a REST server
     *
     * @param string $base base URL of the server
     * @param string $category name of the category
     * @param boolean $info also download full package info
     * @return array of packagenames
     */
    function listCategory($base, $category, $info = false, $channel = false)
    {
        // gives '404 Not Found' error when category doesn't exist
        $packagelist = $this->_rest->retrieveData($base.'c/'.urlencode($category).'/packages.xml', false, false, $channel);
        if (PEAR::isError($packagelist)) {
            return $packagelist;
        }
        if (!is_array($packagelist) || !isset($packagelist['p'])) {
            return array();
        }
        if (!is_array($packagelist['p']) ||
            !isset($packagelist['p'][0])) { // only 1 pkg
            $packagelist = array($packagelist['p']);
        } else {
            $packagelist = $packagelist['p'];
        }

        if ($info == true) {
            // get individual package info
            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
            foreach ($packagelist as $i => $packageitem) {
                $url = sprintf('%s'.'r/%s/latest.txt',
                        $base,
                        strtolower($packageitem['_content']));
                $version = $this->_rest->retrieveData($url, false, false, $channel);
                if (PEAR::isError($version)) {
                    break; // skipit
                }
                $url = sprintf('%s'.'r/%s/%s.xml',
                        $base,
                        strtolower($packageitem['_content']),
                        $version);
                $info = $this->_rest->retrieveData($url, false, false, $channel);
                if (PEAR::isError($info)) {
                    break; // skipit
                }
                $packagelist[$i]['info'] = $info;
            }
            PEAR::popErrorHandling();
        }

        return $packagelist;
    }


    function listAll($base, $dostable, $basic = true, $searchpackage = false, $searchsummary = false, $channel = false)
    {
        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
        if (PEAR::isError($packagelist)) {
            return $packagelist;
        }
        if ($this->_rest->config->get('verbose') > 0) {
            $ui = &PEAR_Frontend::singleton();
            $ui->log('Retrieving data...0%', false);
        }
        $ret = array();
        if (!is_array($packagelist) || !isset($packagelist['p'])) {
            return $ret;
        }
        if (!is_array($packagelist['p'])) {
            $packagelist['p'] = array($packagelist['p']);
        }

        // only search-packagename = quicksearch !
        if ($searchpackage && (!$searchsummary || empty($searchpackage))) {
            $newpackagelist = array();
            foreach ($packagelist['p'] as $package) {
                if (!empty($searchpackage) && stristr($package, $searchpackage) !== false) {
                    $newpackagelist[] = $package;
                }
            }
            $packagelist['p'] = $newpackagelist;
        }
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        $next = .1;
        foreach ($packagelist['p'] as $progress => $package) {
            if ($this->_rest->config->get('verbose') > 0) {
                if ($progress / count($packagelist['p']) >= $next) {
                    if ($next == .5) {
                        $ui->log('50%', false);
                    } else {
                        $ui->log('.', false);
                    }
                    $next += .1;
                }
            }
            if ($basic) { // remote-list command
                if ($dostable) {
                    $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
                        '/stable.txt', false, false, $channel);
                } else {
                    $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
                        '/latest.txt', false, false, $channel);
                }
                if (PEAR::isError($latest)) {
                    $latest = false;
                }
                $info = array('stable' => $latest);
            } else { // list-all command
                $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
                if (PEAR::isError($inf)) {
                    PEAR::popErrorHandling();
                    return $inf;
                }
                if ($searchpackage) {
                    $found = (!empty($searchpackage) && stristr($package, $searchpackage) !== false);
                    if (!$found && !(isset($searchsummary) && !empty($searchsummary)
                        && (stristr($inf['s'], $searchsummary) !== false
                            || stristr($inf['d'], $searchsummary) !== false)))
                    {
                        continue;
                    };
                }
                $releases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
                    '/allreleases.xml', false, false, $channel);
                if (PEAR::isError($releases)) {
                    continue;
                }
                if (!isset($releases['r'][0])) {
                    $releases['r'] = array($releases['r']);
                }
                unset($latest);
                unset($unstable);
                unset($stable);
                unset($state);
                foreach ($releases['r'] as $release) {
                    if (!isset($latest)) {
                        if ($dostable && $release['s'] == 'stable') {
                            $latest = $release['v'];
                            $state = 'stable';
                        }
                        if (!$dostable) {
                            $latest = $release['v'];
                            $state = $release['s'];
                        }
                    }
                    if (!isset($stable) && $release['s'] == 'stable') {
                        $stable = $release['v'];
                        if (!isset($unstable)) {
                            $unstable = $stable;
                        }
                    }
                    if (!isset($unstable) && $release['s'] != 'stable') {
                        $latest = $unstable = $release['v'];
                        $state = $release['s'];
                    }
                    if (isset($latest) && !isset($state)) {
                        $state = $release['s'];
                    }
                    if (isset($latest) && isset($stable) && isset($unstable)) {
                        break;
                    }
                }
                $deps = array();
                if (!isset($unstable)) {
                    $unstable = false;
                    $state = 'stable';
                    if (isset($stable)) {
                        $latest = $unstable = $stable;
                    }
                } else {
                    $latest = $unstable;
                }
                if (!isset($latest)) {
                    $latest = false;
                }
                if ($latest) {
                    $d = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' .
                        $latest . '.txt', false, false, $channel);
                    if (!PEAR::isError($d)) {
                        $d = unserialize($d);
                        if ($d) {
                            if (isset($d['required'])) {
                                if (!class_exists('PEAR_PackageFile_v2')) {
                                    require_once 'PEAR/PackageFile/v2.php';
                                }
                                if (!isset($pf)) {
                                    $pf = new PEAR_PackageFile_v2;
                                }
                                $pf->setDeps($d);
                                $tdeps = $pf->getDeps();
                            } else {
                                $tdeps = $d;
                            }
                            foreach ($tdeps as $dep) {
                                if ($dep['type'] !== 'pkg') {
                                    continue;
                                }
                                $deps[] = $dep;
                            }
                        }
                    }
                }
                if (!isset($stable)) {
                    $stable = '-n/a-';
                }
                if (!$searchpackage) {
                    $info = array('stable' => $latest, 'summary' => $inf['s'], 'description' =>
                        $inf['d'], 'deps' => $deps, 'category' => $inf['ca']['_content'],
                        'unstable' => $unstable, 'state' => $state);
                } else {
                    $info = array('stable' => $stable, 'summary' => $inf['s'], 'description' =>
                        $inf['d'], 'deps' => $deps, 'category' => $inf['ca']['_content'],
                        'unstable' => $unstable, 'state' => $state);
                }
            }
            $ret[$package] = $info;
        }
        PEAR::popErrorHandling();
        return $ret;
    }

    function listLatestUpgrades($base, $pref_state, $installed, $channel, &$reg)
    {
        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
        if (PEAR::isError($packagelist)) {
            return $packagelist;
        }
        $ret = array();
        if (!is_array($packagelist) || !isset($packagelist['p'])) {
            return $ret;
        }
        if (!is_array($packagelist['p'])) {
            $packagelist['p'] = array($packagelist['p']);
        }
        foreach ($packagelist['p'] as $package) {
            if (!isset($installed[strtolower($package)])) {
                continue;
            }
            $inst_version = $reg->packageInfo($package, 'version', $channel);
            $inst_state = $reg->packageInfo($package, 'release_state', $channel);
            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
            $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
                '/allreleases.xml', false, false, $channel);
            PEAR::popErrorHandling();
            if (PEAR::isError($info)) {
                continue; // no remote releases
            }
            if (!isset($info['r'])) {
                continue;
            }
            $found = false;
            $release = false;
            if (!is_array($info['r']) || !isset($info['r'][0])) {
                $info['r'] = array($info['r']);
            }
            // $info['r'] is sorted by version number
            foreach ($info['r'] as $release) {
                if ($inst_version && version_compare($release['v'], $inst_version, '<=')) {
                    // not newer than the one installed
                    break;
                }

                // new version > installed version
                if (!$pref_state) {
                    // every state is a good state
                    $found = true;
                    break;
                } else {
                    $new_state = $release['s'];
                    // if new state >= installed state: go
                    if (in_array($new_state, $this->betterStates($inst_state, true))) {
                        $found = true;
                        break;
                    } else {
                        // only allow to lower the state of package,
                        // if new state >= preferred state: go
                        if (in_array($new_state, $this->betterStates($pref_state, true))) {
                            $found = true;
                            break;
                        }
                    }
                }
            }
            if (!$found) {
                continue;
            }
            $relinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/' .
                $release['v'] . '.xml', false, false, $channel);
            if (PEAR::isError($relinfo)) {
                return $relinfo;
            }
            $ret[$package] = array(
                    'version' => $release['v'],
                    'state' => $release['s'],
                    'filesize' => $relinfo['f'],
                );
        }
        return $ret;
    }

    function packageInfo($base, $package, $channel = false)
    {
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        $pinfo = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
        if (PEAR::isError($pinfo)) {
            PEAR::popErrorHandling();
            return PEAR::raiseError('Unknown package: "' . $package . '" (Debug: ' .
                $pinfo->getMessage() . ')');
        }
        $releases = array();
        $allreleases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
            '/allreleases.xml', false, false, $channel);
        if (!PEAR::isError($allreleases)) {
            if (!class_exists('PEAR_PackageFile_v2')) {
                require_once 'PEAR/PackageFile/v2.php';
            }
            if (!is_array($allreleases['r']) || !isset($allreleases['r'][0])) {
                $allreleases['r'] = array($allreleases['r']);
            }
            $pf = new PEAR_PackageFile_v2;
            foreach ($allreleases['r'] as $release) {
                $ds = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' .
                    $release['v'] . '.txt', false, false, $channel);
                if (PEAR::isError($ds)) {
                    continue;
                }
                if (!isset($latest)) {
                    $latest = $release['v'];
                }
                $pf->setDeps(unserialize($ds));
                $ds = $pf->getDeps();
                $info = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package)
                    . '/' . $release['v'] . '.xml', false, false, $channel);
                if (PEAR::isError($info)) {
                    continue;
                }
                $releases[$release['v']] = array(
                    'doneby' => $info['m'],
                    'license' => $info['l'],
                    'summary' => $info['s'],
                    'description' => $info['d'],
                    'releasedate' => $info['da'],
                    'releasenotes' => $info['n'],
                    'state' => $release['s'],
                    'deps' => $ds ? $ds : array(),
                );
            }
        } else {
            $latest = '';
        }
        PEAR::popErrorHandling();
        if (isset($pinfo['dc']) && isset($pinfo['dp'])) {
            if (is_array($pinfo['dp'])) {
                $deprecated = array('channel' => (string) $pinfo['dc'],
                                    'package' => trim($pinfo['dp']['_content']));
            } else {
                $deprecated = array('channel' => (string) $pinfo['dc'],
                                    'package' => trim($pinfo['dp']));
            }
        } else {
            $deprecated = false;
        }
        return array(
            'name' => $pinfo['n'],
            'channel' => $pinfo['c'],
            'category' => $pinfo['ca']['_content'],
            'stable' => $latest,
            'license' => $pinfo['l'],
            'summary' => $pinfo['s'],
            'description' => $pinfo['d'],
            'releases' => $releases,
            'deprecated' => $deprecated,
            );
    }

    /**
     * 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);
    }
}
?>PEAR-1.7.2/PEAR/REST/11.php100644   1750   1750       27065 11014312742   7755 <?php
/**
 * PEAR_REST_11 - implement faster list-all/remote-list command
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: 11.php,v 1.14 2008/04/11 01:16:40 dufuz Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.3
 */

/**
 * For downloading REST xml/txt files
 */
require_once 'PEAR/REST.php';

/**
 * Implement REST 1.1
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.3
 */
class PEAR_REST_11
{
    /**
     * @var PEAR_REST
     */
    var $_rest;

    function PEAR_REST_11($config, $options = array())
    {
        $this->_rest = &new PEAR_REST($config, $options);
    }

    function listAll($base, $dostable, $basic = true, $searchpackage = false, $searchsummary = false, $channel = false)
    {
        $categorylist = $this->_rest->retrieveData($base . 'c/categories.xml', false, false, $channel);
        if (PEAR::isError($categorylist)) {
            return $categorylist;
        }
        $ret = array();
        if (!is_array($categorylist['c']) || !isset($categorylist['c'][0])) {
            $categorylist['c'] = array($categorylist['c']);
        }
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);

        foreach ($categorylist['c'] as $progress => $category) {
            $category = $category['_content'];
            $packagesinfo = $this->_rest->retrieveData($base .
                'c/' . urlencode($category) . '/packagesinfo.xml', false, false, $channel);

            if (PEAR::isError($packagesinfo)) {
                continue;
            }

            if (!is_array($packagesinfo) || !isset($packagesinfo['pi'])) {
                continue;
            }

            if (!is_array($packagesinfo['pi']) || !isset($packagesinfo['pi'][0])) {
                $packagesinfo['pi'] = array($packagesinfo['pi']);
            }

            foreach ($packagesinfo['pi'] as $packageinfo) {
                $info = $packageinfo['p'];
                $package = $info['n'];
                $releases = isset($packageinfo['a']) ? $packageinfo['a'] : false;
                unset($latest);
                unset($unstable);
                unset($stable);
                unset($state);

                if ($releases) {
                    if (!isset($releases['r'][0])) {
                        $releases['r'] = array($releases['r']);
                    }
                    foreach ($releases['r'] as $release) {
                        if (!isset($latest)) {
                            if ($dostable && $release['s'] == 'stable') {
                                $latest = $release['v'];
                                $state = 'stable';
                            }
                            if (!$dostable) {
                                $latest = $release['v'];
                                $state = $release['s'];
                            }
                        }
                        if (!isset($stable) && $release['s'] == 'stable') {
                            $stable = $release['v'];
                            if (!isset($unstable)) {
                                $unstable = $stable;
                            }
                        }
                        if (!isset($unstable) && $release['s'] != 'stable') {
                            $unstable = $release['v'];
                            $state = $release['s'];
                        }
                        if (isset($latest) && !isset($state)) {
                            $state = $release['s'];
                        }
                        if (isset($latest) && isset($stable) && isset($unstable)) {
                            break;
                        }
                    }
                }

                if ($basic) { // remote-list command
                    if (!isset($latest)) {
                        $latest = false;
                    }
                    if ($dostable) {
                        // $state is not set if there are no releases
                        if (isset($state) && $state == 'stable') {
                            $ret[$package] = array('stable' => $latest);
                        } else {
                            $ret[$package] = array('stable' => '-n/a-');
                        }
                    } else {
                        $ret[$package] = array('stable' => $latest);
                    }
                    continue;
                }

                // list-all command
                $deps = array();
                if (!isset($unstable)) {
                    $unstable = false;
                    $state = 'stable';
                    if (isset($stable)) {
                        $latest = $unstable = $stable;
                    }
                } else {
                    $latest = $unstable;
                }

                if (!isset($latest)) {
                    $latest = false;
                }

                if ($latest && isset($packageinfo['deps'])) {
                    if (!is_array($packageinfo['deps']) ||
                          !isset($packageinfo['deps'][0])) {
                        $packageinfo['deps'] = array($packageinfo['deps']);
                    }
                    $d = false;
                    foreach ($packageinfo['deps'] as $dep) {
                        if ($dep['v'] == $latest) {
                            $d = unserialize($dep['d']);
                        }
                    }
                    if ($d) {
                        if (isset($d['required'])) {
                            if (!class_exists('PEAR_PackageFile_v2')) {
                                require_once 'PEAR/PackageFile/v2.php';
                            }
                            if (!isset($pf)) {
                                $pf = new PEAR_PackageFile_v2;
                            }
                            $pf->setDeps($d);
                            $tdeps = $pf->getDeps();
                        } else {
                            $tdeps = $d;
                        }
                        foreach ($tdeps as $dep) {
                            if ($dep['type'] !== 'pkg') {
                                continue;
                            }
                            $deps[] = $dep;
                        }
                    }
                }

                $info = array('stable' => $latest, 'summary' => $info['s'],
                    'description' =>
                    $info['d'], 'deps' => $deps, 'category' => $info['ca']['_content'],
                    'unstable' => $unstable, 'state' => $state);
                $ret[$package] = $info;
            }
        }
        PEAR::popErrorHandling();
        return $ret;
    }

    /**
     * List all categories of a REST server
     *
     * @param string $base base URL of the server
     * @return array of categorynames
     */
    function listCategories($base, $channel = false)
    {
        $categorylist = $this->_rest->retrieveData($base . 'c/categories.xml', false, false, $channel);
        if (PEAR::isError($categorylist)) {
            return $categorylist;
        }
        if (!is_array($categorylist) || !isset($categorylist['c'])) {
            return array();
        }
        if (isset($categorylist['c']['_content'])) {
            // only 1 category
            $categorylist['c'] = array($categorylist['c']);
        }
        return $categorylist['c'];
    }

    /**
     * List packages in a category of a REST server
     *
     * @param string $base base URL of the server
     * @param string $category name of the category
     * @param boolean $info also download full package info
     * @return array of packagenames
     */
    function listCategory($base, $category, $info = false, $channel = false)
    {
        if ($info == false) {
            $url = '%s'.'c/%s/packages.xml';
        } else {
            $url = '%s'.'c/%s/packagesinfo.xml';
        }
        $url = sprintf($url,
                    $base,
                    urlencode($category));

        // gives '404 Not Found' error when category doesn't exist
        $packagelist = $this->_rest->retrieveData($url, false, false, $channel);
        if (PEAR::isError($packagelist)) {
            return $packagelist;
        }
        if (!is_array($packagelist)) {
            return array();
        }

        if ($info == false) {
            if (!isset($packagelist['p'])) {
                return array();
            }
            if (!is_array($packagelist['p']) ||
                !isset($packagelist['p'][0])) { // only 1 pkg
                $packagelist = array($packagelist['p']);
            } else {
                $packagelist = $packagelist['p'];
            }
            return $packagelist;
        } else {
            // info == true
            if (!isset($packagelist['pi'])) {
                return array();
            }
            if (!is_array($packagelist['pi']) ||
                !isset($packagelist['pi'][0])) { // only 1 pkg
                $packagelist_pre = array($packagelist['pi']);
            } else {
                $packagelist_pre = $packagelist['pi'];
            }

            $packagelist = array();
            foreach ($packagelist_pre as $i => $item) {
                // compatibility with r/<latest.txt>.xml
                if (isset($item['a']['r'][0])) {
                    // multiple releases
                    $item['p']['v'] = $item['a']['r'][0]['v'];
                    $item['p']['st'] = $item['a']['r'][0]['s'];
                } elseif (isset($item['a'])) {
                    // first and only release
                    $item['p']['v'] = $item['a']['r']['v'];
                    $item['p']['st'] = $item['a']['r']['s'];
                }

                $packagelist[$i] = array('attribs' => $item['p']['r'],
                                         '_content' => $item['p']['n'],
                                         'info' => $item['p']);
            }
        }

        return $packagelist;
    }

    /**
     * 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);
    }
}
?>PEAR-1.7.2/PEAR/REST/13.php100644   1750   1750       27653 11014312742   7762 <?php
/**
 * PEAR_REST_13
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: 13.php,v 1.6 2008/04/11 01:16:40 dufuz Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a12
 */

/**
 * For downloading REST xml/txt files
 */
require_once 'PEAR/REST.php';
require_once 'PEAR/REST/10.php';

/**
 * Implement REST 1.3
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a12
 */
class PEAR_REST_13 extends PEAR_REST_10
{
    /**
     * Retrieve information about a remote package to be downloaded from a REST server
     *
     * This is smart enough to resolve the minimum PHP version dependency prior to download
     * @param string $base The uri to prepend to all REST calls
     * @param array $packageinfo an array of format:
     * <pre>
     *  array(
     *   'package' => 'packagename',
     *   'channel' => 'channelname',
     *  ['state' => 'alpha' (or valid state),]
     *  -or-
     *  ['version' => '1.whatever']
     * </pre>
     * @param string $prefstate Current preferred_state config variable value
     * @param bool $installed the installed version of this package to compare against
     * @return array|false|PEAR_Error see {@link _returnDownloadURL()}
     */
    function getDownloadURL($base, $packageinfo, $prefstate, $installed, $channel = false)
    {
        $channel = $packageinfo['channel'];
        $package = $packageinfo['package'];
        $states = $this->betterStates($prefstate, true);
        if (!$states) {
            return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
        }
        $state   = isset($packageinfo['state'])   ? $packageinfo['state']   : null;
        $version = isset($packageinfo['version']) ? $packageinfo['version'] : null;
        $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
            '/allreleases2.xml');
        if (PEAR::isError($info)) {
            return PEAR::raiseError('No releases available for package "' .
                $channel . '/' . $package . '"');
        }
        if (!isset($info['r'])) {
            return false;
        }
        $release = $found = false;
        if (!is_array($info['r']) || !isset($info['r'][0])) {
            $info['r'] = array($info['r']);
        }
        $skippedphp = false;
        foreach ($info['r'] as $release) {
            if (!isset($this->_rest->_options['force']) && ($installed &&
                  version_compare($release['v'], $installed, '<'))) {
                continue;
            }
            if (isset($state)) {
                // try our preferred state first
                if ($release['s'] == $state) {
                    if (!isset($version) && version_compare($release['m'], phpversion(), '>')) {
                        // skip releases that require a PHP version newer than our PHP version
                        $skippedphp = $release;
                        continue;
                    }
                    $found = true;
                    break;
                }
                // see if there is something newer and more stable
                // bug #7221
                if (in_array($release['s'], $this->betterStates($state), true)) {
                    if (!isset($version) && version_compare($release['m'], phpversion(), '>')) {
                        // skip releases that require a PHP version newer than our PHP version
                        $skippedphp = $release;
                        continue;
                    }
                    $found = true;
                    break;
                }
            } elseif (isset($version)) {
                if ($release['v'] == $version) {
                    if (!isset($this->_rest->_options['force']) &&
                          !isset($version) &&
                          version_compare($release['m'], phpversion(), '>')) {
                        // skip releases that require a PHP version newer than our PHP version
                        $skippedphp = $release;
                        continue;
                    }
                    $found = true;
                    break;
                }
            } else {
                if (in_array($release['s'], $states)) {
                    if (version_compare($release['m'], phpversion(), '>')) {
                        // skip releases that require a PHP version newer than our PHP version
                        $skippedphp = $release;
                        continue;
                    }
                    $found = true;
                    break;
                }
            }
        }
        if (!$found && $skippedphp) {
            $found = null;
        }
        return $this->_returnDownloadURL($base, $package, $release, $info, $found, $skippedphp, $channel);
    }

    function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage,
                               $prefstate = 'stable', $installed = false, $channel = false)
    {
        $channel = $dependency['channel'];
        $package = $dependency['name'];
        $states = $this->betterStates($prefstate, true);
        if (!$states) {
            return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
        }
        $state   = isset($dependency['state'])   ? $dependency['state']   : null;
        $version = isset($dependency['version']) ? $dependency['version'] : null;
        $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
            '/allreleases2.xml');
        if (PEAR::isError($info)) {
            return PEAR::raiseError('Package "' . $deppackage['channel'] . '/' . $deppackage['package']
                . '" dependency "' . $channel . '/' . $package . '" has no releases');
        }
        if (!is_array($info) || !isset($info['r'])) {
            return false;
        }
        $exclude = array();
        $min = $max = $recommended = false;
        if ($xsdversion == '1.0') {
            $pinfo['package'] = $dependency['name'];
            $pinfo['channel'] = 'pear.php.net'; // this is always true - don't change this
            switch ($dependency['rel']) {
                case 'ge' :
                    $min = $dependency['version'];
                break;
                case 'gt' :
                    $min = $dependency['version'];
                    $exclude = array($dependency['version']);
                break;
                case 'eq' :
                    $recommended = $dependency['version'];
                break;
                case 'lt' :
                    $max = $dependency['version'];
                    $exclude = array($dependency['version']);
                break;
                case 'le' :
                    $max = $dependency['version'];
                break;
                case 'ne' :
                    $exclude = array($dependency['version']);
                break;
            }
        } else {
            $pinfo['package'] = $dependency['name'];
            $min = isset($dependency['min']) ? $dependency['min'] : false;
            $max = isset($dependency['max']) ? $dependency['max'] : false;
            $recommended = isset($dependency['recommended']) ?
                $dependency['recommended'] : false;
            if (isset($dependency['exclude'])) {
                if (!isset($dependency['exclude'][0])) {
                    $exclude = array($dependency['exclude']);
                }
            }
        }
        $found = false;
        $release = false;
        $skippedphp = false;
        if (!is_array($info['r']) || !isset($info['r'][0])) {
            $info['r'] = array($info['r']);
        }
        foreach ($info['r'] as $release) {
            if (!isset($this->_rest->_options['force']) && ($installed &&
                  version_compare($release['v'], $installed, '<'))) {
                continue;
            }
            if (in_array($release['v'], $exclude)) { // skip excluded versions
                continue;
            }
            // allow newer releases to say "I'm OK with the dependent package"
            if ($xsdversion == '2.0' && isset($release['co'])) {
                if (!is_array($release['co']) || !isset($release['co'][0])) {
                    $release['co'] = array($release['co']);
                }
                foreach ($release['co'] as $entry) {
                    if (isset($entry['x']) && !is_array($entry['x'])) {
                        $entry['x'] = array($entry['x']);
                    } elseif (!isset($entry['x'])) {
                        $entry['x'] = array();
                    }
                    if ($entry['c'] == $deppackage['channel'] &&
                          strtolower($entry['p']) == strtolower($deppackage['package']) &&
                          version_compare($deppackage['version'], $entry['min'], '>=') &&
                          version_compare($deppackage['version'], $entry['max'], '<=') &&
                          !in_array($release['v'], $entry['x'])) {
                        if (version_compare($release['m'], phpversion(), '>')) {
                            // skip dependency releases that require a PHP version
                            // newer than our PHP version
                            $skippedphp = $release;
                            continue;
                        }
                        $recommended = $release['v'];
                        break;
                    }
                }
            }
            if ($recommended) {
                if ($release['v'] != $recommended) { // if we want a specific
                    // version, then skip all others
                    continue;
                } else {
                    if (!in_array($release['s'], $states)) {
                        // the stability is too low, but we must return the
                        // recommended version if possible
                        return $this->_returnDownloadURL($base, $package, $release, $info, true, false, $channel);
                    }
                }
            }
            if ($min && version_compare($release['v'], $min, 'lt')) { // skip too old versions
                continue;
            }
            if ($max && version_compare($release['v'], $max, 'gt')) { // skip too new versions
                continue;
            }
            if ($installed && version_compare($release['v'], $installed, '<')) {
                continue;
            }
            if (in_array($release['s'], $states)) { // if in the preferred state...
                if (version_compare($release['m'], phpversion(), '>')) {
                    // skip dependency releases that require a PHP version
                    // newer than our PHP version
                    $skippedphp = $release;
                    continue;
                }
                $found = true; // ... then use it
                break;
            }
        }
        if (!$found && $skippedphp) {
            $found = null;
        }
        return $this->_returnDownloadURL($base, $package, $release, $info, $found, $skippedphp, $channel);
    }
}
?>PEAR-1.7.2/PEAR/Task/Postinstallscript/rw.php100644   1750   1750       14076 11014312742  14050 <?php
/**
 * <tasks:postinstallscript> - read/write version
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: rw.php,v 1.12 2008/01/03 20:26:37 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a10
 */
/**
 * Base class
 */
require_once 'PEAR/Task/Postinstallscript.php';
/**
 * Abstracts the postinstallscript file task xml.
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a10
 */
class PEAR_Task_Postinstallscript_rw extends PEAR_Task_Postinstallscript
{
    /**
     * parent package file object
     *
     * @var PEAR_PackageFile_v2_rw
     */
    var $_pkg;
    /**
     * Enter description here...
     *
     * @param PEAR_PackageFile_v2_rw $pkg
     * @param PEAR_Config $config
     * @param PEAR_Frontend $logger
     * @param array $fileXml
     * @return PEAR_Task_Postinstallscript_rw
     */
    function PEAR_Task_Postinstallscript_rw(&$pkg, &$config, &$logger, $fileXml)
    {
        parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE);
        $this->_contents = $fileXml;
        $this->_pkg = &$pkg;
        $this->_params = array();
    }

    function validate()
    {
        return $this->validateXml($this->_pkg, $this->_params, $this->config, $this->_contents);
    }

    function getName()
    {
        return 'postinstallscript';
    }

    /**
     * add a simple <paramgroup> to the post-install script
     *
     * Order is significant, so call this method in the same
     * sequence the users should see the paramgroups.  The $params
     * parameter should either be the result of a call to {@link getParam()}
     * or an array of calls to getParam().
     * 
     * Use {@link addConditionTypeGroup()} to add a <paramgroup> containing
     * a <conditiontype> tag
     * @param string $id <paramgroup> id as seen by the script
     * @param array|false $params array of getParam() calls, or false for no params
     * @param string|false $instructions
     */
    function addParamGroup($id, $params = false, $instructions = false)
    {
        if ($params && isset($params[0]) && !isset($params[1])) {
            $params = $params[0];
        }
        $stuff =
            array(
                $this->_pkg->getTasksNs() . ':id' => $id,
            );
        if ($instructions) {
            $stuff[$this->_pkg->getTasksNs() . ':instructions'] = $instructions;
        }
        if ($params) {
            $stuff[$this->_pkg->getTasksNs() . ':param'] = $params;
        }
        $this->_params[$this->_pkg->getTasksNs() . ':paramgroup'][] = $stuff;
    }

    /**
     * add a complex <paramgroup> to the post-install script with conditions
     *
     * This inserts a <paramgroup> with
     *
     * Order is significant, so call this method in the same
     * sequence the users should see the paramgroups.  The $params
     * parameter should either be the result of a call to {@link getParam()}
     * or an array of calls to getParam().
     * 
     * Use {@link addParamGroup()} to add a simple <paramgroup>
     *
     * @param string $id <paramgroup> id as seen by the script
     * @param string $oldgroup <paramgroup> id of the section referenced by
     *                         <conditiontype>
     * @param string $param name of the <param> from the older section referenced
     *                      by <contitiontype>
     * @param string $value value to match of the parameter
     * @param string $conditiontype one of '=', '!=', 'preg_match'
     * @param array|false $params array of getParam() calls, or false for no params
     * @param string|false $instructions
     */
    function addConditionTypeGroup($id, $oldgroup, $param, $value, $conditiontype = '=',
                                   $params = false, $instructions = false)
    {
        if ($params && isset($params[0]) && !isset($params[1])) {
            $params = $params[0];
        }
        $stuff =
            array(
                $this->_pkg->getTasksNs() . ':id' => $id,
            );
        if ($instructions) {
            $stuff[$this->_pkg->getTasksNs() . ':instructions'] = $instructions;
        }
        $stuff[$this->_pkg->getTasksNs() . ':name'] = $oldgroup . '::' . $param;
        $stuff[$this->_pkg->getTasksNs() . ':conditiontype'] = $conditiontype;
        $stuff[$this->_pkg->getTasksNs() . ':value'] = $value;
        if ($params) {
            $stuff[$this->_pkg->getTasksNs() . ':param'] = $params;
        }
        $this->_params[$this->_pkg->getTasksNs() . ':paramgroup'][] = $stuff;
    }

    function getXml()
    {
        return $this->_params;
    }

    /**
     * Use to set up a param tag for use in creating a paramgroup
     * @static
     */
    function getParam($name, $prompt, $type = 'string', $default = null)
    {
        if ($default !== null) {
            return 
            array(
                $this->_pkg->getTasksNs() . ':name' => $name,
                $this->_pkg->getTasksNs() . ':prompt' => $prompt,
                $this->_pkg->getTasksNs() . ':type' => $type,
                $this->_pkg->getTasksNs() . ':default' => $default
            );
        }
        return
            array(
                $this->_pkg->getTasksNs() . ':name' => $name,
                $this->_pkg->getTasksNs() . ':prompt' => $prompt,
                $this->_pkg->getTasksNs() . ':type' => $type,
            );
    }
}
?>PEAR-1.7.2/PEAR/Task/Replace/rw.php100644   1750   1750        3652 11014312742  11640 <?php
/**
 * <tasks:replace> - read/write version
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: rw.php,v 1.4 2008/01/03 20:26:37 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a10
 */
/**
 * Base class
 */
require_once 'PEAR/Task/Replace.php';
/**
 * Abstracts the replace task xml.
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a10
 */
class PEAR_Task_Replace_rw extends PEAR_Task_Replace
{
    function PEAR_Task_Replace_rw(&$pkg, &$config, &$logger, $fileXml)
    {
        parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE);
        $this->_contents = $fileXml;
        $this->_pkg = &$pkg;
        $this->_params = array();
    }

    function validate()
    {
        return $this->validateXml($this->_pkg, $this->_params, $this->config, $this->_contents);
    }

    function setInfo($from, $to, $type)
    {
        $this->_params = array('attribs' => array('from' => $from, 'to' => $to, 'type' => $type));
    }

    function getName()
    {
        return 'replace';
    }

    function getXml()
    {
        return $this->_params;
    }
}
?>PEAR-1.7.2/PEAR/Task/Unixeol/rw.php100644   1750   1750        3272 11014312742  11706 <?php
/**
 * <tasks:unixeol> - read/write version
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: rw.php,v 1.5 2008/01/03 20:26:37 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a10
 */
/**
 * Base class
 */
require_once 'PEAR/Task/Unixeol.php';
/**
 * Abstracts the unixeol task xml.
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a10
 */
class PEAR_Task_Unixeol_rw extends PEAR_Task_Unixeol
{
    function PEAR_Task_Unixeol_rw(&$pkg, &$config, &$logger, $fileXml)
    {
        parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE);
        $this->_contents = $fileXml;
        $this->_pkg = &$pkg;
        $this->_params = array();
    }

    function validate()
    {
        return true;
    }

    function getName()
    {
        return 'unixeol';
    }

    function getXml()
    {
        return '';
    }
}
?>PEAR-1.7.2/PEAR/Task/Windowseol/rw.php100644   1750   1750        3317 11014312742  12415 <?php
/**
 * <tasks:windowseol> - read/write version
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: rw.php,v 1.5 2008/01/03 20:26:37 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a10
 */
/**
 * Base class
 */
require_once 'PEAR/Task/Windowseol.php';
/**
 * Abstracts the windowseol task xml.
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a10
 */
class PEAR_Task_Windowseol_rw extends PEAR_Task_Windowseol
{
    function PEAR_Task_Windowseol_rw(&$pkg, &$config, &$logger, $fileXml)
    {
        parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE);
        $this->_contents = $fileXml;
        $this->_pkg = &$pkg;
        $this->_params = array();
    }

    function validate()
    {
        return true;
    }

    function getName()
    {
        return 'windowseol';
    }

    function getXml()
    {
        return '';
    }
}
?>PEAR-1.7.2/PEAR/Task/Common.php100644   1750   1750       14474 11014312742  11111 <?php
/**
 * PEAR_Task_Common, base class for installer tasks
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Common.php,v 1.18 2008/05/13 21:28:20 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**#@+
 * Error codes for task validation routines
 */
define('PEAR_TASK_ERROR_NOATTRIBS', 1);
define('PEAR_TASK_ERROR_MISSING_ATTRIB', 2);
define('PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE', 3);
define('PEAR_TASK_ERROR_INVALID', 4);
/**#@-*/
define('PEAR_TASK_PACKAGE', 1);
define('PEAR_TASK_INSTALL', 2);
define('PEAR_TASK_PACKAGEANDINSTALL', 3);
/**
 * A task is an operation that manipulates the contents of a file.
 *
 * Simple tasks operate on 1 file.  Multiple tasks are executed after all files have been
 * processed and installed, and are designed to operate on all files containing the task.
 * The Post-install script task simply takes advantage of the fact that it will be run
 * after installation, replace is a simple task.
 *
 * Combining tasks is possible, but ordering is significant.
 *
 * <file name="test.php" role="php">
 *  <tasks:replace from="@data-dir@" to="data_dir" type="pear-config"/>
 *  <tasks:postinstallscript/>
 * </file>
 *
 * This will first replace any instance of @data-dir@ in the test.php file
 * with the path to the current data directory.  Then, it will include the
 * test.php file and run the script it contains to configure the package post-installation.
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 * @abstract
 */
class PEAR_Task_Common
{
    /**
     * Valid types for this version are 'simple' and 'multiple'
     *
     * - simple tasks operate on the contents of a file and write out changes to disk
     * - multiple tasks operate on the contents of many files and write out the
     *   changes directly to disk
     *
     * Child task classes must override this property.
     * @access protected
     */
    var $type = 'simple';
    /**
     * Determines which install phase this task is executed under
     */
    var $phase = PEAR_TASK_INSTALL;
    /**
     * @access protected
     */
    var $config;
    /**
     * @access protected
     */
    var $registry;
    /**
     * @access protected
     */
    var $logger;
    /**
     * @access protected
     */
    var $installphase;
    /**
     * @param PEAR_Config
     * @param PEAR_Common
     */
    function PEAR_Task_Common(&$config, &$logger, $phase)
    {
        $this->config = &$config;
        $this->registry = &$config->getRegistry();
        $this->logger = &$logger;
        $this->installphase = $phase;
        if ($this->type == 'multiple') {
            $GLOBALS['_PEAR_TASK_POSTINSTANCES'][get_class($this)][] = &$this;
        }
    }

    /**
     * Validate the basic contents of a task tag.
     * @param PEAR_PackageFile_v2
     * @param array
     * @param PEAR_Config
     * @param array the entire parsed <file> tag
     * @return true|array On error, return an array in format:
     *    array(PEAR_TASK_ERROR_???[, param1][, param2][, ...])
     *
     *    For PEAR_TASK_ERROR_MISSING_ATTRIB, pass the attribute name in
     *    For PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, pass the attribute name and an array
     *    of legal values in
     * @static
     * @abstract
     */
    function validateXml($pkg, $xml, $config, $fileXml)
    {
    }

    /**
     * Initialize a task instance with the parameters
     * @param array raw, parsed xml
     * @param array attributes from the <file> tag containing this task
     * @param string|null last installed version of this package
     * @abstract
     */
    function init($xml, $fileAttributes, $lastVersion)
    {
    }

    /**
     * Begin a task processing session.  All multiple tasks will be processed after each file
     * has been successfully installed, all simple tasks should perform their task here and
     * return any errors using the custom throwError() method to allow forward compatibility
     *
     * This method MUST NOT write out any changes to disk
     * @param PEAR_PackageFile_v2
     * @param string file contents
     * @param string the eventual final file location (informational only)
     * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
     *         (use $this->throwError), otherwise return the new contents
     * @abstract
     */
    function startSession($pkg, $contents, $dest)
    {
    }

    /**
     * This method is used to process each of the tasks for a particular multiple class
     * type.  Simple tasks need not implement this method.
     * @param array an array of tasks
     * @access protected
     * @static
     * @abstract
     */
    function run($tasks)
    {
    }

    /**
     * @static
     * @final
     */
    function hasPostinstallTasks()
    {
        return isset($GLOBALS['_PEAR_TASK_POSTINSTANCES']);
    }

    /**
     * @static
     * @final
     */
     function runPostinstallTasks()
     {
         foreach ($GLOBALS['_PEAR_TASK_POSTINSTANCES'] as $class => $tasks) {
             $err = call_user_func(array($class, 'run'),
                  $GLOBALS['_PEAR_TASK_POSTINSTANCES'][$class]);
             if ($err) {
                 return PEAR_Task_Common::throwError($err);
             }
         }
         unset($GLOBALS['_PEAR_TASK_POSTINSTANCES']);
    }

    /**
     * Determines whether a role is a script
     * @return bool
     */
    function isScript()
    {
        return $this->type == 'script';
    }

    function throwError($msg, $code = -1)
    {
        include_once 'PEAR.php';
        return PEAR::raiseError($msg, $code);
    }
}
?>PEAR-1.7.2/PEAR/Task/Postinstallscript.php100644   1750   1750       34661 11014312742  13422 <?php
/**
 * <tasks:postinstallscript>
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Postinstallscript.php,v 1.20 2008/05/13 21:28:20 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * Base class
 */
require_once 'PEAR/Task/Common.php';
/**
 * Implements the postinstallscript file task.
 *
 * Note that post-install scripts are handled separately from installation, by the
 * "pear run-scripts" command
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Task_Postinstallscript extends PEAR_Task_Common
{
    var $type = 'script';
    var $_class;
    var $_params;
    var $_obj;
    /**
     *
     * @var PEAR_PackageFile_v2
     */
    var $_pkg;
    var $_contents;
    var $phase = PEAR_TASK_INSTALL;

    /**
     * Validate the raw xml at parsing-time.
     *
     * This also attempts to validate the script to make sure it meets the criteria
     * for a post-install script
     * @param PEAR_PackageFile_v2
     * @param array The XML contents of the <postinstallscript> tag
     * @param PEAR_Config
     * @param array the entire parsed <file> tag
     * @static
     */
    function validateXml($pkg, $xml, $config, $fileXml)
    {
        if ($fileXml['role'] != 'php') {
            return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
            $fileXml['name'] . '" must be role="php"');
        }
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        $file = $pkg->getFileContents($fileXml['name']);
        if (PEAR::isError($file)) {
            PEAR::popErrorHandling();
            return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                $fileXml['name'] . '" is not valid: ' .
                $file->getMessage());
        } elseif ($file === null) {
            return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                $fileXml['name'] . '" could not be retrieved for processing!');
        } else {
            $analysis = $pkg->analyzeSourceCode($file, true);
            if (!$analysis) {
                PEAR::popErrorHandling();
                $warnings = '';
                foreach ($pkg->getValidationWarnings() as $warn) {
                    $warnings .= $warn['message'] . "\n";
                }
                return array(PEAR_TASK_ERROR_INVALID, 'Analysis of post-install script "' .
                    $fileXml['name'] . '" failed: ' . $warnings);
            }
            if (count($analysis['declared_classes']) != 1) {
                PEAR::popErrorHandling();
                return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                    $fileXml['name'] . '" must declare exactly 1 class');
            }
            $class = $analysis['declared_classes'][0];
            if ($class != str_replace(array('/', '.php'), array('_', ''),
                  $fileXml['name']) . '_postinstall') {
                PEAR::popErrorHandling();
                return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                    $fileXml['name'] . '" class "' . $class . '" must be named "' .
                    str_replace(array('/', '.php'), array('_', ''),
                    $fileXml['name']) . '_postinstall"');
            }
            if (!isset($analysis['declared_methods'][$class])) {
                PEAR::popErrorHandling();
                return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                    $fileXml['name'] . '" must declare methods init() and run()');
            }
            $methods = array('init' => 0, 'run' => 1);
            foreach ($analysis['declared_methods'][$class] as $method) {
                if (isset($methods[$method])) {
                    unset($methods[$method]);
                }
            }
            if (count($methods)) {
                PEAR::popErrorHandling();
                return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                    $fileXml['name'] . '" must declare methods init() and run()');
            }
        }
        PEAR::popErrorHandling();
        $definedparams = array();
        $tasksNamespace = $pkg->getTasksNs() . ':';
        if (!isset($xml[$tasksNamespace . 'paramgroup']) && isset($xml['paramgroup'])) {
            // in order to support the older betas, which did not expect internal tags
            // to also use the namespace
            $tasksNamespace = '';
        }
        if (isset($xml[$tasksNamespace . 'paramgroup'])) {
            $params = $xml[$tasksNamespace . 'paramgroup'];
            if (!is_array($params) || !isset($params[0])) {
                $params = array($params);
            }
            foreach ($params as $param) {
                if (!isset($param[$tasksNamespace . 'id'])) {
                    return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                        $fileXml['name'] . '" <paramgroup> must have ' .
                        'an ' . $tasksNamespace . 'id> tag');
                }
                if (isset($param[$tasksNamespace . 'name'])) {
                    if (!in_array($param[$tasksNamespace . 'name'], $definedparams)) {
                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                            $fileXml['name'] . '" ' . $tasksNamespace .
                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
                            '" parameter "' . $param[$tasksNamespace . 'name'] .
                            '" has not been previously defined');
                    }
                    if (!isset($param[$tasksNamespace . 'conditiontype'])) {
                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                            $fileXml['name'] . '" ' . $tasksNamespace .
                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
                            '" must have a ' . $tasksNamespace . 
                            'conditiontype> tag containing either "=", ' .
                            '"!=", or "preg_match"');
                    }
                    if (!in_array($param[$tasksNamespace . 'conditiontype'],
                          array('=', '!=', 'preg_match'))) {
                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                            $fileXml['name'] . '" ' . $tasksNamespace .
                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
                            '" must have a ' . $tasksNamespace .
                            'conditiontype> tag containing either "=", ' .
                            '"!=", or "preg_match"');
                    }
                    if (!isset($param[$tasksNamespace . 'value'])) {
                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                            $fileXml['name'] . '" ' . $tasksNamespace .
                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
                            '" must have a ' . $tasksNamespace .
                            'value> tag containing expected parameter value');
                    }
                }
                if (isset($param[$tasksNamespace . 'instructions'])) {
                    if (!is_string($param[$tasksNamespace . 'instructions'])) {
                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                            $fileXml['name'] . '" ' . $tasksNamespace .
                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
                            '" ' . $tasksNamespace . 'instructions> must be simple text');
                    }
                }
                if (!isset($param[$tasksNamespace . 'param'])) {
                    continue; // <param> is no longer required
                }
                $subparams = $param[$tasksNamespace . 'param'];
                if (!is_array($subparams) || !isset($subparams[0])) {
                    $subparams = array($subparams);
                }
                foreach ($subparams as $subparam) {
                    if (!isset($subparam[$tasksNamespace . 'name'])) {
                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                            $fileXml['name'] . '" parameter for ' .
                            $tasksNamespace . 'paramgroup> id "' .
                            $param[$tasksNamespace . 'id'] . '" must have ' .
                            'a ' . $tasksNamespace . 'name> tag');
                    }
                    if (!preg_match('/[a-zA-Z0-9]+/',
                          $subparam[$tasksNamespace . 'name'])) {
                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                            $fileXml['name'] . '" parameter "' .
                            $subparam[$tasksNamespace . 'name'] .
                            '" for ' . $tasksNamespace . 'paramgroup> id "' .
                            $param[$tasksNamespace . 'id'] .
                            '" is not a valid name.  Must contain only alphanumeric characters');
                    }
                    if (!isset($subparam[$tasksNamespace . 'prompt'])) {
                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                            $fileXml['name'] . '" parameter "' .
                            $subparam[$tasksNamespace . 'name'] .
                            '" for ' . $tasksNamespace . 'paramgroup> id "' .
                            $param[$tasksNamespace . 'id'] .
                            '" must have a ' . $tasksNamespace . 'prompt> tag');
                    }
                    if (!isset($subparam[$tasksNamespace . 'type'])) {
                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                            $fileXml['name'] . '" parameter "' .
                            $subparam[$tasksNamespace . 'name'] .
                            '" for ' . $tasksNamespace . 'paramgroup> id "' .
                            $param[$tasksNamespace . 'id'] .
                            '" must have a ' . $tasksNamespace . 'type> tag');
                    }
                    $definedparams[] = $param[$tasksNamespace . 'id'] . '::' .
                    $subparam[$tasksNamespace . 'name'];
                }
            }
        }
        return true;
    }

    /**
     * Initialize a task instance with the parameters
     * @param array raw, parsed xml
     * @param array attributes from the <file> tag containing this task
     * @param string|null last installed version of this package, if any (useful for upgrades)
     */
    function init($xml, $fileattribs, $lastversion)
    {
        $this->_class = str_replace('/', '_', $fileattribs['name']);
        $this->_filename = $fileattribs['name'];
        $this->_class = str_replace ('.php', '', $this->_class) . '_postinstall';
        $this->_params = $xml;
        $this->_lastversion = $lastversion;
    }

    /**
     * Strip the tasks: namespace from internal params
     *
     * @access private
     */
    function _stripNamespace($params = null)
    {
        if ($params === null) {
            $params = array();
            if (!is_array($this->_params)) {
                return;
            }
            foreach ($this->_params as $i => $param) {
                if (is_array($param)) {
                    $param = $this->_stripNamespace($param);
                }
                $params[str_replace($this->_pkg->getTasksNs() . ':', '', $i)] = $param;
            }
            $this->_params = $params;
        } else {
            $newparams = array();
            foreach ($params as $i => $param) {
                if (is_array($param)) {
                    $param = $this->_stripNamespace($param);
                }
                $newparams[str_replace($this->_pkg->getTasksNs() . ':', '', $i)] = $param;
            }
            return $newparams;
        }
    }

    /**
     * Unlike other tasks, the installed file name is passed in instead of the file contents,
     * because this task is handled post-installation
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @param string file name
     * @return bool|PEAR_Error false to skip this file, PEAR_Error to fail
     *         (use $this->throwError)
     */
    function startSession($pkg, $contents)
    {
        if ($this->installphase != PEAR_TASK_INSTALL) {
            return false;
        }
        // remove the tasks: namespace if present
        $this->_pkg = $pkg;
        $this->_stripNamespace();
        $this->logger->log(0, 'Including external post-installation script "' .
            $contents . '" - any errors are in this script');
        include_once $contents;
        if (class_exists($this->_class)) {
            $this->logger->log(0, 'Inclusion succeeded');
        } else {
            return $this->throwError('init of post-install script class "' . $this->_class
                . '" failed');
        }
        $this->_obj = new $this->_class;
        $this->logger->log(1, 'running post-install script "' . $this->_class . '->init()"');
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        $res = $this->_obj->init($this->config, $pkg, $this->_lastversion);
        PEAR::popErrorHandling();
        if ($res) {
            $this->logger->log(0, 'init succeeded');
        } else {
            return $this->throwError('init of post-install script "' . $this->_class .
                '->init()" failed');
        }
        $this->_contents = $contents;
        return true;
    }

    /**
     * No longer used
     * @see PEAR_PackageFile_v2::runPostinstallScripts()
     * @param array an array of tasks
     * @param string install or upgrade
     * @access protected
     * @static
     */
    function run()
    {
    }
}
?>PEAR-1.7.2/PEAR/Task/Replace.php100644   1750   1750       15770 11014312742  11234 <?php
/**
 * <tasks:replace>
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Replace.php,v 1.17 2008/05/13 21:28:20 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * Base class
 */
require_once 'PEAR/Task/Common.php';
/**
 * Implements the replace file task.
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Task_Replace extends PEAR_Task_Common
{
    var $type = 'simple';
    var $phase = PEAR_TASK_PACKAGEANDINSTALL;
    var $_replacements;

    /**
     * Validate the raw xml at parsing-time.
     * @param PEAR_PackageFile_v2
     * @param array raw, parsed xml
     * @param PEAR_Config
     * @static
     */
    function validateXml($pkg, $xml, $config, $fileXml)
    {
        if (!isset($xml['attribs'])) {
            return array(PEAR_TASK_ERROR_NOATTRIBS);
        }
        if (!isset($xml['attribs']['type'])) {
            return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'type');
        }
        if (!isset($xml['attribs']['to'])) {
            return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'to');
        }
        if (!isset($xml['attribs']['from'])) {
            return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'from');
        }
        if ($xml['attribs']['type'] == 'pear-config') {
            if (!in_array($xml['attribs']['to'], $config->getKeys())) {
                return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'],
                    $config->getKeys());
            }
        } elseif ($xml['attribs']['type'] == 'php-const') {
            if (defined($xml['attribs']['to'])) {
                return true;
            } else {
                return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'],
                    array('valid PHP constant'));
            }
        } elseif ($xml['attribs']['type'] == 'package-info') {
            if (in_array($xml['attribs']['to'],
                array('name', 'summary', 'channel', 'notes', 'extends', 'description',
                    'release_notes', 'license', 'release-license', 'license-uri',
                    'version', 'api-version', 'state', 'api-state', 'release_date',
                    'date', 'time'))) {
                return true;
            } else {
                return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'],
                    array('name', 'summary', 'channel', 'notes', 'extends', 'description',
                    'release_notes', 'license', 'release-license', 'license-uri',
                    'version', 'api-version', 'state', 'api-state', 'release_date',
                    'date', 'time'));
            }
        } else {
            return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'type', $xml['attribs']['type'],
                array('pear-config', 'package-info', 'php-const'));
        }
        return true;
    }

    /**
     * Initialize a task instance with the parameters
     * @param array raw, parsed xml
     * @param unused
     */
    function init($xml, $attribs)
    {
        $this->_replacements = isset($xml['attribs']) ? array($xml) : $xml;
    }

    /**
     * Do a package.xml 1.0 replacement, with additional package-info fields available
     *
     * See validateXml() source for the complete list of allowed fields
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @param string file contents
     * @param string the eventual final file location (informational only)
     * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
     *         (use $this->throwError), otherwise return the new contents
     */
    function startSession($pkg, $contents, $dest)
    {
        $subst_from = $subst_to = array();
        foreach ($this->_replacements as $a) {
            $a = $a['attribs'];
            $to = '';
            if ($a['type'] == 'pear-config') {
                if ($this->installphase == PEAR_TASK_PACKAGE) {
                    return false;
                }
                if ($a['to'] == 'master_server') {
                    $chan = $this->registry->getChannel($pkg->getChannel());
                    if (!PEAR::isError($chan)) {
                        $to = $chan->getServer();
                    } else {
                        $this->logger->log(0, "$dest: invalid pear-config replacement: $a[to]");
                        return false;
                    }
                } else {
                    if ($this->config->isDefinedLayer('ftp')) {
                        // try the remote config file first
                        $to = $this->config->get($a['to'], 'ftp', $pkg->getChannel());
                        if (is_null($to)) {
                            // then default to local
                            $to = $this->config->get($a['to'], null, $pkg->getChannel());
                        }
                    } else {
                        $to = $this->config->get($a['to'], null, $pkg->getChannel());
                    }
                }
                if (is_null($to)) {
                    $this->logger->log(0, "$dest: invalid pear-config replacement: $a[to]");
                    return false;
                }
            } elseif ($a['type'] == 'php-const') {
                if ($this->installphase == PEAR_TASK_PACKAGE) {
                    return false;
                }
                if (defined($a['to'])) {
                    $to = constant($a['to']);
                } else {
                    $this->logger->log(0, "$dest: invalid php-const replacement: $a[to]");
                    return false;
                }
            } else {
                if ($t = $pkg->packageInfo($a['to'])) {
                    $to = $t;
                } else {
                    $this->logger->log(0, "$dest: invalid package-info replacement: $a[to]");
                    return false;
                }
            }
            if (!is_null($to)) {
                $subst_from[] = $a['from'];
                $subst_to[] = $to;
            }
        }
        $this->logger->log(3, "doing " . sizeof($subst_from) .
            " substitution(s) for $dest");
        if (sizeof($subst_from)) {
            $contents = str_replace($subst_from, $subst_to, $contents);
        }
        return $contents;
    }
}
?>PEAR-1.7.2/PEAR/Task/Unixeol.php100644   1750   1750        5077 11014312742  11263 <?php
/**
 * <tasks:unixeol>
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Unixeol.php,v 1.10 2008/05/13 21:28:20 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * Base class
 */
require_once 'PEAR/Task/Common.php';
/**
 * Implements the unix line endings file task.
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Task_Unixeol extends PEAR_Task_Common
{
    var $type = 'simple';
    var $phase = PEAR_TASK_PACKAGE;
    var $_replacements;

    /**
     * Validate the raw xml at parsing-time.
     * @param PEAR_PackageFile_v2
     * @param array raw, parsed xml
     * @param PEAR_Config
     * @static
     */
    function validateXml($pkg, $xml, $config, $fileXml)
    {
        if ($xml != '') {
            return array(PEAR_TASK_ERROR_INVALID, 'no attributes allowed');
        }
        return true;
    }

    /**
     * Initialize a task instance with the parameters
     * @param array raw, parsed xml
     * @param unused
     */
    function init($xml, $attribs)
    {
    }

    /**
     * Replace all line endings with line endings customized for the current OS
     *
     * See validateXml() source for the complete list of allowed fields
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @param string file contents
     * @param string the eventual final file location (informational only)
     * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
     *         (use $this->throwError), otherwise return the new contents
     */
    function startSession($pkg, $contents, $dest)
    {
        $this->logger->log(3, "replacing all line endings with \\n in $dest");
        return preg_replace("/\r\n|\n\r|\r|\n/", "\n", $contents);
    }
}
?>PEAR-1.7.2/PEAR/Task/Windowseol.php100644   1750   1750        5072 11014312742  11765 <?php
/**
 * <tasks:windowseol>
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Windowseol.php,v 1.9 2008/05/13 21:28:20 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * Base class
 */
require_once 'PEAR/Task/Common.php';
/**
 * Implements the windows line endsings file task.
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Task_Windowseol extends PEAR_Task_Common
{
    var $type = 'simple';
    var $phase = PEAR_TASK_PACKAGE;
    var $_replacements;

    /**
     * Validate the raw xml at parsing-time.
     * @param PEAR_PackageFile_v2
     * @param array raw, parsed xml
     * @param PEAR_Config
     * @static
     */
    function validateXml($pkg, $xml, $config, $fileXml)
    {
        if ($xml != '') {
            return array(PEAR_TASK_ERROR_INVALID, 'no attributes allowed');
        }
        return true;
    }

    /**
     * Initialize a task instance with the parameters
     * @param array raw, parsed xml
     * @param unused
     */
    function init($xml, $attribs)
    {
    }

    /**
     * Replace all line endings with windows line endings
     *
     * See validateXml() source for the complete list of allowed fields
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @param string file contents
     * @param string the eventual final file location (informational only)
     * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
     *         (use $this->throwError), otherwise return the new contents
     */
    function startSession($pkg, $contents, $dest)
    {
        $this->logger->log(3, "replacing all line endings with \\r\\n in $dest");
        return preg_replace("/\r\n|\n\r|\r|\n/", "\r\n", $contents);
    }
}
?>PEAR-1.7.2/PEAR/Validator/PECL.php100644   1750   1750        4154 11014312742  11401 <?php
/**
 * Channel Validator for the pecl.php.net channel
 *
 * PHP 4 and PHP 5
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2006 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: PECL.php,v 1.9 2008/01/03 20:26:37 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a5
 */
/**
 * This is the parent class for all validators
 */
require_once 'PEAR/Validate.php';
/**
 * Channel Validator for the pecl.php.net channel
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a5
 */
class PEAR_Validator_PECL extends PEAR_Validate
{
    function validateVersion()
    {
        if ($this->_state == PEAR_VALIDATE_PACKAGING) {
            $version = $this->_packagexml->getVersion();
            $versioncomponents = explode('.', $version);
            $last = array_pop($versioncomponents);
            if (substr($last, 1, 2) == 'rc') {
                $this->_addFailure('version', 'Release Candidate versions must have ' .
                'upper-case RC, not lower-case rc');
                return false;
            }
        }
        return true;
    }

    function validatePackageName()
    {
        $ret = parent::validatePackageName();
        if ($this->_packagexml->getPackageType() == 'extsrc' ||
              $this->_packagexml->getPackageType() == 'zendextsrc') {
            if (strtolower($this->_packagexml->getPackage()) !=
                  strtolower($this->_packagexml->getProvidesExtension())) {
                $this->_addWarning('providesextension', 'package name "' .
                    $this->_packagexml->getPackage() . '" is different from extension name "' .
                    $this->_packagexml->getProvidesExtension() . '"');
            }
        }
        return $ret;
    }
}
?>PEAR-1.7.2/PEAR/Autoloader.php100644   1750   1750       15402 11014312742  11046 <?php
/**
 * Class auto-loader
 *
 * PHP versions 4
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Autoloader.php,v 1.14 2008/01/03 20:26:34 cellog Exp $
 * @link       http://pear.php.net/manual/en/core.ppm.php#core.ppm.pear-autoloader
 * @since      File available since Release 0.1
 * @deprecated File deprecated in Release 1.4.0a1
 */

// /* vim: set expandtab tabstop=4 shiftwidth=4: */

if (!extension_loaded("overload")) {
    // die hard without ext/overload
    die("Rebuild PHP with the `overload' extension to use PEAR_Autoloader");
}

/**
 * Include for PEAR_Error and PEAR classes
 */
require_once "PEAR.php";

/**
 * This class is for objects where you want to separate the code for
 * some methods into separate classes.  This is useful if you have a
 * class with not-frequently-used methods that contain lots of code
 * that you would like to avoid always parsing.
 *
 * The PEAR_Autoloader class provides autoloading and aggregation.
 * The autoloading lets you set up in which classes the separated
 * methods are found.  Aggregation is the technique used to import new
 * methods, an instance of each class providing separated methods is
 * stored and called every time the aggregated method is called.
 *
 * @category   pear
 * @package    PEAR
 * @author Stig Bakken <ssb@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/manual/en/core.ppm.php#core.ppm.pear-autoloader
 * @since      File available since Release 0.1
 * @deprecated File deprecated in Release 1.4.0a1
 */
class PEAR_Autoloader extends PEAR
{
    // {{{ properties

    /**
     * Map of methods and classes where they are defined
     *
     * @var array
     *
     * @access private
     */
    var $_autoload_map = array();

    /**
     * Map of methods and aggregate objects
     *
     * @var array
     *
     * @access private
     */
    var $_method_map = array();

    // }}}
    // {{{ addAutoload()

    /**
     * Add one or more autoload entries.
     *
     * @param string $method     which method to autoload
     *
     * @param string $classname  (optional) which class to find the method in.
     *                           If the $method parameter is an array, this
     *                           parameter may be omitted (and will be ignored
     *                           if not), and the $method parameter will be
     *                           treated as an associative array with method
     *                           names as keys and class names as values.
     *
     * @return void
     *
     * @access public
     */
    function addAutoload($method, $classname = null)
    {
        if (is_array($method)) {
            array_walk($method, create_function('$a,&$b', '$b = strtolower($b);'));
            $this->_autoload_map = array_merge($this->_autoload_map, $method);
        } else {
            $this->_autoload_map[strtolower($method)] = $classname;
        }
    }

    // }}}
    // {{{ removeAutoload()

    /**
     * Remove an autoload entry.
     *
     * @param string $method  which method to remove the autoload entry for
     *
     * @return bool TRUE if an entry was removed, FALSE if not
     *
     * @access public
     */
    function removeAutoload($method)
    {
        $method = strtolower($method);
        $ok = isset($this->_autoload_map[$method]);
        unset($this->_autoload_map[$method]);
        return $ok;
    }

    // }}}
    // {{{ addAggregateObject()

    /**
     * Add an aggregate object to this object.  If the specified class
     * is not defined, loading it will be attempted following PEAR's
     * file naming scheme.  All the methods in the class will be
     * aggregated, except private ones (name starting with an
     * underscore) and constructors.
     *
     * @param string $classname  what class to instantiate for the object.
     *
     * @return void
     *
     * @access public
     */
    function addAggregateObject($classname)
    {
        $classname = strtolower($classname);
        if (!class_exists($classname)) {
            $include_file = preg_replace('/[^a-z0-9]/i', '_', $classname);
            include_once $include_file;
        }
        $obj =& new $classname;
        $methods = get_class_methods($classname);
        foreach ($methods as $method) {
            // don't import priviate methods and constructors
            if ($method{0} != '_' && $method != $classname) {
                $this->_method_map[$method] = $obj;
            }
        }
    }

    // }}}
    // {{{ removeAggregateObject()

    /**
     * Remove an aggregate object.
     *
     * @param string $classname  the class of the object to remove
     *
     * @return bool  TRUE if an object was removed, FALSE if not
     *
     * @access public
     */
    function removeAggregateObject($classname)
    {
        $ok = false;
        $classname = strtolower($classname);
        reset($this->_method_map);
        while (list($method, $obj) = each($this->_method_map)) {
            if (is_a($obj, $classname)) {
                unset($this->_method_map[$method]);
                $ok = true;
            }
        }
        return $ok;
    }

    // }}}
    // {{{ __call()

    /**
     * Overloaded object call handler, called each time an
     * undefined/aggregated method is invoked.  This method repeats
     * the call in the right aggregate object and passes on the return
     * value.
     *
     * @param string $method  which method that was called
     *
     * @param string $args    An array of the parameters passed in the
     *                        original call
     *
     * @return mixed  The return value from the aggregated method, or a PEAR
     *                error if the called method was unknown.
     */
    function __call($method, $args, &$retval)
    {
        $method = strtolower($method);
        if (empty($this->_method_map[$method]) && isset($this->_autoload_map[$method])) {
            $this->addAggregateObject($this->_autoload_map[$method]);
        }
        if (isset($this->_method_map[$method])) {
            $retval = call_user_func_array(array($this->_method_map[$method], $method), $args);
            return true;
        }
        return false;
    }

    // }}}
}

overload("PEAR_Autoloader");

?>
PEAR-1.7.2/PEAR/Builder.php100644   1750   1750       37137 11014312742  10346 <?php
/**
 * PEAR_Builder for building PHP extensions (PECL packages)
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Builder.php,v 1.34 2008/05/12 23:43:21 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 * 
 * TODO: log output parameters in PECL command line
 * TODO: msdev path in configuration
 */

/**
 * Needed for extending PEAR_Builder
 */
require_once 'PEAR/Common.php';
require_once 'PEAR/PackageFile.php';
/**
 * Class to handle building (compiling) extensions.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since PHP 4.0.2
 * @see        http://pear.php.net/manual/en/core.ppm.pear-builder.php
 */
class PEAR_Builder extends PEAR_Common
{
    // {{{ properties

    var $php_api_version = 0;
    var $zend_module_api_no = 0;
    var $zend_extension_api_no = 0;

    var $extensions_built = array();

    /**
     * @var string Used for reporting when it is not possible to pass function
     *             via extra parameter, e.g. log, msdevCallback
     */
    var $current_callback = null;

    // used for msdev builds
    var $_lastline = null;
    var $_firstline = null;
    // }}}
    // {{{ constructor

    /**
     * PEAR_Builder constructor.
     *
     * @param object $ui user interface object (instance of PEAR_Frontend_*)
     *
     * @access public
     */
    function PEAR_Builder(&$ui)
    {
        parent::PEAR_Common();
        $this->setFrontendObject($ui);
    }

    // }}}

    // {{{ _build_win32()

    /**
     * Build an extension from source on windows.
     * requires msdev
     */
    function _build_win32($descfile, $callback = null)
    {
        if (is_object($descfile)) {
            $pkg = $descfile;
            $descfile = $pkg->getPackageFile();
        } else {
            $pf = &new PEAR_PackageFile($this->config, $this->debug);
            $pkg = &$pf->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
            if (PEAR::isError($pkg)) {
                return $pkg;
            }
        }
        $dir = dirname($descfile);
        $old_cwd = getcwd();

        if (!file_exists($dir) || !is_dir($dir) || !chdir($dir)) {
            return $this->raiseError("could not chdir to $dir");
        }
        // packages that were in a .tar have the packagefile in this directory
        $vdir = $pkg->getPackage() . '-' . $pkg->getVersion();
        if (file_exists($dir) && is_dir($vdir)) {
            if (chdir($vdir)) {
                $dir = getcwd();
            } else {
                return $this->raiseError("could not chdir to " . realpath($vdir));
            }
        }

        $this->log(2, "building in $dir");

        $dsp = $pkg->getPackage().'.dsp';
        if (!file_exists("$dir/$dsp")) {
            return $this->raiseError("The DSP $dsp does not exist.");
        }
        // XXX TODO: make release build type configurable
        $command = 'msdev '.$dsp.' /MAKE "'.$pkg->getPackage(). ' - Release"';

        $err = $this->_runCommand($command, array(&$this, 'msdevCallback'));
        if (PEAR::isError($err)) {
            return $err;
        }

        // figure out the build platform and type
        $platform = 'Win32';
        $buildtype = 'Release';
        if (preg_match('/.*?'.$pkg->getPackage().'\s-\s(\w+)\s(.*?)-+/i',$this->_firstline,$matches)) {
            $platform = $matches[1];
            $buildtype = $matches[2];
        }

        if (preg_match('/(.*)?\s-\s(\d+).*?(\d+)/',$this->_lastline,$matches)) {
            if ($matches[2]) {
                // there were errors in the build
                return $this->raiseError("There were errors during compilation.");
            }
            $out = $matches[1];
        } else {
            return $this->raiseError("Did not understand the completion status returned from msdev.exe.");
        }

        // msdev doesn't tell us the output directory :/
        // open the dsp, find /out and use that directory
        $dsptext = join(file($dsp),'');

        // this regex depends on the build platform and type having been
        // correctly identified above.
        $regex ='/.*?!IF\s+"\$\(CFG\)"\s+==\s+("'.
                    $pkg->getPackage().'\s-\s'.
                    $platform.'\s'.
                    $buildtype.'").*?'.
                    '\/out:"(.*?)"/is';

        if ($dsptext && preg_match($regex,$dsptext,$matches)) {
            // what we get back is a relative path to the output file itself.
            $outfile = realpath($matches[2]);
        } else {
            return $this->raiseError("Could not retrieve output information from $dsp.");
        }
        // realpath returns false if the file doesn't exist
        if ($outfile && copy($outfile, "$dir/$out")) {
            $outfile = "$dir/$out";
        }

        $built_files[] = array(
            'file' => "$outfile",
            'php_api' => $this->php_api_version,
            'zend_mod_api' => $this->zend_module_api_no,
            'zend_ext_api' => $this->zend_extension_api_no,
            );

        return $built_files;
    }
    // }}}

    // {{{ msdevCallback()
    function msdevCallback($what, $data)
    {
        if (!$this->_firstline)
            $this->_firstline = $data;
        $this->_lastline = $data;
        call_user_func($this->current_callback, $what, $data);
    }
    // }}}

    // {{{ _harventInstDir
    /**
     * @param string
     * @param string
     * @param array
     * @access private
     */
    function _harvestInstDir($dest_prefix, $dirname, &$built_files)
    {
        $d = opendir($dirname);
        if (!$d)
            return false;

        $ret = true;
        while (($ent = readdir($d)) !== false) {
            if ($ent{0} == '.')
                continue;

            $full = $dirname . DIRECTORY_SEPARATOR . $ent;
            if (is_dir($full)) {
                if (!$this->_harvestInstDir(
                        $dest_prefix . DIRECTORY_SEPARATOR . $ent,
                        $full, $built_files)) {
                    $ret = false;
                    break;
                }
            } else {
                $dest = $dest_prefix . DIRECTORY_SEPARATOR . $ent;
                $built_files[] = array(
                        'file' => $full,
                        'dest' => $dest,
                        'php_api' => $this->php_api_version,
                        'zend_mod_api' => $this->zend_module_api_no,
                        'zend_ext_api' => $this->zend_extension_api_no,
                        );
            }
        }
        closedir($d);
        return $ret;
    }

    // }}}

    // {{{ build()

    /**
     * Build an extension from source.  Runs "phpize" in the source
     * directory, but compiles in a temporary directory
     * (/var/tmp/pear-build-USER/PACKAGE-VERSION).
     *
     * @param string|PEAR_PackageFile_v* $descfile path to XML package description file, or
     *               a PEAR_PackageFile object
     *
     * @param mixed $callback callback function used to report output,
     * see PEAR_Builder::_runCommand for details
     *
     * @return array an array of associative arrays with built files,
     * format:
     * array( array( 'file' => '/path/to/ext.so',
     *               'php_api' => YYYYMMDD,
     *               'zend_mod_api' => YYYYMMDD,
     *               'zend_ext_api' => YYYYMMDD ),
     *        ... )
     *
     * @access public
     *
     * @see PEAR_Builder::_runCommand
     */
    function build($descfile, $callback = null)
    {
        $this->current_callback = $callback;
        if (PEAR_OS == "Windows") {
            return $this->_build_win32($descfile,$callback);
        }
        if (PEAR_OS != 'Unix') {
            return $this->raiseError("building extensions not supported on this platform");
        }
        if (is_object($descfile)) {
            $pkg = $descfile;
            $descfile = $pkg->getPackageFile();
            if (is_a($pkg, 'PEAR_PackageFile_v1')) {
                $dir = dirname($descfile);
            } else {
                $dir = $pkg->_config->get('temp_dir') . '/' . $pkg->getName();
                // automatically delete at session end
                $this->addTempFile($dir);
            }
        } else {
            $pf = &new PEAR_PackageFile($this->config);
            $pkg = &$pf->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
            if (PEAR::isError($pkg)) {
                return $pkg;
            }
            $dir = dirname($descfile);
        }
        $old_cwd = getcwd();
        if (!file_exists($dir) || !is_dir($dir) || !chdir($dir)) {
            return $this->raiseError("could not chdir to $dir");
        }
        $vdir = $pkg->getPackage() . '-' . $pkg->getVersion();
        if (is_dir($vdir)) {
            chdir($vdir);
        }
        $dir = getcwd();
        $this->log(2, "building in $dir");
        putenv('PATH=' . $this->config->get('bin_dir') . ':' . getenv('PATH'));
        $err = $this->_runCommand("phpize", array(&$this, 'phpizeCallback'));
        if (PEAR::isError($err)) {
            return $err;
        }
        if (!$err) {
            return $this->raiseError("`phpize' failed");
        }

        // {{{ start of interactive part
        $configure_command = "$dir/configure";
        $configure_options = $pkg->getConfigureOptions();
        if ($configure_options) {
            foreach ($configure_options as $o) {
                $default = array_key_exists('default', $o) ? $o['default'] : null;
                list($r) = $this->ui->userDialog('build',
                                                 array($o['prompt']),
                                                 array('text'),
                                                 array($default));
                if (substr($o['name'], 0, 5) == 'with-' &&
                    ($r == 'yes' || $r == 'autodetect')) {
                    $configure_command .= " --$o[name]";
                } else {
                    $configure_command .= " --$o[name]=".trim($r);
                }
            }
        }
        // }}} end of interactive part

        // FIXME make configurable
        if(!$user=getenv('USER')){
            $user='defaultuser';
        }
        $build_basedir = "/var/tmp/pear-build-$user";
        $build_dir = "$build_basedir/$vdir";
        $inst_dir = "$build_basedir/install-$vdir";
        $this->log(1, "building in $build_dir");
        if (is_dir($build_dir)) {
            System::rm(array('-rf', $build_dir));
        }
        if (!System::mkDir(array('-p', $build_dir))) {
            return $this->raiseError("could not create build dir: $build_dir");
        }
        $this->addTempFile($build_dir);
        if (!System::mkDir(array('-p', $inst_dir))) {
            return $this->raiseError("could not create temporary install dir: $inst_dir");
        }
        $this->addTempFile($inst_dir);

        if (getenv('MAKE')) {
            $make_command = getenv('MAKE');
        } else {
            $make_command = 'make';
        }
        $to_run = array(
            $configure_command,
            $make_command,
            "$make_command INSTALL_ROOT=\"$inst_dir\" install",
            "find \"$inst_dir\" | xargs ls -dils"
            );
        if (!file_exists($build_dir) || !is_dir($build_dir) || !chdir($build_dir)) {
            return $this->raiseError("could not chdir to $build_dir");
        }
        putenv('PHP_PEAR_VERSION=1.7.2');
        foreach ($to_run as $cmd) {
            $err = $this->_runCommand($cmd, $callback);
            if (PEAR::isError($err)) {
                chdir($old_cwd);
                return $err;
            }
            if (!$err) {
                chdir($old_cwd);
                return $this->raiseError("`$cmd' failed");
            }
        }
        if (!($dp = opendir("modules"))) {
            chdir($old_cwd);
            return $this->raiseError("no `modules' directory found");
        }
        $built_files = array();
        $prefix = exec("php-config --prefix");
        $this->_harvestInstDir($prefix, $inst_dir . DIRECTORY_SEPARATOR . $prefix, $built_files);
        chdir($old_cwd);
        return $built_files;
    }

    // }}}
    // {{{ phpizeCallback()

    /**
     * Message callback function used when running the "phpize"
     * program.  Extracts the API numbers used.  Ignores other message
     * types than "cmdoutput".
     *
     * @param string $what the type of message
     * @param mixed $data the message
     *
     * @return void
     *
     * @access public
     */
    function phpizeCallback($what, $data)
    {
        if ($what != 'cmdoutput') {
            return;
        }
        $this->log(1, rtrim($data));
        if (preg_match('/You should update your .aclocal.m4/', $data)) {
            return;
        }
        $matches = array();
        if (preg_match('/^\s+(\S[^:]+):\s+(\d{8})/', $data, $matches)) {
            $member = preg_replace('/[^a-z]/', '_', strtolower($matches[1]));
            $apino = (int)$matches[2];
            if (isset($this->$member)) {
                $this->$member = $apino;
                //$msg = sprintf("%-22s : %d", $matches[1], $apino);
                //$this->log(1, $msg);
            }
        }
    }

    // }}}
    // {{{ _runCommand()

    /**
     * Run an external command, using a message callback to report
     * output.  The command will be run through popen and output is
     * reported for every line with a "cmdoutput" message with the
     * line string, including newlines, as payload.
     *
     * @param string $command the command to run
     *
     * @param mixed $callback (optional) function to use as message
     * callback
     *
     * @return bool whether the command was successful (exit code 0
     * means success, any other means failure)
     *
     * @access private
     */
    function _runCommand($command, $callback = null)
    {
        $this->log(1, "running: $command");
        $pp = popen("$command 2>&1", "r");
        if (!$pp) {
            return $this->raiseError("failed to run `$command'");
        }
        if ($callback && $callback[0]->debug == 1) {
            $olddbg = $callback[0]->debug;
            $callback[0]->debug = 2;
        }

        while ($line = fgets($pp, 1024)) {
            if ($callback) {
                call_user_func($callback, 'cmdoutput', $line);
            } else {
                $this->log(2, rtrim($line));
            }
        }
        if ($callback && isset($olddbg)) {
            $callback[0]->debug = $olddbg;
        }
        if (is_resource($pp)) {
            $exitcode = pclose($pp);
        } else {
            $exitcode = -1;
        }
        return ($exitcode == 0);
    }

    // }}}
    // {{{ log()

    function log($level, $msg)
    {
        if ($this->current_callback) {
            if ($this->debug >= $level) {
                call_user_func($this->current_callback, 'output', $msg);
            }
            return;
        }
        return PEAR_Common::log($level, $msg);
    }

    // }}}
}

?>
PEAR-1.7.2/PEAR/ChannelFile.php100644   1750   1750      157212 11014312742  11145 <?php
/**
 * PEAR_ChannelFile, the channel handling class
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: ChannelFile.php,v 1.80 2008/01/03 20:26:34 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * Needed for error handling
 */
require_once 'PEAR/ErrorStack.php';
require_once 'PEAR/XMLParser.php';
require_once 'PEAR/Common.php';

/**
 * Error code if the channel.xml <channel> tag does not contain a valid version
 */
define('PEAR_CHANNELFILE_ERROR_NO_VERSION', 1);
/**
 * Error code if the channel.xml <channel> tag version is not supported (version 1.0 is the only supported version,
 * currently
 */
define('PEAR_CHANNELFILE_ERROR_INVALID_VERSION', 2);

/**
 * Error code if parsing is attempted with no xml extension
 */
define('PEAR_CHANNELFILE_ERROR_NO_XML_EXT', 3);

/**
 * Error code if creating the xml parser resource fails
 */
define('PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER', 4);

/**
 * Error code used for all sax xml parsing errors
 */
define('PEAR_CHANNELFILE_ERROR_PARSER_ERROR', 5);

/**#@+
 * Validation errors
 */
/**
 * Error code when channel name is missing
 */
define('PEAR_CHANNELFILE_ERROR_NO_NAME', 6);
/**
 * Error code when channel name is invalid
 */
define('PEAR_CHANNELFILE_ERROR_INVALID_NAME', 7);
/**
 * Error code when channel summary is missing
 */
define('PEAR_CHANNELFILE_ERROR_NO_SUMMARY', 8);
/**
 * Error code when channel summary is multi-line
 */
define('PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY', 9);
/**
 * Error code when channel server is missing for xmlrpc or soap protocol
 */
define('PEAR_CHANNELFILE_ERROR_NO_HOST', 10);
/**
 * Error code when channel server is invalid for xmlrpc or soap protocol
 */
define('PEAR_CHANNELFILE_ERROR_INVALID_HOST', 11);
/**
 * Error code when a mirror name is invalid
 */
define('PEAR_CHANNELFILE_ERROR_INVALID_MIRROR', 21);
/**
 * Error code when a mirror type is invalid
 */
define('PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE', 22);
/**
 * Error code when an attempt is made to generate xml, but the parsed content is invalid
 */
define('PEAR_CHANNELFILE_ERROR_INVALID', 23);
/**
 * Error code when an empty package name validate regex is passed in
 */
define('PEAR_CHANNELFILE_ERROR_EMPTY_REGEX', 24);
/**
 * Error code when a <function> tag has no version
 */
define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION', 25);
/**
 * Error code when a <function> tag has no name
 */
define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME', 26);
/**
 * Error code when a <validatepackage> tag has no name
 */
define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME', 27);
/**
 * Error code when a <validatepackage> tag has no version attribute
 */
define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION', 28);
/**
 * Error code when a mirror does not exist but is called for in one of the set*
 * methods.
 */
define('PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND', 32);
/**
 * Error code when a server port is not numeric
 */
define('PEAR_CHANNELFILE_ERROR_INVALID_PORT', 33);
/**
 * Error code when <static> contains no version attribute
 */
define('PEAR_CHANNELFILE_ERROR_NO_STATICVERSION', 34);
/**
 * Error code when <baseurl> contains no type attribute in a <rest> protocol definition
 */
define('PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE', 35);
/** 
 * Error code when a mirror is defined and the channel.xml represents the __uri pseudo-channel
 */
define('PEAR_CHANNELFILE_URI_CANT_MIRROR', 36);
/** 
 * Error code when ssl attribute is present and is not "yes"
 */
define('PEAR_CHANNELFILE_ERROR_INVALID_SSL', 37);
/**#@-*/

/**
 * Mirror types allowed.  Currently only internet servers are recognized.
 */
$GLOBALS['_PEAR_CHANNELS_MIRROR_TYPES'] =  array('server');


/**
 * The Channel handling class
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_ChannelFile {
    /**
     * @access private
     * @var PEAR_ErrorStack
     * @access private
     */
    var $_stack;
    
    /**
     * Supported channel.xml versions, for parsing
     * @var array
     * @access private
     */
    var $_supportedVersions = array('1.0');

    /**
     * Parsed channel information
     * @var array
     * @access private
     */
    var $_channelInfo;

    /**
     * index into the subchannels array, used for parsing xml
     * @var int
     * @access private
     */
    var $_subchannelIndex;

    /**
     * index into the mirrors array, used for parsing xml
     * @var int
     * @access private
     */
    var $_mirrorIndex;
    
    /**
     * Flag used to determine the validity of parsed content
     * @var boolean
     * @access private
     */
    var $_isValid = false;

    function PEAR_ChannelFile()
    {
        $this->_stack = &new PEAR_ErrorStack('PEAR_ChannelFile');
        $this->_stack->setErrorMessageTemplate($this->_getErrorMessage());
        $this->_isValid = false;
    }
    
    /**
     * @return array
     * @access protected
     */
    function _getErrorMessage()
    {
        return
            array(
                PEAR_CHANNELFILE_ERROR_INVALID_VERSION =>
                    'While parsing channel.xml, an invalid version number "%version% was passed in, expecting one of %versions%',
                PEAR_CHANNELFILE_ERROR_NO_VERSION =>
                    'No version number found in <channel> tag',
                PEAR_CHANNELFILE_ERROR_NO_XML_EXT =>
                    '%error%',
                PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER =>
                    'Unable to create XML parser',
                PEAR_CHANNELFILE_ERROR_PARSER_ERROR =>
                    '%error%',
                PEAR_CHANNELFILE_ERROR_NO_NAME =>
                    'Missing channel name',
                PEAR_CHANNELFILE_ERROR_INVALID_NAME =>
                    'Invalid channel %tag% "%name%"',
                PEAR_CHANNELFILE_ERROR_NO_SUMMARY =>
                    'Missing channel summary',
                PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY =>
                    'Channel summary should be on one line, but is multi-line',
                PEAR_CHANNELFILE_ERROR_NO_HOST =>
                    'Missing channel server for %type% server',
                PEAR_CHANNELFILE_ERROR_INVALID_HOST =>
                    'Server name "%server%" is invalid for %type% server',
                PEAR_CHANNELFILE_ERROR_INVALID_MIRROR =>
                    'Invalid mirror name "%name%", mirror type %type%',
                PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE =>
                    'Invalid mirror type "%type%"',
                PEAR_CHANNELFILE_ERROR_INVALID =>
                    'Cannot generate xml, contents are invalid',
                PEAR_CHANNELFILE_ERROR_EMPTY_REGEX =>
                    'packagenameregex cannot be empty',
                PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION =>
                    '%parent% %protocol% function has no version',
                PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME =>
                    '%parent% %protocol% function has no name',
                PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE =>
                    '%parent% rest baseurl has no type',
                PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME =>
                    'Validation package has no name in <validatepackage> tag',
                PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION =>
                    'Validation package "%package%" has no version',
                PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND =>
                    'Mirror "%mirror%" does not exist',
                PEAR_CHANNELFILE_ERROR_INVALID_PORT =>
                    'Port "%port%" must be numeric',
                PEAR_CHANNELFILE_ERROR_NO_STATICVERSION =>
                    '<static> tag must contain version attribute',
                PEAR_CHANNELFILE_URI_CANT_MIRROR =>
                    'The __uri pseudo-channel cannot have mirrors',
                PEAR_CHANNELFILE_ERROR_INVALID_SSL =>
                    '%server% has invalid ssl attribute "%ssl%" can only be yes or not present',
            );
    }

    /**
     * @param string contents of package.xml file
     * @return bool success of parsing
     */
    function fromXmlString($data)
    {
        if (preg_match('/<channel\s+version="([0-9]+\.[0-9]+)"/', $data, $channelversion)) {
            if (!in_array($channelversion[1], $this->_supportedVersions)) {
                $this->_stack->push(PEAR_CHANNELFILE_ERROR_INVALID_VERSION, 'error',
                    array('version' => $channelversion[1]));
                return false;
            }
            $parser = new PEAR_XMLParser;
            $result = $parser->parse($data);
            if ($result !== true) {
                if ($result->getCode() == 1) {
                    $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_XML_EXT, 'error',
                        array('error' => $result->getMessage()));
                } else {
                    $this->_stack->push(PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER, 'error');
                }
                return false;
            }
            $this->_channelInfo = $parser->getData();
            return true;
        } else {
            $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_VERSION, 'error', array('xml' => $data));
            return false;
        }
    }
    
    /**
     * @return array
     */
    function toArray()
    {
        if (!$this->_isValid && !$this->validate()) {
            return false;
        }
        return $this->_channelInfo;
    }
    
    /**
     * @param array
     * @static
     * @return PEAR_ChannelFile|false false if invalid
     */
    function &fromArray($data, $compatibility = false, $stackClass = 'PEAR_ErrorStack')
    {
        $a = new PEAR_ChannelFile($compatibility, $stackClass);
        $a->_fromArray($data);
        if (!$a->validate()) {
            $a = false;
            return $a;
        }
        return $a;
    }

    /**
     * Unlike {@link fromArray()} this does not do any validation
     * @param array
     * @static
     * @return PEAR_ChannelFile
     */
    function &fromArrayWithErrors($data, $compatibility = false,
                                  $stackClass = 'PEAR_ErrorStack')
    {
        $a = new PEAR_ChannelFile($compatibility, $stackClass);
        $a->_fromArray($data);
        return $a;
    }
    
    /**
     * @param array
     * @access private
     */
    function _fromArray($data)
    {
        $this->_channelInfo = $data;
    }
    
    /**
     * Wrapper to {@link PEAR_ErrorStack::getErrors()}
     * @param boolean determines whether to purge the error stack after retrieving
     * @return array
     */
    function getErrors($purge = false)
    {
        return $this->_stack->getErrors($purge);
    }

    /**
     * Unindent given string (?)
     *
     * @param string $str The string that has to be unindented.
     * @return string
     * @access private
     */
    function _unIndent($str)
    {
        // remove leading newlines
        $str = preg_replace('/^[\r\n]+/', '', $str);
        // find whitespace at the beginning of the first line
        $indent_len = strspn($str, " \t");
        $indent = substr($str, 0, $indent_len);
        $data = '';
        // remove the same amount of whitespace from following lines
        foreach (explode("\n", $str) as $line) {
            if (substr($line, 0, $indent_len) == $indent) {
                $data .= substr($line, $indent_len) . "\n";
            }
        }
        return $data;
    }

    /**
     * Parse a channel.xml file.  Expects the name of
     * a channel xml file as input.
     *
     * @param string  $descfile  name of channel xml file
     * @return bool success of parsing
     */
    function fromXmlFile($descfile)
    {
        if (!file_exists($descfile) || !is_file($descfile) || !is_readable($descfile) ||
             (!$fp = fopen($descfile, 'r'))) {
            require_once 'PEAR.php';
            return PEAR::raiseError("Unable to open $descfile");
        }

        // read the whole thing so we only get one cdata callback
        // for each block of cdata
        fclose($fp);
        $data = file_get_contents($descfile);
        return $this->fromXmlString($data);
    }

    /**
     * Parse channel information from different sources
     *
     * This method is able to extract information about a channel
     * from an .xml file or a string
     *
     * @access public
     * @param  string Filename of the source or the source itself
     * @return bool
     */
    function fromAny($info)
    {
        if (is_string($info) && file_exists($info) && strlen($info) < 255) {
            $tmp = substr($info, -4);
            if ($tmp == '.xml') {
                $info = $this->fromXmlFile($info);
            } else {
                $fp = fopen($info, "r");
                $test = fread($fp, 5);
                fclose($fp);
                if ($test == "<?xml") {
                    $info = $this->fromXmlFile($info);
                }
            }
            if (PEAR::isError($info)) {
                require_once 'PEAR.php';
                return PEAR::raiseError($info);
            }
        }
        if (is_string($info)) {
            $info = $this->fromXmlString($info);
        }
        return $info;
    }

    /**
     * Return an XML document based on previous parsing and modifications
     *
     * @return string XML data
     *
     * @access public
     */
    function toXml()
    {
        if (!$this->_isValid && !$this->validate()) {
            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID);
            return false;
        }
        if (!isset($this->_channelInfo['attribs']['version'])) {
            $this->_channelInfo['attribs']['version'] = '1.0';
        }
        $channelInfo = $this->_channelInfo;
        $ret = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
        $ret .= "<channel version=\"" .
            $channelInfo['attribs']['version'] . "\" xmlns=\"http://pear.php.net/channel-1.0\"
  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
  xsi:schemaLocation=\"http://pear.php.net/dtd/channel-"
            . $channelInfo['attribs']['version'] . " http://pear.php.net/dtd/channel-" .
            $channelInfo['attribs']['version'] . ".xsd\">
 <name>$channelInfo[name]</name>
 <summary>" . htmlspecialchars($channelInfo['summary'])."</summary>
";
        if (isset($channelInfo['suggestedalias'])) {
            $ret .= ' <suggestedalias>' . $channelInfo['suggestedalias'] . "</suggestedalias>\n";
        }
        if (isset($channelInfo['validatepackage'])) {
            $ret .= ' <validatepackage version="' .
                $channelInfo['validatepackage']['attribs']['version']. '">' .
                htmlspecialchars($channelInfo['validatepackage']['_content']) .
                "</validatepackage>\n";
        }
        $ret .= " <servers>\n";
        $ret .= '  <primary';
        if (isset($channelInfo['servers']['primary']['attribs']['ssl'])) {
            $ret .= ' ssl="' . $channelInfo['servers']['primary']['attribs']['ssl'] . '"';
        }
        if (isset($channelInfo['servers']['primary']['attribs']['port'])) {
            $ret .= ' port="' . $channelInfo['servers']['primary']['attribs']['port'] . '"';
        }
        $ret .= ">\n";
        if (isset($channelInfo['servers']['primary']['xmlrpc'])) {
            $ret .= $this->_makeXmlrpcXml($channelInfo['servers']['primary']['xmlrpc'], '   ');
        }
        if (isset($channelInfo['servers']['primary']['rest'])) {
            $ret .= $this->_makeRestXml($channelInfo['servers']['primary']['rest'], '   ');
        }
        if (isset($channelInfo['servers']['primary']['soap'])) {
            $ret .= $this->_makeSoapXml($channelInfo['servers']['primary']['soap'], '   ');
        }
        $ret .= "  </primary>\n";
        if (isset($channelInfo['servers']['mirror'])) {
            $ret .= $this->_makeMirrorsXml($channelInfo);
        }
        $ret .= " </servers>\n";
        $ret .= "</channel>";
        return str_replace("\r", "\n", str_replace("\r\n", "\n", $ret));
    }

    /**
     * Generate the <xmlrpc> tag
     * @access private
     */
    function _makeXmlrpcXml($info, $indent)
    {
        $ret = $indent . "<xmlrpc";
        if (isset($info['attribs']['path'])) {
            $ret .= ' path="' . htmlspecialchars($info['attribs']['path']) . '"';
        }
        $ret .= ">\n";
        $ret .= $this->_makeFunctionsXml($info['function'], "$indent ");
        $ret .= $indent . "</xmlrpc>\n";
        return $ret;
    }

    /**
     * Generate the <soap> tag
     * @access private
     */
    function _makeSoapXml($info, $indent)
    {
        $ret = $indent . "<soap";
        if (isset($info['attribs']['path'])) {
            $ret .= ' path="' . htmlspecialchars($info['attribs']['path']) . '"';
        }
        $ret .= ">\n";
        $ret .= $this->_makeFunctionsXml($info['function'], "$indent ");
        $ret .= $indent . "</soap>\n";
        return $ret;
    }

    /**
     * Generate the <rest> tag
     * @access private
     */
    function _makeRestXml($info, $indent)
    {
        $ret = $indent . "<rest>\n";
        if (!isset($info['baseurl'][0])) {
            $info['baseurl'] = array($info['baseurl']);
        }
        foreach ($info['baseurl'] as $url) {
            $ret .= "$indent <baseurl type=\"" . $url['attribs']['type'] . "\"";
            $ret .= ">" . $url['_content'] . "</baseurl>\n";
        }
        $ret .= $indent . "</rest>\n";
        return $ret;
    }

    /**
     * Generate the <mirrors> tag
     * @access private
     */
    function _makeMirrorsXml($channelInfo)
    {
        $ret = "";
        if (!isset($channelInfo['servers']['mirror'][0])) {
            $channelInfo['servers']['mirror'] = array($channelInfo['servers']['mirror']);
        }
        foreach ($channelInfo['servers']['mirror'] as $mirror) {
            $ret .= '  <mirror host="' . $mirror['attribs']['host'] . '"';
            if (isset($mirror['attribs']['port'])) {
                $ret .= ' port="' . $mirror['attribs']['port'] . '"';
            }
            if (isset($mirror['attribs']['ssl'])) {
                $ret .= ' ssl="' . $mirror['attribs']['ssl'] . '"';
            }
            $ret .= ">\n";
            if (isset($mirror['xmlrpc']) || isset($mirror['soap'])) {
                if (isset($mirror['xmlrpc'])) {
                    $ret .= $this->_makeXmlrpcXml($mirror['xmlrpc'], '   ');
                }
                if (isset($mirror['rest'])) {
                    $ret .= $this->_makeRestXml($mirror['rest'], '   ');
                }
                if (isset($mirror['soap'])) {
                    $ret .= $this->_makeSoapXml($mirror['soap'], '   ');
                }
                $ret .= "  </mirror>\n";
            } else {
                $ret .= "/>\n";
            }
        }
        return $ret;
    }

    /**
     * Generate the <functions> tag
     * @access private
     */
    function _makeFunctionsXml($functions, $indent, $rest = false)
    {
        $ret = '';
        if (!isset($functions[0])) {
            $functions = array($functions);
        }
        foreach ($functions as $function) {
            $ret .= "$indent<function version=\"" . $function['attribs']['version'] . "\"";
            if ($rest) {
                $ret .= ' uri="' . $function['attribs']['uri'] . '"';
            }
            $ret .= ">" . $function['_content'] . "</function>\n";
        }
        return $ret;
    }

    /**
     * Validation error.  Also marks the object contents as invalid
     * @param error code
     * @param array error information
     * @access private
     */
    function _validateError($code, $params = array())
    {
        $this->_stack->push($code, 'error', $params);
        $this->_isValid = false;
    }

    /**
     * Validation warning.  Does not mark the object contents invalid.
     * @param error code
     * @param array error information
     * @access private
     */
    function _validateWarning($code, $params = array())
    {
        $this->_stack->push($code, 'warning', $params);
    }

    /**
     * Validate parsed file.
     *
     * @access public
     * @return boolean
     */
    function validate()
    {
        $this->_isValid = true;
        $info = $this->_channelInfo;
        if (empty($info['name'])) {
            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_NAME);
        } elseif (!$this->validChannelServer($info['name'])) {
            if ($info['name'] != '__uri') {
                $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, array('tag' => 'name',
                    'name' => $info['name']));
            }
        }
        if (empty($info['summary'])) {
            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY);
        } elseif (strpos(trim($info['summary']), "\n") !== false) {
            $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY,
                array('summary' => $info['summary']));
        }
        if (isset($info['suggestedalias'])) {
            if (!$this->validChannelServer($info['suggestedalias'])) {
                $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
                    array('tag' => 'suggestedalias', 'name' =>$info['suggestedalias']));
            }
        }
        if (isset($info['localalias'])) {
            if (!$this->validChannelServer($info['localalias'])) {
                $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
                    array('tag' => 'localalias', 'name' =>$info['localalias']));
            }
        }
        if (isset($info['validatepackage'])) {
            if (!isset($info['validatepackage']['_content'])) {
                $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME);
            }
            if (!isset($info['validatepackage']['attribs']['version'])) {
                $content = isset($info['validatepackage']['_content']) ?
                    $info['validatepackage']['_content'] :
                    null;
                $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION,
                    array('package' => $content));
            }
        }
        if (isset($info['servers']['primary']['attribs']['port']) &&
              !is_numeric($info['servers']['primary']['attribs']['port'])) {
            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_PORT,
                array('port' => $info['servers']['primary']['attribs']['port']));
        }
        if (isset($info['servers']['primary']['attribs']['ssl']) &&
              $info['servers']['primary']['attribs']['ssl'] != 'yes') {
            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL,
                array('ssl' => $info['servers']['primary']['attribs']['ssl'],
                    'server' => $info['name']));
        }

        if (isset($info['servers']['primary']['xmlrpc']) &&
              isset($info['servers']['primary']['xmlrpc']['function'])) {
            $this->_validateFunctions('xmlrpc', $info['servers']['primary']['xmlrpc']['function']);
        }
        if (isset($info['servers']['primary']['soap']) &&
              isset($info['servers']['primary']['soap']['function'])) {
            $this->_validateFunctions('soap', $info['servers']['primary']['soap']['function']);
        }
        if (isset($info['servers']['primary']['rest']) &&
              isset($info['servers']['primary']['rest']['baseurl'])) {
            $this->_validateFunctions('rest', $info['servers']['primary']['rest']['baseurl']);
        }
        if (isset($info['servers']['mirror'])) {
            if ($this->_channelInfo['name'] == '__uri') {
                $this->_validateError(PEAR_CHANNELFILE_URI_CANT_MIRROR);
            }
            if (!isset($info['servers']['mirror'][0])) {
                $info['servers']['mirror'] = array($info['servers']['mirror']);
            }
            foreach ($info['servers']['mirror'] as $mirror) {
                if (!isset($mirror['attribs']['host'])) {
                    $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_HOST,
                      array('type' => 'mirror'));
                } elseif (!$this->validChannelServer($mirror['attribs']['host'])) {
                    $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_HOST,
                        array('server' => $mirror['attribs']['host'], 'type' => 'mirror'));
                }
                if (isset($mirror['attribs']['ssl']) && $mirror['attribs']['ssl'] != 'yes') {
                    $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL,
                        array('ssl' => $info['ssl'], 'server' => $mirror['attribs']['host']));
                }
                if (isset($mirror['xmlrpc'])) {
                    $this->_validateFunctions('xmlrpc',
                        $mirror['xmlrpc']['function'], $mirror['attribs']['host']);
                }
                if (isset($mirror['soap'])) {
                    $this->_validateFunctions('soap', $mirror['soap']['function'],
                        $mirror['attribs']['host']);
                }
                if (isset($mirror['rest'])) {
                    $this->_validateFunctions('rest', $mirror['rest']['baseurl'],
                        $mirror['attribs']['host']);
                }
            }
        }
        return $this->_isValid;
    }

    /**
     * @param string xmlrpc or soap - protocol name this function applies to
     * @param array the functions
     * @param string the name of the parent element (mirror name, for instance)
     */
    function _validateFunctions($protocol, $functions, $parent = '')
    {
        if (!isset($functions[0])) {
            $functions = array($functions);
        }
        foreach ($functions as $function) {
            if (!isset($function['_content']) || empty($function['_content'])) {
                $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME,
                    array('parent' => $parent, 'protocol' => $protocol));
            }
            if ($protocol == 'rest') {
                if (!isset($function['attribs']['type']) ||
                      empty($function['attribs']['type'])) {
                    $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_BASEURLTYPE,
                        array('parent' => $parent, 'protocol' => $protocol));
                }
            } else {
                if (!isset($function['attribs']['version']) ||
                      empty($function['attribs']['version'])) {
                    $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION,
                        array('parent' => $parent, 'protocol' => $protocol));
                }
            }
        }
    }

    /**
     * Test whether a string contains a valid channel server.
     * @param string $ver the package version to test
     * @return bool
     */
    function validChannelServer($server)
    {
        if ($server == '__uri') {
            return true;
        }
        return (bool) preg_match(PEAR_CHANNELS_SERVER_PREG, $server);
    }

    /**
     * @return string|false
     */
    function getName()
    {
        if (isset($this->_channelInfo['name'])) {
            return $this->_channelInfo['name'];
        } else {
            return false;
        }
    }

    /**
     * @return string|false
     */
    function getServer()
    {
        if (isset($this->_channelInfo['name'])) {
            return $this->_channelInfo['name'];
        } else {
            return false;
        }
    }

    /**
     * @return int|80 port number to connect to
     */
    function getPort($mirror = false)
    {
        if ($mirror) {
            if ($mir = $this->getMirror($mirror)) {
                if (isset($mir['attribs']['port'])) {
                    return $mir['attribs']['port'];
                } else {
                    if ($this->getSSL($mirror)) {
                        return 443;
                    }
                    return 80;
                }
            }
            return false;
        }
        if (isset($this->_channelInfo['servers']['primary']['attribs']['port'])) {
            return $this->_channelInfo['servers']['primary']['attribs']['port'];
        }
        if ($this->getSSL()) {
            return 443;
        }
        return 80;
    }

    /**
     * @return bool Determines whether secure sockets layer (SSL) is used to connect to this channel
     */
    function getSSL($mirror = false)
    {
        if ($mirror) {
            if ($mir = $this->getMirror($mirror)) {
                if (isset($mir['attribs']['ssl'])) {
                    return true;
                } else {
                    return false;
                }
            }
            return false;
        }
        if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) {
            return true;
        }
        return false;
    }

    /**
     * @return string|false
     */
    function getSummary()
    {
        if (isset($this->_channelInfo['summary'])) {
            return $this->_channelInfo['summary'];
        } else {
            return false;
        }
    }

    /**
     * @param string xmlrpc or soap
     * @param string|false mirror name or false for primary server
     */
    function getPath($protocol, $mirror = false)
    {   
        if (!in_array($protocol, array('xmlrpc', 'soap'))) {
            return false;
        }
        if ($mirror) {
            if (!($mir = $this->getMirror($mirror))) {
                return false;
            }
            if (isset($mir[$protocol]['attribs']['path'])) {
                return $mir[$protocol]['attribs']['path'];
            } else {
                return $protocol . '.php';
            }
        } elseif (isset($this->_channelInfo['servers']['primary'][$protocol]['attribs']['path'])) {
            return $this->_channelInfo['servers']['primary'][$protocol]['attribs']['path'];
        }
        return $protocol . '.php';
    }

    /**
     * @param string protocol type (xmlrpc, soap)
     * @param string Mirror name
     * @return array|false
     */
    function getFunctions($protocol, $mirror = false)
    {
        if ($this->getName() == '__uri') {
            return false;
        }
        if ($protocol == 'rest') {
            $function = 'baseurl';
        } else {
            $function = 'function';
        }
        if ($mirror) {
            if ($mir = $this->getMirror($mirror)) {
                if (isset($mir[$protocol][$function])) {
                    return $mir[$protocol][$function];
                }
            }
            return false;
        }
        if (isset($this->_channelInfo['servers']['primary'][$protocol][$function])) {
            return $this->_channelInfo['servers']['primary'][$protocol][$function];
        } else {
            return false;
        }
    }

    /**
     * @param string Protocol type
     * @param string Function name (null to return the
     *               first protocol of the type requested)
     * @param string Mirror name, if any
     * @return array
     */
     function getFunction($type, $name = null, $mirror = false)
     {
        $protocols = $this->getFunctions($type, $mirror);
        if (!$protocols) {
            return false;
        }
        foreach ($protocols as $protocol) {
            if ($name === null) {
                return $protocol;
            }
            if ($protocol['_content'] != $name) {
                continue;
            }
            return $protocol;
        }
        return false;
     }

    /**
     * @param string protocol type
     * @param string protocol name
     * @param string version
     * @param string mirror name
     * @return boolean
     */
    function supports($type, $name = null, $mirror = false, $version = '1.0')
    {
        $protocols = $this->getFunctions($type, $mirror);
        if (!$protocols) {
            return false;
        }
        foreach ($protocols as $protocol) {
            if ($protocol['attribs']['version'] != $version) {
                continue;
            }
            if ($name === null) {
                return true;
            }
            if ($protocol['_content'] != $name) {
                continue;
            }
            return true;
        }
        return false;
    }

    /**
     * Determines whether a channel supports Representational State Transfer (REST) protocols
     * for retrieving channel information
     * @param string
     * @return bool
     */
    function supportsREST($mirror = false)
    {
        if ($mirror == $this->_channelInfo['name']) {
            $mirror = false;
        }
        if ($mirror) {
            if ($mir = $this->getMirror($mirror)) {
                return isset($mir['rest']);
            }
            return false;
        }
        return isset($this->_channelInfo['servers']['primary']['rest']);
    }

    /**
     * Get the URL to access a base resource.
     *
     * Hyperlinks in the returned xml will be used to retrieve the proper information
     * needed.  This allows extreme extensibility and flexibility in implementation
     * @param string Resource Type to retrieve
     */
    function getBaseURL($resourceType, $mirror = false)
    {
        if ($mirror == $this->_channelInfo['name']) {
            $mirror = false;
        }
        if ($mirror) {
            if ($mir = $this->getMirror($mirror)) {
                $rest = $mir['rest'];
            } else {
                return false;
            }
        } else {
            $rest = $this->_channelInfo['servers']['primary']['rest'];
        }
        if (!isset($rest['baseurl'][0])) {
            $rest['baseurl'] = array($rest['baseurl']);
        }
        foreach ($rest['baseurl'] as $baseurl) {
            if (strtolower($baseurl['attribs']['type']) == strtolower($resourceType)) {
                return $baseurl['_content'];
            }
        }
        return false;
    }

    /**
     * Since REST does not implement RPC, provide this as a logical wrapper around
     * resetFunctions for REST
     * @param string|false mirror name, if any
     */
    function resetREST($mirror = false)
    {
        return $this->resetFunctions('rest', $mirror);
    }

    /**
     * Empty all protocol definitions
     * @param string protocol type (xmlrpc, soap)
     * @param string|false mirror name, if any
     */
    function resetFunctions($type, $mirror = false)
    {
        if ($mirror) {
            if (isset($this->_channelInfo['servers']['mirror'])) {
                $mirrors = $this->_channelInfo['servers']['mirror'];
                if (!isset($mirrors[0])) {
                    $mirrors = array($mirrors);
                }
                foreach ($mirrors as $i => $mir) {
                    if ($mir['attribs']['host'] == $mirror) {
                        if (isset($this->_channelInfo['servers']['mirror'][$i][$type])) {
                            unset($this->_channelInfo['servers']['mirror'][$i][$type]);
                        }
                        return true;
                    }
                }
                return false;
            } else {
                return false;
            }
        } else {
            if (isset($this->_channelInfo['servers']['primary'][$type])) {
                unset($this->_channelInfo['servers']['primary'][$type]);
            }
            return true;
        }
    }

    /**
     * Set a channel's protocols to the protocols supported by pearweb
     */
    function setDefaultPEARProtocols($version = '1.0', $mirror = false)
    {
        switch ($version) {
            case '1.0' :
                $this->resetFunctions('xmlrpc', $mirror);
                $this->resetFunctions('soap', $mirror);
                $this->resetREST($mirror);
                $this->addFunction('xmlrpc', '1.0', 'logintest', $mirror);
                $this->addFunction('xmlrpc', '1.0', 'package.listLatestReleases', $mirror);
                $this->addFunction('xmlrpc', '1.0', 'package.listAll', $mirror);
                $this->addFunction('xmlrpc', '1.0', 'package.info', $mirror);
                $this->addFunction('xmlrpc', '1.0', 'package.getDownloadURL', $mirror);
                $this->addFunction('xmlrpc', '1.1', 'package.getDownloadURL', $mirror);
                $this->addFunction('xmlrpc', '1.0', 'package.getDepDownloadURL', $mirror);
                $this->addFunction('xmlrpc', '1.1', 'package.getDepDownloadURL', $mirror);
                $this->addFunction('xmlrpc', '1.0', 'package.search', $mirror);
                $this->addFunction('xmlrpc', '1.0', 'channel.listAll', $mirror);
                return true;
            break;
            default :
                return false;
            break;
        }
    }
    
    /**
     * @return array
     */
    function getMirrors()
    {
        if (isset($this->_channelInfo['servers']['mirror'])) {
            $mirrors = $this->_channelInfo['servers']['mirror'];
            if (!isset($mirrors[0])) {
                $mirrors = array($mirrors);
            }
            return $mirrors;
        } else {
            return array();
        }
    }

    /**
     * Get the unserialized XML representing a mirror
     * @return array|false
     */
    function getMirror($server)
    {
        foreach ($this->getMirrors() as $mirror) {
            if ($mirror['attribs']['host'] == $server) {
                return $mirror;
            }
        }
        return false;
    }

    /**
     * @param string
     * @return string|false
     * @error PEAR_CHANNELFILE_ERROR_NO_NAME
     * @error PEAR_CHANNELFILE_ERROR_INVALID_NAME
     */
    function setName($name)
    {
        return $this->setServer($name);
    }

    /**
     * Set the socket number (port) that is used to connect to this channel
     * @param integer
     * @param string|false name of the mirror server, or false for the primary
     */
    function setPort($port, $mirror = false)
    {
        if ($mirror) {
            if (!isset($this->_channelInfo['servers']['mirror'])) {
                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
                    array('mirror' => $mirror));
                return false;
            }
            if (isset($this->_channelInfo['servers']['mirror'][0])) {
                foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
                    if ($mirror == $mir['attribs']['host']) {
                        $this->_channelInfo['servers']['mirror'][$i]['attribs']['port'] = $port;
                        return true;
                    }
                }
                return false;
            } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
                $this->_channelInfo['servers']['mirror']['attribs']['port'] = $port;
                $this->_isValid = false;
                return true;
            }
        }
        $this->_channelInfo['servers']['primary']['attribs']['port'] = $port;
        $this->_isValid = false;
        return true;
    }

    /**
     * Set the socket number (port) that is used to connect to this channel
     * @param bool Determines whether to turn on SSL support or turn it off
     * @param string|false name of the mirror server, or false for the primary
     */
    function setSSL($ssl = true, $mirror = false)
    {
        if ($mirror) {
            if (!isset($this->_channelInfo['servers']['mirror'])) {
                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
                    array('mirror' => $mirror));
                return false;
            }
            if (isset($this->_channelInfo['servers']['mirror'][0])) {
                foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
                    if ($mirror == $mir['attribs']['host']) {
                        if (!$ssl) {
                            if (isset($this->_channelInfo['servers']['mirror'][$i]
                                  ['attribs']['ssl'])) {
                                unset($this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl']);
                            }
                        } else {
                            $this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl'] = 'yes';
                        }
                        return true;
                    }
                }
                return false;
            } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
                if (!$ssl) {
                    if (isset($this->_channelInfo['servers']['mirror']['attribs']['ssl'])) {
                        unset($this->_channelInfo['servers']['mirror']['attribs']['ssl']);
                    }
                } else {
                    $this->_channelInfo['servers']['mirror']['attribs']['ssl'] = 'yes';
                }
                $this->_isValid = false;
                return true;
            }
        }
        if ($ssl) {
            $this->_channelInfo['servers']['primary']['attribs']['ssl'] = 'yes';
        } else {
            if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) {
                unset($this->_channelInfo['servers']['primary']['attribs']['ssl']);
            }
        }
        $this->_isValid = false;
        return true;
    }

    /**
     * Set the socket number (port) that is used to connect to this channel
     * @param integer
     * @param string|false name of the mirror server, or false for the primary
     */
    function setPath($protocol, $path, $mirror = false)
    {
        if (!in_array($protocol, array('xmlrpc', 'soap'))) {
            return false;
        }
        if ($mirror) {
            if (!isset($this->_channelInfo['servers']['mirror'])) {
                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
                    array('mirror' => $mirror));
                return false;
            }
            if (isset($this->_channelInfo['servers']['mirror'][0])) {
                foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
                    if ($mirror == $mir['attribs']['host']) {
                        $this->_channelInfo['servers']['mirror'][$i][$protocol]['attribs']['path'] =
                            $path;
                        return true;
                    }
                }
                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
                    array('mirror' => $mirror));
                return false;
            } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
                $this->_channelInfo['servers']['mirror'][$protocol]['attribs']['path'] = $path;
                $this->_isValid = false;
                return true;
            }
        }
        $this->_channelInfo['servers']['primary'][$protocol]['attribs']['path'] = $path;
        $this->_isValid = false;
        return true;
    }

    /**
     * @param string
     * @return string|false
     * @error PEAR_CHANNELFILE_ERROR_NO_SERVER
     * @error PEAR_CHANNELFILE_ERROR_INVALID_SERVER
     */
    function setServer($server, $mirror = false)
    {
        if (empty($server)) {
            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SERVER);
            return false;
        } elseif (!$this->validChannelServer($server)) {
            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
                array('tag' => 'name', 'name' => $server));
            return false;
        }
        if ($mirror) {
            $found = false;
            foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
                if ($mirror == $mir['attribs']['host']) {
                    $found = true;
                    break;
                }
            }
            if (!$found) {
                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
                    array('mirror' => $mirror));
                return false;
            }
            $this->_channelInfo['mirror'][$i]['attribs']['host'] = $server;
            return true;
        }
        $this->_channelInfo['name'] = $server;
        return true;
    }

    /**
     * @param string
     * @return boolean success
     * @error PEAR_CHANNELFILE_ERROR_NO_SUMMARY
     * @warning PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY
     */
    function setSummary($summary)
    {
        if (empty($summary)) {
            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY);
            return false;
        } elseif (strpos(trim($summary), "\n") !== false) {
            $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY,
                array('summary' => $summary));
        }
        $this->_channelInfo['summary'] = $summary;
        return true;
    }

    /**
     * @param string
     * @param boolean determines whether the alias is in channel.xml or local
     * @return boolean success
     */
    function setAlias($alias, $local = false)
    {
        if (!$this->validChannelServer($alias)) {
            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
                array('tag' => 'suggestedalias', 'name' => $alias));
            return false;
        }
        if ($local) {
            $this->_channelInfo['localalias'] = $alias;
        } else {
            $this->_channelInfo['suggestedalias'] = $alias;
        }
        return true;
    }

    /**
     * @return string
     */
    function getAlias()
    {
        if (isset($this->_channelInfo['localalias'])) {
            return $this->_channelInfo['localalias'];
        }
        if (isset($this->_channelInfo['suggestedalias'])) {
            return $this->_channelInfo['suggestedalias'];
        }
        if (isset($this->_channelInfo['name'])) {
            return $this->_channelInfo['name'];
        }
        return '';
    }

    /**
     * Set the package validation object if it differs from PEAR's default
     * The class must be includeable via changing _ in the classname to path separator,
     * but no checking of this is made.
     * @param string|false pass in false to reset to the default packagename regex
     * @return boolean success
     */
    function setValidationPackage($validateclass, $version)
    {
        if (empty($validateclass)) {
            unset($this->_channelInfo['validatepackage']);
        }
        $this->_channelInfo['validatepackage'] = array('_content' => $validateclass);
        $this->_channelInfo['validatepackage']['attribs'] = array('version' => $version);
    }

    /**
     * Add a protocol to the provides section
     * @param string protocol type
     * @param string protocol version
     * @param string protocol name, if any
     * @param string mirror name, if this is a mirror's protocol
     * @return bool
     */
    function addFunction($type, $version, $name = '', $mirror = false)
    {
        if ($mirror) {
            return $this->addMirrorFunction($mirror, $type, $version, $name);
        }
        $set = array('attribs' => array('version' => $version), '_content' => $name);
        if (!isset($this->_channelInfo['servers']['primary'][$type]['function'])) {
            if (!isset($this->_channelInfo['servers'])) {
                $this->_channelInfo['servers'] = array('primary' =>
                    array($type => array()));
            } elseif (!isset($this->_channelInfo['servers']['primary'])) {
                $this->_channelInfo['servers']['primary'] = array($type => array());
            }
            $this->_channelInfo['servers']['primary'][$type]['function'] = $set;
            $this->_isValid = false;
            return true;
        } elseif (!isset($this->_channelInfo['servers']['primary'][$type]['function'][0])) {
            $this->_channelInfo['servers']['primary'][$type]['function'] = array(
                $this->_channelInfo['servers']['primary'][$type]['function']);
        }
        $this->_channelInfo['servers']['primary'][$type]['function'][] = $set;
        return true;
    }
    /**
     * Add a protocol to a mirror's provides section
     * @param string mirror name (server)
     * @param string protocol type
     * @param string protocol version
     * @param string protocol name, if any
     */
    function addMirrorFunction($mirror, $type, $version, $name = '')
    {
        if (!isset($this->_channelInfo['servers']['mirror'])) {
            $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
                array('mirror' => $mirror));
            return false;
        }
        $setmirror = false;
        if (isset($this->_channelInfo['servers']['mirror'][0])) {
            foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
                if ($mirror == $mir['attribs']['host']) {
                    $setmirror = &$this->_channelInfo['servers']['mirror'][$i];
                    break;
                }
            }
        } else {
            if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
                $setmirror = &$this->_channelInfo['servers']['mirror'];
            }
        }
        if (!$setmirror) {
            $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
                array('mirror' => $mirror));
            return false;
        }
        $set = array('attribs' => array('version' => $version), '_content' => $name);
        if (!isset($setmirror[$type]['function'])) {
            $setmirror[$type]['function'] = $set;
            $this->_isValid = false;
            return true;
        } elseif (!isset($setmirror[$type]['function'][0])) {
            $setmirror[$type]['function'] = array($setmirror[$type]['function']);
        }
        $setmirror[$type]['function'][] = $set;
        $this->_isValid = false;
        return true;
    }

    /**
     * @param string Resource Type this url links to
     * @param string URL
     * @param string|false mirror name, if this is not a primary server REST base URL
     */
    function setBaseURL($resourceType, $url, $mirror = false)
    {
        if ($mirror) {
            if (!isset($this->_channelInfo['servers']['mirror'])) {
                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
                    array('mirror' => $mirror));
                return false;
            }
            $setmirror = false;
            if (isset($this->_channelInfo['servers']['mirror'][0])) {
                foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
                    if ($mirror == $mir['attribs']['host']) {
                        $setmirror = &$this->_channelInfo['servers']['mirror'][$i];
                        break;
                    }
                }
            } else {
                if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
                    $setmirror = &$this->_channelInfo['servers']['mirror'];
                }
            }
        } else {
            $setmirror = &$this->_channelInfo['servers']['primary'];
        }
        $set = array('attribs' => array('type' => $resourceType), '_content' => $url);
        if (!isset($setmirror['rest'])) {
            $setmirror['rest'] = array();
        }
        if (!isset($setmirror['rest']['baseurl'])) {
            $setmirror['rest']['baseurl'] = $set;
            $this->_isValid = false;
            return true;
        } elseif (!isset($setmirror['rest']['baseurl'][0])) {
            $setmirror['rest']['baseurl'] = array($setmirror['rest']['baseurl']);
        }
        foreach ($setmirror['rest']['baseurl'] as $i => $url) {
            if ($url['attribs']['type'] == $resourceType) {
                $this->_isValid = false;
                $setmirror['rest']['baseurl'][$i] = $set;
                return true;
            }
        }
        $setmirror['rest']['baseurl'][] = $set;
        $this->_isValid = false;
        return true;
    }

    /**
     * @param string mirror server
     * @param int mirror http port
     * @return boolean
     */
    function addMirror($server, $port = null)
    {
        if ($this->_channelInfo['name'] == '__uri') {
            return false; // the __uri channel cannot have mirrors by definition
        }
        $set = array('attribs' => array('host' => $server));
        if (is_numeric($port)) {
            $set['attribs']['port'] = $port;
        }
        if (!isset($this->_channelInfo['servers']['mirror'])) {
            $this->_channelInfo['servers']['mirror'] = $set;
            return true;
        } else {
            if (!isset($this->_channelInfo['servers']['mirror'][0])) {
                $this->_channelInfo['servers']['mirror'] =
                    array($this->_channelInfo['servers']['mirror']);
            }
        }
        $this->_channelInfo['servers']['mirror'][] = $set;
        return true;
    }

    /**
     * Retrieve the name of the validation package for this channel
     * @return string|false
     */
    function getValidationPackage()
    {
        if (!$this->_isValid && !$this->validate()) {
            return false;
        }
        if (!isset($this->_channelInfo['validatepackage'])) {
            return array('attribs' => array('version' => 'default'),
                '_content' => 'PEAR_Validate');
        }
        return $this->_channelInfo['validatepackage'];
    }

    /**
     * Retrieve the object that can be used for custom validation
     * @param string|false the name of the package to validate.  If the package is
     *                     the channel validation package, PEAR_Validate is returned
     * @return PEAR_Validate|false false is returned if the validation package
     *         cannot be located
     */
    function &getValidationObject($package = false)
    {
        if (!class_exists('PEAR_Validate')) {
            require_once 'PEAR/Validate.php';
        }
        if (!$this->_isValid) {
            if (!$this->validate()) {
                $a = false;
                return $a;
            }
        }
        if (isset($this->_channelInfo['validatepackage'])) {
            if ($package == $this->_channelInfo['validatepackage']) {
                // channel validation packages are always validated by PEAR_Validate
                $val = &new PEAR_Validate;
                return $val;
            }
            if (!class_exists(str_replace('.', '_',
                  $this->_channelInfo['validatepackage']['_content']))) {
                if ($this->isIncludeable(str_replace('_', '/',
                      $this->_channelInfo['validatepackage']['_content']) . '.php')) {
                    include_once str_replace('_', '/',
                        $this->_channelInfo['validatepackage']['_content']) . '.php';
                    $vclass = str_replace('.', '_',
                        $this->_channelInfo['validatepackage']['_content']);
                    $val = &new $vclass;
                } else {
                    $a = false;
                    return $a;
                }
            } else {
                $vclass = str_replace('.', '_',
                    $this->_channelInfo['validatepackage']['_content']);
                $val = &new $vclass;
            }
        } else {
            $val = &new PEAR_Validate;
        }
        return $val;
    }

    function isIncludeable($path)
    {
        $possibilities = explode(PATH_SEPARATOR, ini_get('include_path'));
        foreach ($possibilities as $dir) {
            if (file_exists($dir . DIRECTORY_SEPARATOR . $path)
                  && is_readable($dir . DIRECTORY_SEPARATOR . $path)) {
                return true;
            }
        }
        return false;
    }

    /**
     * This function is used by the channel updater and retrieves a value set by
     * the registry, or the current time if it has not been set
     * @return string
     */
    function lastModified()
    {
        if (isset($this->_channelInfo['_lastmodified'])) {
            return $this->_channelInfo['_lastmodified'];
        }
        return time();
    }
}
?>
PEAR-1.7.2/PEAR/Command.php100644   1750   1750       31447 11014312742  10334 <?php
/**
 * PEAR_Command, command pattern class
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Command.php,v 1.39 2008/01/03 20:26:34 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * Needed for error handling
 */
require_once 'PEAR.php';
require_once 'PEAR/Frontend.php';
require_once 'PEAR/XMLParser.php';

/**
 * List of commands and what classes they are implemented in.
 * @var array command => implementing class
 */
$GLOBALS['_PEAR_Command_commandlist'] = array();

/**
 * List of commands and their descriptions
 * @var array command => description
 */
$GLOBALS['_PEAR_Command_commanddesc'] = array();

/**
 * List of shortcuts to common commands.
 * @var array shortcut => command
 */
$GLOBALS['_PEAR_Command_shortcuts'] = array();

/**
 * Array of command objects
 * @var array class => object
 */
$GLOBALS['_PEAR_Command_objects'] = array();

/**
 * PEAR command class, a simple factory class for administrative
 * commands.
 *
 * How to implement command classes:
 *
 * - The class must be called PEAR_Command_Nnn, installed in the
 *   "PEAR/Common" subdir, with a method called getCommands() that
 *   returns an array of the commands implemented by the class (see
 *   PEAR/Command/Install.php for an example).
 *
 * - The class must implement a run() function that is called with three
 *   params:
 *
 *    (string) command name
 *    (array)  assoc array with options, freely defined by each
 *             command, for example:
 *             array('force' => true)
 *    (array)  list of the other parameters
 *
 *   The run() function returns a PEAR_CommandResponse object.  Use
 *   these methods to get information:
 *
 *    int getStatus()   Returns PEAR_COMMAND_(SUCCESS|FAILURE|PARTIAL)
 *                      *_PARTIAL means that you need to issue at least
 *                      one more command to complete the operation
 *                      (used for example for validation steps).
 *
 *    string getMessage()  Returns a message for the user.  Remember,
 *                         no HTML or other interface-specific markup.
 *
 *   If something unexpected happens, run() returns a PEAR error.
 *
 * - DON'T OUTPUT ANYTHING! Return text for output instead.
 *
 * - DON'T USE HTML! The text you return will be used from both Gtk,
 *   web and command-line interfaces, so for now, keep everything to
 *   plain text.
 *
 * - DON'T USE EXIT OR DIE! Always use pear errors.  From static
 *   classes do PEAR::raiseError(), from other classes do
 *   $this->raiseError().
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */
class PEAR_Command
{
    // {{{ factory()

    /**
     * Get the right object for executing a command.
     *
     * @param string $command The name of the command
     * @param object $config  Instance of PEAR_Config object
     *
     * @return object the command object or a PEAR error
     *
     * @access public
     * @static
     */
    function &factory($command, &$config)
    {
        if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
            PEAR_Command::registerCommands();
        }
        if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
            $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
        }
        if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
            $a = PEAR::raiseError("unknown command `$command'");
            return $a;
        }
        $class = $GLOBALS['_PEAR_Command_commandlist'][$command];
        if (!class_exists($class)) {
            require_once $GLOBALS['_PEAR_Command_objects'][$class];
        }
        if (!class_exists($class)) {
            $a = PEAR::raiseError("unknown command `$command'");
            return $a;
        }
        $ui =& PEAR_Command::getFrontendObject();
        $obj = &new $class($ui, $config);
        return $obj;
    }

    // }}}
    // {{{ & getObject()
    function &getObject($command)
    {
        $class = $GLOBALS['_PEAR_Command_commandlist'][$command];
        if (!class_exists($class)) {
            require_once $GLOBALS['_PEAR_Command_objects'][$class];
        }
        if (!class_exists($class)) {
            return PEAR::raiseError("unknown command `$command'");
        }
        $ui =& PEAR_Command::getFrontendObject();
        $config = &PEAR_Config::singleton();
        $obj = &new $class($ui, $config);
        return $obj;
    }

    // }}}
    // {{{ & getFrontendObject()

    /**
     * Get instance of frontend object.
     *
     * @return object|PEAR_Error
     * @static
     */
    function &getFrontendObject()
    {
        $a = &PEAR_Frontend::singleton();
        return $a;
    }

    // }}}
    // {{{ & setFrontendClass()

    /**
     * Load current frontend class.
     *
     * @param string $uiclass Name of class implementing the frontend
     *
     * @return object the frontend object, or a PEAR error
     * @static
     */
    function &setFrontendClass($uiclass)
    {
        $a = &PEAR_Frontend::setFrontendClass($uiclass);
        return $a;
    }

    // }}}
    // {{{ setFrontendType()

    /**
     * Set current frontend.
     *
     * @param string $uitype Name of the frontend type (for example "CLI")
     *
     * @return object the frontend object, or a PEAR error
     * @static
     */
    function setFrontendType($uitype)
    {
        $uiclass = 'PEAR_Frontend_' . $uitype;
        return PEAR_Command::setFrontendClass($uiclass);
    }

    // }}}
    // {{{ registerCommands()

    /**
     * Scan through the Command directory looking for classes
     * and see what commands they implement.
     *
     * @param bool   (optional) if FALSE (default), the new list of
     *               commands should replace the current one.  If TRUE,
     *               new entries will be merged with old.
     *
     * @param string (optional) where (what directory) to look for
     *               classes, defaults to the Command subdirectory of
     *               the directory from where this file (__FILE__) is
     *               included.
     *
     * @return bool TRUE on success, a PEAR error on failure
     *
     * @access public
     * @static
     */
    function registerCommands($merge = false, $dir = null)
    {
        $parser = new PEAR_XMLParser;
        if ($dir === null) {
            $dir = dirname(__FILE__) . '/Command';
        }
        if (!is_dir($dir)) {
            return PEAR::raiseError("registerCommands: opendir($dir) '$dir' does not exist or is not a directory");
        }
        $dp = @opendir($dir);
        if (empty($dp)) {
            return PEAR::raiseError("registerCommands: opendir($dir) failed");
        }
        if (!$merge) {
            $GLOBALS['_PEAR_Command_commandlist'] = array();
        }
        while ($entry = readdir($dp)) {
            if ($entry{0} == '.' || substr($entry, -4) != '.xml') {
                continue;
            }
            $class = "PEAR_Command_".substr($entry, 0, -4);
            $file = "$dir/$entry";
            $parser->parse(file_get_contents($file));
            $implements = $parser->getData();
            // List of commands
            if (empty($GLOBALS['_PEAR_Command_objects'][$class])) {
                $GLOBALS['_PEAR_Command_objects'][$class] = "$dir/" . substr($entry, 0, -4) .
                    '.php';
            }
            foreach ($implements as $command => $desc) {
                if ($command == 'attribs') {
                    continue;
                }
                if (isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
                    return PEAR::raiseError('Command "' . $command . '" already registered in ' .
                        'class "' . $GLOBALS['_PEAR_Command_commandlist'][$command] . '"');
                }
                $GLOBALS['_PEAR_Command_commandlist'][$command] = $class;
                $GLOBALS['_PEAR_Command_commanddesc'][$command] = $desc['summary'];
                if (isset($desc['shortcut'])) {
                    $shortcut = $desc['shortcut'];
                    if (isset($GLOBALS['_PEAR_Command_shortcuts'][$shortcut])) {
                        return PEAR::raiseError('Command shortcut "' . $shortcut . '" already ' .
                            'registered to command "' . $command . '" in class "' .
                            $GLOBALS['_PEAR_Command_commandlist'][$command] . '"');
                    }
                    $GLOBALS['_PEAR_Command_shortcuts'][$shortcut] = $command;
                }
                if (isset($desc['options']) && $desc['options']) {
                    foreach ($desc['options'] as $oname => $option) {
                        if (isset($option['shortopt']) && strlen($option['shortopt']) > 1) {
                            return PEAR::raiseError('Option "' . $oname . '" short option "' .
                                $option['shortopt'] . '" must be ' .
                                'only 1 character in Command "' . $command . '" in class "' .
                                $class . '"');
                        }
                    }
                }
            }
        }
        ksort($GLOBALS['_PEAR_Command_shortcuts']);
        ksort($GLOBALS['_PEAR_Command_commandlist']);
        @closedir($dp);
        return true;
    }

    // }}}
    // {{{ getCommands()

    /**
     * Get the list of currently supported commands, and what
     * classes implement them.
     *
     * @return array command => implementing class
     *
     * @access public
     * @static
     */
    function getCommands()
    {
        if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
            PEAR_Command::registerCommands();
        }
        return $GLOBALS['_PEAR_Command_commandlist'];
    }

    // }}}
    // {{{ getShortcuts()

    /**
     * Get the list of command shortcuts.
     *
     * @return array shortcut => command
     *
     * @access public
     * @static
     */
    function getShortcuts()
    {
        if (empty($GLOBALS['_PEAR_Command_shortcuts'])) {
            PEAR_Command::registerCommands();
        }
        return $GLOBALS['_PEAR_Command_shortcuts'];
    }

    // }}}
    // {{{ getGetoptArgs()

    /**
     * Compiles arguments for getopt.
     *
     * @param string $command     command to get optstring for
     * @param string $short_args  (reference) short getopt format
     * @param array  $long_args   (reference) long getopt format
     *
     * @return void
     *
     * @access public
     * @static
     */
    function getGetoptArgs($command, &$short_args, &$long_args)
    {
        if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
            PEAR_Command::registerCommands();
        }
        if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
            $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
        }
        if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
            return null;
        }
        $obj = &PEAR_Command::getObject($command);
        return $obj->getGetoptArgs($command, $short_args, $long_args);
    }

    // }}}
    // {{{ getDescription()

    /**
     * Get description for a command.
     *
     * @param  string $command Name of the command
     *
     * @return string command description
     *
     * @access public
     * @static
     */
    function getDescription($command)
    {
        if (!isset($GLOBALS['_PEAR_Command_commanddesc'][$command])) {
            return null;
        }
        return $GLOBALS['_PEAR_Command_commanddesc'][$command];
    }

    // }}}
    // {{{ getHelp()

    /**
     * Get help for command.
     *
     * @param string $command Name of the command to return help for
     *
     * @access public
     * @static
     */
    function getHelp($command)
    {
        $cmds = PEAR_Command::getCommands();
        if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
            $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
        }
        if (isset($cmds[$command])) {
            $obj = &PEAR_Command::getObject($command);
            return $obj->getHelp($command);
        }
        return false;
    }
    // }}}
}

?>
PEAR-1.7.2/PEAR/Common.php100644   1750   1750      105476 11014312742  10232 <?php
/**
 * PEAR_Common, the base class for the PEAR Installer
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V. V. Cox <cox@idecnet.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Common.php,v 1.160 2008/01/03 20:26:34 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1.0
 * @deprecated File deprecated since Release 1.4.0a1
 */

/**
 * Include error handling
 */
require_once 'PEAR.php';

// {{{ constants and globals

/**
 * PEAR_Common error when an invalid PHP file is passed to PEAR_Common::analyzeSourceCode()
 */
define('PEAR_COMMON_ERROR_INVALIDPHP', 1);
define('_PEAR_COMMON_PACKAGE_NAME_PREG', '[A-Za-z][a-zA-Z0-9_]+');
define('PEAR_COMMON_PACKAGE_NAME_PREG', '/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '\\z/');

// this should allow: 1, 1.0, 1.0RC1, 1.0dev, 1.0dev123234234234, 1.0a1, 1.0b1, 1.0pl1
define('_PEAR_COMMON_PACKAGE_VERSION_PREG', '\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?');
define('PEAR_COMMON_PACKAGE_VERSION_PREG', '/^' . _PEAR_COMMON_PACKAGE_VERSION_PREG . '\\z/i');

// XXX far from perfect :-)
define('_PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '(' . _PEAR_COMMON_PACKAGE_NAME_PREG .
    ')(-([.0-9a-zA-Z]+))?');
define('PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_PACKAGE_DOWNLOAD_PREG .
    '\\z/');

define('_PEAR_CHANNELS_NAME_PREG', '[A-Za-z][a-zA-Z0-9\.]+');
define('PEAR_CHANNELS_NAME_PREG', '/^' . _PEAR_CHANNELS_NAME_PREG . '\\z/');

// this should allow any dns or IP address, plus a path - NO UNDERSCORES ALLOWED
define('_PEAR_CHANNELS_SERVER_PREG', '[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*(\/[a-zA-Z0-9\-]+)*');
define('PEAR_CHANNELS_SERVER_PREG', '/^' . _PEAR_CHANNELS_SERVER_PREG . '\\z/i');

define('_PEAR_CHANNELS_PACKAGE_PREG',  '(' ._PEAR_CHANNELS_SERVER_PREG . ')\/('
         . _PEAR_COMMON_PACKAGE_NAME_PREG . ')');
define('PEAR_CHANNELS_PACKAGE_PREG', '/^' . _PEAR_CHANNELS_PACKAGE_PREG . '\\z/i');

define('_PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '(' . _PEAR_CHANNELS_NAME_PREG . ')::('
    . _PEAR_COMMON_PACKAGE_NAME_PREG . ')(-([.0-9a-zA-Z]+))?');
define('PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_CHANNEL_DOWNLOAD_PREG . '\\z/');

/**
 * List of temporary files and directories registered by
 * PEAR_Common::addTempFile().
 * @var array
 */
$GLOBALS['_PEAR_Common_tempfiles'] = array();

/**
 * Valid maintainer roles
 * @var array
 */
$GLOBALS['_PEAR_Common_maintainer_roles'] = array('lead','developer','contributor','helper');

/**
 * Valid release states
 * @var array
 */
$GLOBALS['_PEAR_Common_release_states'] = array('alpha','beta','stable','snapshot','devel');

/**
 * Valid dependency types
 * @var array
 */
$GLOBALS['_PEAR_Common_dependency_types'] = array('pkg','ext','php','prog','ldlib','rtlib','os','websrv','sapi');

/**
 * Valid dependency relations
 * @var array
 */
$GLOBALS['_PEAR_Common_dependency_relations'] = array('has','eq','lt','le','gt','ge','not', 'ne');

/**
 * Valid file roles
 * @var array
 */
$GLOBALS['_PEAR_Common_file_roles'] = array('php','ext','test','doc','data','src','script');

/**
 * Valid replacement types
 * @var array
 */
$GLOBALS['_PEAR_Common_replacement_types'] = array('php-const', 'pear-config', 'package-info');

/**
 * Valid "provide" types
 * @var array
 */
$GLOBALS['_PEAR_Common_provide_types'] = array('ext', 'prog', 'class', 'function', 'feature', 'api');

/**
 * Valid "provide" types
 * @var array
 */
$GLOBALS['_PEAR_Common_script_phases'] = array('pre-install', 'post-install', 'pre-uninstall', 'post-uninstall', 'pre-build', 'post-build', 'pre-configure', 'post-configure', 'pre-setup', 'post-setup');

// }}}

/**
 * Class providing common functionality for PEAR administration classes.
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V. V. Cox <cox@idecnet.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 * @deprecated This class will disappear, and its components will be spread
 *             into smaller classes, like the AT&T breakup, as of Release 1.4.0a1
 */
class PEAR_Common extends PEAR
{
    // {{{ properties

    /** stack of elements, gives some sort of XML context */
    var $element_stack = array();

    /** name of currently parsed XML element */
    var $current_element;

    /** array of attributes of the currently parsed XML element */
    var $current_attributes = array();

    /** assoc with information about a package */
    var $pkginfo = array();

    /**
     * User Interface object (PEAR_Frontend_* class).  If null,
     * the log() method uses print.
     * @var object
     */
    var $ui = null;

    /**
     * Configuration object (PEAR_Config).
     * @var PEAR_Config
     */
    var $config = null;

    var $current_path = null;

    /**
     * PEAR_SourceAnalyzer instance
     * @var object
     */
    var $source_analyzer = null;
    /**
     * Flag variable used to mark a valid package file
     * @var boolean
     * @access private
     */
    var $_validPackageFile;

    // }}}

    // {{{ constructor

    /**
     * PEAR_Common constructor
     *
     * @access public
     */
    function PEAR_Common()
    {
        parent::PEAR();
        $this->config = &PEAR_Config::singleton();
        $this->debug = $this->config->get('verbose');
    }

    // }}}
    // {{{ destructor

    /**
     * PEAR_Common destructor
     *
     * @access private
     */
    function _PEAR_Common()
    {
        // doesn't work due to bug #14744
        //$tempfiles = $this->_tempfiles;
        $tempfiles =& $GLOBALS['_PEAR_Common_tempfiles'];
        while ($file = array_shift($tempfiles)) {
            if (@is_dir($file)) {
                if (!class_exists('System')) {
                    require_once 'System.php';
                }
                System::rm(array('-rf', $file));
            } elseif (file_exists($file)) {
                unlink($file);
            }
        }
    }

    // }}}
    // {{{ addTempFile()

    /**
     * Register a temporary file or directory.  When the destructor is
     * executed, all registered temporary files and directories are
     * removed.
     *
     * @param string  $file  name of file or directory
     *
     * @return void
     *
     * @access public
     */
    function addTempFile($file)
    {
        if (!class_exists('PEAR_Frontend')) {
            require_once 'PEAR/Frontend.php';
        }
        PEAR_Frontend::addTempFile($file);
    }

    // }}}
    // {{{ mkDirHier()

    /**
     * Wrapper to System::mkDir(), creates a directory as well as
     * any necessary parent directories.
     *
     * @param string  $dir  directory name
     *
     * @return bool TRUE on success, or a PEAR error
     *
     * @access public
     */
    function mkDirHier($dir)
    {
        $this->log(2, "+ create dir $dir");
        if (!class_exists('System')) {
            require_once 'System.php';
        }
        return System::mkDir(array('-p', $dir));
    }

    // }}}
    // {{{ log()

    /**
     * Logging method.
     *
     * @param int    $level  log level (0 is quiet, higher is noisier)
     * @param string $msg    message to write to the log
     *
     * @return void
     *
     * @access public
     * @static
     */
    function log($level, $msg, $append_crlf = true)
    {
        if ($this->debug >= $level) {
            if (!class_exists('PEAR_Frontend')) {
                require_once 'PEAR/Frontend.php';
            }
            $ui = &PEAR_Frontend::singleton();
            if (is_a($ui, 'PEAR_Frontend')) {
                $ui->log($msg, $append_crlf);
            } else {
                print "$msg\n";
            }
        }
    }

    // }}}
    // {{{ mkTempDir()

    /**
     * Create and register a temporary directory.
     *
     * @param string $tmpdir (optional) Directory to use as tmpdir.
     *                       Will use system defaults (for example
     *                       /tmp or c:\windows\temp) if not specified
     *
     * @return string name of created directory
     *
     * @access public
     */
    function mkTempDir($tmpdir = '')
    {
        if ($tmpdir) {
            $topt = array('-t', $tmpdir);
        } else {
            $topt = array();
        }
        $topt = array_merge($topt, array('-d', 'pear'));
        if (!class_exists('System')) {
            require_once 'System.php';
        }
        if (!$tmpdir = System::mktemp($topt)) {
            return false;
        }
        $this->addTempFile($tmpdir);
        return $tmpdir;
    }

    // }}}
    // {{{ setFrontendObject()

    /**
     * Set object that represents the frontend to be used.
     *
     * @param  object Reference of the frontend object
     * @return void
     * @access public
     */
    function setFrontendObject(&$ui)
    {
        $this->ui = &$ui;
    }

    // }}}

    // {{{ infoFromTgzFile()

    /**
     * Returns information about a package file.  Expects the name of
     * a gzipped tar file as input.
     *
     * @param string  $file  name of .tgz file
     *
     * @return array  array with package information
     *
     * @access public
     * @deprecated use PEAR_PackageFile->fromTgzFile() instead
     *
     */
    function infoFromTgzFile($file)
    {
        $packagefile = &new PEAR_PackageFile($this->config);
        $pf = &$packagefile->fromTgzFile($file, PEAR_VALIDATE_NORMAL);
        if (PEAR::isError($pf)) {
            $errs = $pf->getUserinfo();
            if (is_array($errs)) {
                foreach ($errs as $error) {
                    $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
                }
            }
            return $pf;
        }
        return $this->_postProcessValidPackagexml($pf);
    }

    // }}}
    // {{{ infoFromDescriptionFile()

    /**
     * Returns information about a package file.  Expects the name of
     * a package xml file as input.
     *
     * @param string  $descfile  name of package xml file
     *
     * @return array  array with package information
     *
     * @access public
     * @deprecated use PEAR_PackageFile->fromPackageFile() instead
     *
     */
    function infoFromDescriptionFile($descfile)
    {
        $packagefile = &new PEAR_PackageFile($this->config);
        $pf = &$packagefile->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
        if (PEAR::isError($pf)) {
            $errs = $pf->getUserinfo();
            if (is_array($errs)) {
                foreach ($errs as $error) {
                    $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
                }
            }
            return $pf;
        }
        return $this->_postProcessValidPackagexml($pf);
    }

    // }}}
    // {{{ infoFromString()

    /**
     * Returns information about a package file.  Expects the contents
     * of a package xml file as input.
     *
     * @param string  $data  contents of package.xml file
     *
     * @return array   array with package information
     *
     * @access public
     * @deprecated use PEAR_PackageFile->fromXmlstring() instead
     *
     */
    function infoFromString($data)
    {
        $packagefile = &new PEAR_PackageFile($this->config);
        $pf = &$packagefile->fromXmlString($data, PEAR_VALIDATE_NORMAL, false);
        if (PEAR::isError($pf)) {
            $errs = $pf->getUserinfo();
            if (is_array($errs)) {
                foreach ($errs as $error) {
                    $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
                }
            }
            return $pf;
        }
        return $this->_postProcessValidPackagexml($pf);
    }
    // }}}

    /**
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @return array
     */
    function _postProcessValidPackagexml(&$pf)
    {
        if (is_a($pf, 'PEAR_PackageFile_v2')) {
            // sort of make this into a package.xml 1.0-style array
            // changelog is not converted to old format.
            $arr = $pf->toArray(true);
            $arr = array_merge($arr, $arr['old']);
            unset($arr['old']);
            unset($arr['xsdversion']);
            unset($arr['contents']);
            unset($arr['compatible']);
            unset($arr['channel']);
            unset($arr['uri']);
            unset($arr['dependencies']);
            unset($arr['phprelease']);
            unset($arr['extsrcrelease']);
            unset($arr['zendextsrcrelease']);
            unset($arr['extbinrelease']);
            unset($arr['zendextbinrelease']);
            unset($arr['bundle']);
            unset($arr['lead']);
            unset($arr['developer']);
            unset($arr['helper']);
            unset($arr['contributor']);
            $arr['filelist'] = $pf->getFilelist();
            $this->pkginfo = $arr;
            return $arr;
        } else {
            $this->pkginfo = $pf->toArray();
            return $this->pkginfo;
        }
    }
    // {{{ infoFromAny()

    /**
     * Returns package information from different sources
     *
     * This method is able to extract information about a package
     * from a .tgz archive or from a XML package definition file.
     *
     * @access public
     * @param  string Filename of the source ('package.xml', '<package>.tgz')
     * @return string
     * @deprecated use PEAR_PackageFile->fromAnyFile() instead
     */
    function infoFromAny($info)
    {
        if (is_string($info) && file_exists($info)) {
            $packagefile = &new PEAR_PackageFile($this->config);
            $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL);
            if (PEAR::isError($pf)) {
                $errs = $pf->getUserinfo();
                if (is_array($errs)) {
                    foreach ($errs as $error) {
                        $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
                    }
                }
                return $pf;
            }
            return $this->_postProcessValidPackagexml($pf);
        }
        return $info;
    }

    // }}}
    // {{{ xmlFromInfo()

    /**
     * Return an XML document based on the package info (as returned
     * by the PEAR_Common::infoFrom* methods).
     *
     * @param array  $pkginfo  package info
     *
     * @return string XML data
     *
     * @access public
     * @deprecated use a PEAR_PackageFile_v* object's generator instead
     */
    function xmlFromInfo($pkginfo)
    {
        $config = &PEAR_Config::singleton();
        $packagefile = &new PEAR_PackageFile($config);
        $pf = &$packagefile->fromArray($pkginfo);
        $gen = &$pf->getDefaultGenerator();
        return $gen->toXml(PEAR_VALIDATE_PACKAGING);
    }

    // }}}
    // {{{ validatePackageInfo()

    /**
     * Validate XML package definition file.
     *
     * @param  string $info Filename of the package archive or of the
     *                package definition file
     * @param  array $errors Array that will contain the errors
     * @param  array $warnings Array that will contain the warnings
     * @param  string $dir_prefix (optional) directory where source files
     *                may be found, or empty if they are not available
     * @access public
     * @return boolean
     * @deprecated use the validation of PEAR_PackageFile objects
     */
    function validatePackageInfo($info, &$errors, &$warnings, $dir_prefix = '')
    {
        $config = &PEAR_Config::singleton();
        $packagefile = &new PEAR_PackageFile($config);
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        if (strpos($info, '<?xml') !== false) {
            $pf = &$packagefile->fromXmlString($info, PEAR_VALIDATE_NORMAL, '');
        } else {
            $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL);
        }
        PEAR::staticPopErrorHandling();
        if (PEAR::isError($pf)) {
            $errs = $pf->getUserinfo();
            if (is_array($errs)) {
                foreach ($errs as $error) {
                    if ($error['level'] == 'error') {
                        $errors[] = $error['message'];
                    } else {
                        $warnings[] = $error['message'];
                    }
                }
            }
            return false;
        }
        return true;
    }

    // }}}
    // {{{ buildProvidesArray()

    /**
     * Build a "provides" array from data returned by
     * analyzeSourceCode().  The format of the built array is like
     * this:
     *
     *  array(
     *    'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
     *    ...
     *  )
     *
     *
     * @param array $srcinfo array with information about a source file
     * as returned by the analyzeSourceCode() method.
     *
     * @return void
     *
     * @access public
     *
     */
    function buildProvidesArray($srcinfo)
    {
        $file = basename($srcinfo['source_file']);
        $pn = '';
        if (isset($this->_packageName)) {
            $pn = $this->_packageName;
        }
        $pnl = strlen($pn);
        foreach ($srcinfo['declared_classes'] as $class) {
            $key = "class;$class";
            if (isset($this->pkginfo['provides'][$key])) {
                continue;
            }
            $this->pkginfo['provides'][$key] =
                array('file'=> $file, 'type' => 'class', 'name' => $class);
            if (isset($srcinfo['inheritance'][$class])) {
                $this->pkginfo['provides'][$key]['extends'] =
                    $srcinfo['inheritance'][$class];
            }
        }
        foreach ($srcinfo['declared_methods'] as $class => $methods) {
            foreach ($methods as $method) {
                $function = "$class::$method";
                $key = "function;$function";
                if ($method{0} == '_' || !strcasecmp($method, $class) ||
                    isset($this->pkginfo['provides'][$key])) {
                    continue;
                }
                $this->pkginfo['provides'][$key] =
                    array('file'=> $file, 'type' => 'function', 'name' => $function);
            }
        }

        foreach ($srcinfo['declared_functions'] as $function) {
            $key = "function;$function";
            if ($function{0} == '_' || isset($this->pkginfo['provides'][$key])) {
                continue;
            }
            if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
                $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
            }
            $this->pkginfo['provides'][$key] =
                array('file'=> $file, 'type' => 'function', 'name' => $function);
        }
    }

    // }}}
    // {{{ analyzeSourceCode()

    /**
     * Analyze the source code of the given PHP file
     *
     * @param  string Filename of the PHP file
     * @return mixed
     * @access public
     */
    function analyzeSourceCode($file)
    {
        if (!function_exists("token_get_all")) {
            return false;
        }
        if (!defined('T_DOC_COMMENT')) {
            define('T_DOC_COMMENT', T_COMMENT);
        }
        if (!defined('T_INTERFACE')) {
            define('T_INTERFACE', -1);
        }
        if (!defined('T_IMPLEMENTS')) {
            define('T_IMPLEMENTS', -1);
        }
        if (!$fp = @fopen($file, "r")) {
            return false;
        }
        fclose($fp);
        $contents = file_get_contents($file);
        $tokens = token_get_all($contents);
/*
        for ($i = 0; $i < sizeof($tokens); $i++) {
            @list($token, $data) = $tokens[$i];
            if (is_string($token)) {
                var_dump($token);
            } else {
                print token_name($token) . ' ';
                var_dump(rtrim($data));
            }
        }
*/
        $look_for = 0;
        $paren_level = 0;
        $bracket_level = 0;
        $brace_level = 0;
        $lastphpdoc = '';
        $current_class = '';
        $current_interface = '';
        $current_class_level = -1;
        $current_function = '';
        $current_function_level = -1;
        $declared_classes = array();
        $declared_interfaces = array();
        $declared_functions = array();
        $declared_methods = array();
        $used_classes = array();
        $used_functions = array();
        $extends = array();
        $implements = array();
        $nodeps = array();
        $inquote = false;
        $interface = false;
        for ($i = 0; $i < sizeof($tokens); $i++) {
            if (is_array($tokens[$i])) {
                list($token, $data) = $tokens[$i];
            } else {
                $token = $tokens[$i];
                $data = '';
            }
            if ($inquote) {
                if ($token != '"') {
                    continue;
                } else {
                    $inquote = false;
                    continue;
                }
            }
            switch ($token) {
                case T_WHITESPACE:
                    continue;
                case ';':
                    if ($interface) {
                        $current_function = '';
                        $current_function_level = -1;
                    }
                    break;
                case '"':
                    $inquote = true;
                    break;
                case T_CURLY_OPEN:
                case T_DOLLAR_OPEN_CURLY_BRACES:
                case '{': $brace_level++; continue 2;
                case '}':
                    $brace_level--;
                    if ($current_class_level == $brace_level) {
                        $current_class = '';
                        $current_class_level = -1;
                    }
                    if ($current_function_level == $brace_level) {
                        $current_function = '';
                        $current_function_level = -1;
                    }
                    continue 2;
                case '[': $bracket_level++; continue 2;
                case ']': $bracket_level--; continue 2;
                case '(': $paren_level++;   continue 2;
                case ')': $paren_level--;   continue 2;
                case T_INTERFACE:
                    $interface = true;
                case T_CLASS:
                    if (($current_class_level != -1) || ($current_function_level != -1)) {
                        PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"",
                            PEAR_COMMON_ERROR_INVALIDPHP);
                        return false;
                    }
                case T_FUNCTION:
                case T_NEW:
                case T_EXTENDS:
                case T_IMPLEMENTS:
                    $look_for = $token;
                    continue 2;
                case T_STRING:
                    if (version_compare(zend_version(), '2.0', '<')) {
                        if (in_array(strtolower($data),
                            array('public', 'private', 'protected', 'abstract',
                                  'interface', 'implements', 'throw') 
                                 )) {
                            PEAR::raiseError('Error: PHP5 token encountered in ' . $file . 
                            'packaging should be done in PHP 5');
                            return false;
                        }
                    }
                    if ($look_for == T_CLASS) {
                        $current_class = $data;
                        $current_class_level = $brace_level;
                        $declared_classes[] = $current_class;
                    } elseif ($look_for == T_INTERFACE) {
                        $current_interface = $data;
                        $current_class_level = $brace_level;
                        $declared_interfaces[] = $current_interface;
                    } elseif ($look_for == T_IMPLEMENTS) {
                        $implements[$current_class] = $data;
                    } elseif ($look_for == T_EXTENDS) {
                        $extends[$current_class] = $data;
                    } elseif ($look_for == T_FUNCTION) {
                        if ($current_class) {
                            $current_function = "$current_class::$data";
                            $declared_methods[$current_class][] = $data;
                        } elseif ($current_interface) {
                            $current_function = "$current_interface::$data";
                            $declared_methods[$current_interface][] = $data;
                        } else {
                            $current_function = $data;
                            $declared_functions[] = $current_function;
                        }
                        $current_function_level = $brace_level;
                        $m = array();
                    } elseif ($look_for == T_NEW) {
                        $used_classes[$data] = true;
                    }
                    $look_for = 0;
                    continue 2;
                case T_VARIABLE:
                    $look_for = 0;
                    continue 2;
                case T_DOC_COMMENT:
                case T_COMMENT:
                    if (preg_match('!^/\*\*\s!', $data)) {
                        $lastphpdoc = $data;
                        if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
                            $nodeps = array_merge($nodeps, $m[1]);
                        }
                    }
                    continue 2;
                case T_DOUBLE_COLON:
                    if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) {
                        PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"",
                            PEAR_COMMON_ERROR_INVALIDPHP);
                        return false;
                    }
                    $class = $tokens[$i - 1][1];
                    if (strtolower($class) != 'parent') {
                        $used_classes[$class] = true;
                    }
                    continue 2;
            }
        }
        return array(
            "source_file" => $file,
            "declared_classes" => $declared_classes,
            "declared_interfaces" => $declared_interfaces,
            "declared_methods" => $declared_methods,
            "declared_functions" => $declared_functions,
            "used_classes" => array_diff(array_keys($used_classes), $nodeps),
            "inheritance" => $extends,
            "implements" => $implements,
            );
    }

    // }}}
    // {{{  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)
    {
        if (!function_exists("token_get_all")) {
            return false;
        }
        if (PEAR::isError($info = $this->infoFromAny($any))) {
            return $this->raiseError($info);
        }
        if (!is_array($info)) {
            return false;
        }
        $deps = array();
        $used_c = $decl_c = $decl_f = $decl_m = array();
        foreach ($info['filelist'] as $file => $fa) {
            $tmp = $this->analyzeSourceCode($file);
            $used_c = @array_merge($used_c, $tmp['used_classes']);
            $decl_c = @array_merge($decl_c, $tmp['declared_classes']);
            $decl_f = @array_merge($decl_f, $tmp['declared_functions']);
            $decl_m = @array_merge($decl_m, $tmp['declared_methods']);
            $inheri = @array_merge($inheri, $tmp['inheritance']);
        }
        $used_c = array_unique($used_c);
        $decl_c = array_unique($decl_c);
        $undecl_c = array_diff($used_c, $decl_c);
        return array('used_classes' => $used_c,
                     'declared_classes' => $decl_c,
                     'declared_methods' => $decl_m,
                     'declared_functions' => $decl_f,
                     'undeclared_classes' => $undecl_c,
                     'inheritance' => $inheri,
                     );
    }

    // }}}
    // {{{ getUserRoles()

    /**
     * Get the valid roles for a PEAR package maintainer
     *
     * @return array
     * @static
     */
    function getUserRoles()
    {
        return $GLOBALS['_PEAR_Common_maintainer_roles'];
    }

    // }}}
    // {{{ getReleaseStates()

    /**
     * Get the valid package release states of packages
     *
     * @return array
     * @static
     */
    function getReleaseStates()
    {
        return $GLOBALS['_PEAR_Common_release_states'];
    }

    // }}}
    // {{{ getDependencyTypes()

    /**
     * Get the implemented dependency types (php, ext, pkg etc.)
     *
     * @return array
     * @static
     */
    function getDependencyTypes()
    {
        return $GLOBALS['_PEAR_Common_dependency_types'];
    }

    // }}}
    // {{{ getDependencyRelations()

    /**
     * Get the implemented dependency relations (has, lt, ge etc.)
     *
     * @return array
     * @static
     */
    function getDependencyRelations()
    {
        return $GLOBALS['_PEAR_Common_dependency_relations'];
    }

    // }}}
    // {{{ getFileRoles()

    /**
     * Get the implemented file roles
     *
     * @return array
     * @static
     */
    function getFileRoles()
    {
        return $GLOBALS['_PEAR_Common_file_roles'];
    }

    // }}}
    // {{{ getReplacementTypes()

    /**
     * Get the implemented file replacement types in
     *
     * @return array
     * @static
     */
    function getReplacementTypes()
    {
        return $GLOBALS['_PEAR_Common_replacement_types'];
    }

    // }}}
    // {{{ getProvideTypes()

    /**
     * Get the implemented file replacement types in
     *
     * @return array
     * @static
     */
    function getProvideTypes()
    {
        return $GLOBALS['_PEAR_Common_provide_types'];
    }

    // }}}
    // {{{ getScriptPhases()

    /**
     * Get the implemented file replacement types in
     *
     * @return array
     * @static
     */
    function getScriptPhases()
    {
        return $GLOBALS['_PEAR_Common_script_phases'];
    }

    // }}}
    // {{{ validPackageName()

    /**
     * Test whether a string contains a valid package name.
     *
     * @param string $name the package name to test
     *
     * @return bool
     *
     * @access public
     */
    function validPackageName($name)
    {
        return (bool)preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $name);
    }


    // }}}
    // {{{ validPackageVersion()

    /**
     * Test whether a string contains a valid package version.
     *
     * @param string $ver the package version to test
     *
     * @return bool
     *
     * @access public
     */
    function validPackageVersion($ver)
    {
        return (bool)preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver);
    }


    // }}}

    // {{{ downloadHttp()

    /**
     * Download a file through HTTP.  Considers suggested file name in
     * Content-disposition: header and can run a callback function for
     * different events.  The callback will be called with two
     * parameters: the callback type, and parameters.  The implemented
     * callback types are:
     *
     *  'setup'       called at the very beginning, parameter is a UI object
     *                that should be used for all output
     *  'message'     the parameter is a string with an informational message
     *  'saveas'      may be used to save with a different file name, the
     *                parameter is the filename that is about to be used.
     *                If a 'saveas' callback returns a non-empty string,
     *                that file name will be used as the filename instead.
     *                Note that $save_dir will not be affected by this, only
     *                the basename of the file.
     *  'start'       download is starting, parameter is number of bytes
     *                that are expected, or -1 if unknown
     *  'bytesread'   parameter is the number of bytes read so far
     *  'done'        download is complete, parameter is the total number
     *                of bytes read
     *  'connfailed'  if the TCP connection fails, this callback is called
     *                with array(host,port,errno,errmsg)
     *  'writefailed' if writing to disk fails, this callback is called
     *                with array(destfile,errmsg)
     *
     * If an HTTP proxy has been configured (http_proxy PEAR_Config
     * setting), the proxy will be used.
     *
     * @param string  $url       the URL to download
     * @param object  $ui        PEAR_Frontend_* instance
     * @param object  $config    PEAR_Config instance
     * @param string  $save_dir  (optional) directory to save file in
     * @param mixed   $callback  (optional) function/method to call for status
     *                           updates
     *
     * @return string  Returns the full path of the downloaded file or a PEAR
     *                 error on failure.  If the error is caused by
     *                 socket-related errors, the error object will
     *                 have the fsockopen error code available through
     *                 getCode().
     *
     * @access public
     * @deprecated in favor of PEAR_Downloader::downloadHttp()
     */
    function downloadHttp($url, &$ui, $save_dir = '.', $callback = null)
    {
        if (!class_exists('PEAR_Downloader')) {
            require_once 'PEAR/Downloader.php';
        }
        return PEAR_Downloader::downloadHttp($url, $ui, $save_dir, $callback);
    }

    // }}}

    /**
     * @param string $path relative or absolute include path
     * @return boolean
     * @static
     */
    function isIncludeable($path)
    {
        if (file_exists($path) && is_readable($path)) {
            return true;
        }
        $ipath = explode(PATH_SEPARATOR, ini_get('include_path'));
        foreach ($ipath as $include) {
            $test = realpath($include . DIRECTORY_SEPARATOR . $path);
            if (file_exists($test) && is_readable($test)) {
                return true;
            }
        }
        return false;
    }
}
require_once 'PEAR/Config.php';
require_once 'PEAR/PackageFile.php';
?>PEAR-1.7.2/PEAR/Config.php100644   1750   1750      212737 11014312742  10206 <?php
/**
 * PEAR_Config, customized configuration handling for the PEAR Installer
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Config.php,v 1.146 2008/05/14 04:16:08 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * Required for error handling
 */
require_once 'PEAR.php';
require_once 'PEAR/Registry.php';
require_once 'PEAR/Installer/Role.php';
require_once 'System.php';
require_once 'PEAR/Remote.php';

/**
 * Last created PEAR_Config instance.
 * @var object
 */
$GLOBALS['_PEAR_Config_instance'] = null;
if (!defined('PEAR_INSTALL_DIR') || !PEAR_INSTALL_DIR) {
    $PEAR_INSTALL_DIR = PHP_LIBDIR . DIRECTORY_SEPARATOR . 'pear';
} else {
    $PEAR_INSTALL_DIR = PEAR_INSTALL_DIR;
}

// Below we define constants with default values for all configuration
// parameters except username/password.  All of them can have their
// defaults set through environment variables.  The reason we use the
// PHP_ prefix is for some security, PHP protects environment
// variables starting with PHP_*.

// default channel and preferred mirror is based on whether we are invoked through
// the "pear" or the "pecl" command

if (!defined('PEAR_RUNTYPE') || PEAR_RUNTYPE == 'pear') {
    define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pear.php.net');
} else {
    define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pecl.php.net');
}

if (getenv('PHP_PEAR_SYSCONF_DIR')) {
    define('PEAR_CONFIG_SYSCONFDIR', getenv('PHP_PEAR_SYSCONF_DIR'));
} elseif (getenv('SystemRoot')) {
    define('PEAR_CONFIG_SYSCONFDIR', getenv('SystemRoot'));
} else {
    define('PEAR_CONFIG_SYSCONFDIR', PHP_SYSCONFDIR);
}

// Default for master_server
if (getenv('PHP_PEAR_MASTER_SERVER')) {
    define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', getenv('PHP_PEAR_MASTER_SERVER'));
} else {
    define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', 'pear.php.net');
}

// Default for http_proxy
if (getenv('PHP_PEAR_HTTP_PROXY')) {
    define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('PHP_PEAR_HTTP_PROXY'));
} elseif (getenv('http_proxy')) {
    define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('http_proxy'));
} else {
    define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', '');
}

// Default for php_dir
if (getenv('PHP_PEAR_INSTALL_DIR')) {
    define('PEAR_CONFIG_DEFAULT_PHP_DIR', getenv('PHP_PEAR_INSTALL_DIR'));
} else {
    if (file_exists($PEAR_INSTALL_DIR) && is_dir($PEAR_INSTALL_DIR)) {
        define('PEAR_CONFIG_DEFAULT_PHP_DIR',
               $PEAR_INSTALL_DIR);
    } else {
        define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR);
    }
}

// Default for ext_dir
if (getenv('PHP_PEAR_EXTENSION_DIR')) {
    define('PEAR_CONFIG_DEFAULT_EXT_DIR', getenv('PHP_PEAR_EXTENSION_DIR'));
} else {
    if (ini_get('extension_dir')) {
        define('PEAR_CONFIG_DEFAULT_EXT_DIR', ini_get('extension_dir'));
    } elseif (defined('PEAR_EXTENSION_DIR') &&
              file_exists(PEAR_EXTENSION_DIR) && is_dir(PEAR_EXTENSION_DIR)) {
        define('PEAR_CONFIG_DEFAULT_EXT_DIR', PEAR_EXTENSION_DIR);
    } elseif (defined('PHP_EXTENSION_DIR')) {
        define('PEAR_CONFIG_DEFAULT_EXT_DIR', PHP_EXTENSION_DIR);
    } else {
        define('PEAR_CONFIG_DEFAULT_EXT_DIR', '.');
    }
}

// Default for doc_dir
if (getenv('PHP_PEAR_DOC_DIR')) {
    define('PEAR_CONFIG_DEFAULT_DOC_DIR', getenv('PHP_PEAR_DOC_DIR'));
} else {
    define('PEAR_CONFIG_DEFAULT_DOC_DIR',
           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'docs');
}

// Default for bin_dir
if (getenv('PHP_PEAR_BIN_DIR')) {
    define('PEAR_CONFIG_DEFAULT_BIN_DIR', getenv('PHP_PEAR_BIN_DIR'));
} else {
    define('PEAR_CONFIG_DEFAULT_BIN_DIR', PHP_BINDIR);
}

// Default for data_dir
if (getenv('PHP_PEAR_DATA_DIR')) {
    define('PEAR_CONFIG_DEFAULT_DATA_DIR', getenv('PHP_PEAR_DATA_DIR'));
} else {
    define('PEAR_CONFIG_DEFAULT_DATA_DIR',
           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'data');
}

// Default for cfg_dir
if (getenv('PHP_PEAR_CFG_DIR')) {
    define('PEAR_CONFIG_DEFAULT_CFG_DIR', getenv('PHP_PEAR_CFG_DIR'));
} else {
    define('PEAR_CONFIG_DEFAULT_CFG_DIR',
           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'cfg');
}

// Default for www_dir
if (getenv('PHP_PEAR_WWW_DIR')) {
    define('PEAR_CONFIG_DEFAULT_WWW_DIR', getenv('PHP_PEAR_WWW_DIR'));
} else {
    define('PEAR_CONFIG_DEFAULT_WWW_DIR',
           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'www');
}

// Default for test_dir
if (getenv('PHP_PEAR_TEST_DIR')) {
    define('PEAR_CONFIG_DEFAULT_TEST_DIR', getenv('PHP_PEAR_TEST_DIR'));
} else {
    define('PEAR_CONFIG_DEFAULT_TEST_DIR',
           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'tests');
}

// Default for temp_dir
if (getenv('PHP_PEAR_TEMP_DIR')) {
    define('PEAR_CONFIG_DEFAULT_TEMP_DIR', getenv('PHP_PEAR_TEMP_DIR'));
} else {
    define('PEAR_CONFIG_DEFAULT_TEMP_DIR',
           System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
           DIRECTORY_SEPARATOR . 'temp');
}

// Default for cache_dir
if (getenv('PHP_PEAR_CACHE_DIR')) {
    define('PEAR_CONFIG_DEFAULT_CACHE_DIR', getenv('PHP_PEAR_CACHE_DIR'));
} else {
    define('PEAR_CONFIG_DEFAULT_CACHE_DIR',
           System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
           DIRECTORY_SEPARATOR . 'cache');
}

// Default for download_dir
if (getenv('PHP_PEAR_DOWNLOAD_DIR')) {
    define('PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR', getenv('PHP_PEAR_DOWNLOAD_DIR'));
} else {
    define('PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR',
           System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
           DIRECTORY_SEPARATOR . 'download');
}

// Default for php_bin
if (getenv('PHP_PEAR_PHP_BIN')) {
    define('PEAR_CONFIG_DEFAULT_PHP_BIN', getenv('PHP_PEAR_PHP_BIN'));
} else {
    define('PEAR_CONFIG_DEFAULT_PHP_BIN', PEAR_CONFIG_DEFAULT_BIN_DIR.
           DIRECTORY_SEPARATOR.'php'.(OS_WINDOWS ? '.exe' : ''));
}

// Default for verbose
if (getenv('PHP_PEAR_VERBOSE')) {
    define('PEAR_CONFIG_DEFAULT_VERBOSE', getenv('PHP_PEAR_VERBOSE'));
} else {
    define('PEAR_CONFIG_DEFAULT_VERBOSE', 1);
}

// Default for preferred_state
if (getenv('PHP_PEAR_PREFERRED_STATE')) {
    define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', getenv('PHP_PEAR_PREFERRED_STATE'));
} else {
    define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', 'stable');
}

// Default for umask
if (getenv('PHP_PEAR_UMASK')) {
    define('PEAR_CONFIG_DEFAULT_UMASK', getenv('PHP_PEAR_UMASK'));
} else {
    define('PEAR_CONFIG_DEFAULT_UMASK', decoct(umask()));
}

// Default for cache_ttl
if (getenv('PHP_PEAR_CACHE_TTL')) {
    define('PEAR_CONFIG_DEFAULT_CACHE_TTL', getenv('PHP_PEAR_CACHE_TTL'));
} else {
    define('PEAR_CONFIG_DEFAULT_CACHE_TTL', 3600);
}

// Default for sig_type
if (getenv('PHP_PEAR_SIG_TYPE')) {
    define('PEAR_CONFIG_DEFAULT_SIG_TYPE', getenv('PHP_PEAR_SIG_TYPE'));
} else {
    define('PEAR_CONFIG_DEFAULT_SIG_TYPE', 'gpg');
}

// Default for sig_bin
if (getenv('PHP_PEAR_SIG_BIN')) {
    define('PEAR_CONFIG_DEFAULT_SIG_BIN', getenv('PHP_PEAR_SIG_BIN'));
} else {
    define('PEAR_CONFIG_DEFAULT_SIG_BIN',
           System::which(
               'gpg', OS_WINDOWS ? 'c:\gnupg\gpg.exe' : '/usr/local/bin/gpg'));
}

// Default for sig_keydir
if (getenv('PHP_PEAR_SIG_KEYDIR')) {
    define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR', getenv('PHP_PEAR_SIG_KEYDIR'));
} else {
    define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR',
           PEAR_CONFIG_SYSCONFDIR . DIRECTORY_SEPARATOR . 'pearkeys');
}

/**
 * This is a class for storing configuration data, keeping track of
 * which are system-defined, user-defined or defaulted.
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */
class PEAR_Config extends PEAR
{
    // {{{ properties

    /**
     * Array of config files used.
     *
     * @var array layer => config file
     */
    var $files = array(
        'system' => '',
        'user' => '',
        );

    var $layers = array();
    
    /**
     * Configuration data, two-dimensional array where the first
     * dimension is the config layer ('user', 'system' and 'default'),
     * and the second dimension is keyname => value.
     *
     * The order in the first dimension is important!  Earlier
     * layers will shadow later ones when a config value is
     * requested (if a 'user' value exists, it will be returned first,
     * then 'system' and finally 'default').
     *
     * @var array layer => array(keyname => value, ...)
     */
    var $configuration = array(
        'user' => array(),
        'system' => array(),
        'default' => array(),
        );
    
    /**
     * Configuration values that can be set for a channel
     *
     * All other configuration values can only have a global value
     * @var array
     * @access private
     */
    var $_channelConfigInfo = array(
        'php_dir', 'ext_dir', 'doc_dir', 'bin_dir', 'data_dir', 'cfg_dir',
        'test_dir', 'www_dir', 'php_bin', 'username', 'password', 'verbose',
        'preferred_state', 'umask', 'preferred_mirror', 'php_ini'
        );

    /**
     * Channels that can be accessed
     * @see setChannels()
     * @var array
     * @access private
     */
    var $_channels = array('pear.php.net', 'pecl.php.net', '__uri');

    /**
     * This variable is used to control the directory values returned
     * @see setInstallRoot();
     * @var string|false
     * @access private
     */
    var $_installRoot = false;

    /**
     * If requested, this will always refer to the registry
     * contained in php_dir
     * @var PEAR_Registry
     */
    var $_registry = array();

    /**
     * @var array
     * @access private
     */
    var $_regInitialized = array();

    /**
     * @var bool
     * @access private
     */
    var $_noRegistry = false;

    /**
     * amount of errors found while parsing config
     * @var integer
     * @access private
     */
    var $_errorsFound = 0;
    var $_lastError = null;

    /**
     * Information about the configuration data.  Stores the type,
     * default value and a documentation string for each configuration
     * value.
     *
     * @var array layer => array(infotype => value, ...)
     */
    var $configuration_info = array(
        // Channels/Internet Access
        'default_channel' => array(
            'type' => 'string',
            'default' => PEAR_CONFIG_DEFAULT_CHANNEL,
            'doc' => 'the default channel to use for all non explicit commands',
            'prompt' => 'Default Channel',
            'group' => 'Internet Access',
            ),
        'preferred_mirror' => array(
            'type' => 'string',
            'default' => PEAR_CONFIG_DEFAULT_CHANNEL,
            'doc' => 'the default server or mirror to use for channel actions',
            'prompt' => 'Default Channel Mirror',
            'group' => 'Internet Access',
            ),
        'remote_config' => array(
            'type' => 'password',
            'default' => '',
            'doc' => 'ftp url of remote configuration file to use for synchronized install',
            'prompt' => 'Remote Configuration File',
            'group' => 'Internet Access',
            ),
        'auto_discover' => array(
            'type' => 'integer',
            'default' => 0,
            'doc' => 'whether to automatically discover new channels',
            'prompt' => 'Auto-discover new Channels',
            'group' => 'Internet Access',
            ),
        // Internet Access
        'master_server' => array(
            'type' => 'string',
            'default' => 'pear.php.net',
            'doc' => 'name of the main PEAR server [NOT USED IN THIS VERSION]',
            'prompt' => 'PEAR server [DEPRECATED]',
            'group' => 'Internet Access',
            ),
        'http_proxy' => array(
            'type' => 'string',
            'default' => PEAR_CONFIG_DEFAULT_HTTP_PROXY,
            'doc' => 'HTTP proxy (host:port) to use when downloading packages',
            'prompt' => 'HTTP Proxy Server Address',
            'group' => 'Internet Access',
            ),
        // File Locations
        'php_dir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_PHP_DIR,
            'doc' => 'directory where .php files are installed',
            'prompt' => 'PEAR directory',
            'group' => 'File Locations',
            ),
        'ext_dir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_EXT_DIR,
            'doc' => 'directory where loadable extensions are installed',
            'prompt' => 'PHP extension directory',
            'group' => 'File Locations',
            ),
        'doc_dir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_DOC_DIR,
            'doc' => 'directory where documentation is installed',
            'prompt' => 'PEAR documentation directory',
            'group' => 'File Locations',
            ),
        'bin_dir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_BIN_DIR,
            'doc' => 'directory where executables are installed',
            'prompt' => 'PEAR executables directory',
            'group' => 'File Locations',
            ),
        'data_dir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_DATA_DIR,
            'doc' => 'directory where data files are installed',
            'prompt' => 'PEAR data directory',
            'group' => 'File Locations (Advanced)',
            ),
        'cfg_dir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_CFG_DIR,
            'doc' => 'directory where modifiable configuration files are installed',
            'prompt' => 'PEAR configuration file directory',
            'group' => 'File Locations (Advanced)',
            ),
        'www_dir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_WWW_DIR,
            'doc' => 'directory where www frontend files (html/js) are installed',
            'prompt' => 'PEAR www files directory',
            'group' => 'File Locations (Advanced)',
            ),
        'test_dir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_TEST_DIR,
            'doc' => 'directory where regression tests are installed',
            'prompt' => 'PEAR test directory',
            'group' => 'File Locations (Advanced)',
            ),
        'cache_dir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_CACHE_DIR,
            'doc' => 'directory which is used for XMLRPC cache',
            'prompt' => 'PEAR Installer cache directory',
            'group' => 'File Locations (Advanced)',
            ),
        'temp_dir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_TEMP_DIR,
            'doc' => 'directory which is used for all temp files',
            'prompt' => 'PEAR Installer temp directory',
            'group' => 'File Locations (Advanced)',
            ),
        'download_dir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR,
            'doc' => 'directory which is used for all downloaded files',
            'prompt' => 'PEAR Installer download directory',
            'group' => 'File Locations (Advanced)',
            ),
        'php_bin' => array(
            'type' => 'file',
            'default' => PEAR_CONFIG_DEFAULT_PHP_BIN,
            'doc' => 'PHP CLI/CGI binary for executing scripts',
            'prompt' => 'PHP CLI/CGI binary',
            'group' => 'File Locations (Advanced)',
            ),
        'php_ini' => array(
            'type' => 'file',
            'default' => '',
            'doc' => 'location of php.ini in which to enable PECL extensions on install',
            'prompt' => 'php.ini location',
            'group' => 'File Locations (Advanced)',
            ),
        // Maintainers
        'username' => array(
            'type' => 'string',
            'default' => '',
            'doc' => '(maintainers) your PEAR account name',
            'prompt' => 'PEAR username (for maintainers)',
            'group' => 'Maintainers',
            ),
        'password' => array(
            'type' => 'password',
            'default' => '',
            'doc' => '(maintainers) your PEAR account password',
            'prompt' => 'PEAR password (for maintainers)',
            'group' => 'Maintainers',
            ),
        // Advanced
        'verbose' => array(
            'type' => 'integer',
            'default' => PEAR_CONFIG_DEFAULT_VERBOSE,
            'doc' => 'verbosity level
0: really quiet
1: somewhat quiet
2: verbose
3: debug',
            'prompt' => 'Debug Log Level',
            'group' => 'Advanced',
            ),
        'preferred_state' => array(
            'type' => 'set',
            'default' => PEAR_CONFIG_DEFAULT_PREFERRED_STATE,
            'doc' => 'the installer will prefer releases with this state when installing packages without a version or state specified',
            'valid_set' => array(
                'stable', 'beta', 'alpha', 'devel', 'snapshot'),
            'prompt' => 'Preferred Package State',
            'group' => 'Advanced',
            ),
        'umask' => array(
            'type' => 'mask',
            'default' => PEAR_CONFIG_DEFAULT_UMASK,
            'doc' => 'umask used when creating files (Unix-like systems only)',
            'prompt' => 'Unix file mask',
            'group' => 'Advanced',
            ),
        'cache_ttl' => array(
            'type' => 'integer',
            'default' => PEAR_CONFIG_DEFAULT_CACHE_TTL,
            'doc' => 'amount of secs where the local cache is used and not updated',
            'prompt' => 'Cache TimeToLive',
            'group' => 'Advanced',
            ),
        'sig_type' => array(
            'type' => 'set',
            'default' => PEAR_CONFIG_DEFAULT_SIG_TYPE,
            'doc' => 'which package signature mechanism to use',
            'valid_set' => array('gpg'),
            'prompt' => 'Package Signature Type',
            'group' => 'Maintainers',
            ),
        'sig_bin' => array(
            'type' => 'string',
            'default' => PEAR_CONFIG_DEFAULT_SIG_BIN,
            'doc' => 'which package signature mechanism to use',
            'prompt' => 'Signature Handling Program',
            'group' => 'Maintainers',
            ),
        'sig_keyid' => array(
            'type' => 'string',
            'default' => '',
            'doc' => 'which key to use for signing with',
            'prompt' => 'Signature Key Id',
            'group' => 'Maintainers',
            ),
        'sig_keydir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_SIG_KEYDIR,
            'doc' => 'directory where signature keys are located',
            'prompt' => 'Signature Key Directory',
            'group' => 'Maintainers',
            ),
        // __channels is reserved - used for channel-specific configuration
        );

    // }}}

    // {{{ PEAR_Config([file], [defaults_file])

    /**
     * Constructor.
     *
     * @param string file to read user-defined options from
     * @param string file to read system-wide defaults from
     * @param bool   determines whether a registry object "follows"
     *               the value of php_dir (is automatically created
     *               and moved when php_dir is changed)
     * @param bool   if true, fails if configuration files cannot be loaded
     *
     * @access public
     *
     * @see PEAR_Config::singleton
     */
    function PEAR_Config($user_file = '', $system_file = '', $ftp_file = false,
                         $strict = true)
    {
        $this->PEAR();
        PEAR_Installer_Role::initializeConfig($this);
        $sl = DIRECTORY_SEPARATOR;
        if (empty($user_file)) {
            if (OS_WINDOWS) {
                $user_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini';
            } else {
                $user_file = getenv('HOME') . $sl . '.pearrc';
            }
        }
        if (empty($system_file)) {
            if (OS_WINDOWS) {
                $system_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pearsys.ini';
            } else {
                $system_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.conf';
            }
        }

        $this->layers = array_keys($this->configuration);
        $this->files['user'] = $user_file;
        $this->files['system'] = $system_file;
        if ($user_file && file_exists($user_file)) {
            $this->pushErrorHandling(PEAR_ERROR_RETURN);
            $this->readConfigFile($user_file, 'user', $strict);
            $this->popErrorHandling();
            if ($this->_errorsFound > 0) {
                return;
            }
        }

        if ($system_file && file_exists($system_file)) {
            $this->mergeConfigFile($system_file, false, 'system', $strict);
            if ($this->_errorsFound > 0) {
                return;
            }

        }

        if (!$ftp_file) {
            $ftp_file = $this->get('remote_config');
        }

        if ($ftp_file && defined('PEAR_REMOTEINSTALL_OK')) {
            $this->readFTPConfigFile($ftp_file);
        }

        foreach ($this->configuration_info as $key => $info) {
            $this->configuration['default'][$key] = $info['default'];
        }

        $this->_registry['default'] = &new PEAR_Registry($this->configuration['default']['php_dir']);
        $this->_registry['default']->setConfig($this, false);
        $this->_regInitialized['default'] = false;
        //$GLOBALS['_PEAR_Config_instance'] = &$this;
    }

    // }}}
    /**
     * Return the default locations of user and system configuration files
     * @static
     */
    function getDefaultConfigFiles()
    {
        $sl = DIRECTORY_SEPARATOR;
        if (OS_WINDOWS) {
            return array(
                'user' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini',
                'system' =>  PEAR_CONFIG_SYSCONFDIR . $sl . 'pearsys.ini'
            );
        } else {
            return array(
                'user' => getenv('HOME') . $sl . '.pearrc',
                'system' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.conf'
            );
        }
    }
    // {{{ singleton([file], [defaults_file])

    /**
     * Static singleton method.  If you want to keep only one instance
     * of this class in use, this method will give you a reference to
     * the last created PEAR_Config object if one exists, or create a
     * new object.
     *
     * @param string (optional) file to read user-defined options from
     * @param string (optional) file to read system-wide defaults from
     *
     * @return object an existing or new PEAR_Config instance
     *
     * @access public
     *
     * @see PEAR_Config::PEAR_Config
     */
    function &singleton($user_file = '', $system_file = '', $strict = true)
    {
        if (is_object($GLOBALS['_PEAR_Config_instance'])) {
            return $GLOBALS['_PEAR_Config_instance'];
        }

        $t_conf = &new PEAR_Config($user_file, $system_file, false, $strict);
        if ($t_conf->_errorsFound > 0) {
             return $t_conf->lastError;
        }

        $GLOBALS['_PEAR_Config_instance'] = &$t_conf;
        return $GLOBALS['_PEAR_Config_instance'];
    }

    // }}}
    // {{{ validConfiguration()

    /**
     * Determine whether any configuration files have been detected, and whether a
     * registry object can be retrieved from this configuration.
     * @return bool
     * @since PEAR 1.4.0a1
     */
    function validConfiguration()
    {
        if ($this->isDefinedLayer('user') || $this->isDefinedLayer('system')) {
            return true;
        }
        return false;
    }

    // }}}
    // {{{ readConfigFile([file], [layer])

    /**
     * Reads configuration data from a file.  All existing values in
     * the config layer are discarded and replaced with data from the
     * file.
     * @param string file to read from, if NULL or not specified, the
     *               last-used file for the same layer (second param) is used
     * @param string config layer to insert data into ('user' or 'system')
     * @return bool TRUE on success or a PEAR error on failure
     */
    function readConfigFile($file = null, $layer = 'user', $strict = true)
    {
        if (empty($this->files[$layer])) {
            return $this->raiseError("unknown config layer `$layer'");
        }

        if ($file === null) {
            $file = $this->files[$layer];
        }

        $data = $this->_readConfigDataFrom($file);

        if (PEAR::isError($data)) {
            if ($strict) {
                $this->_errorsFound++;
                $this->lastError = $data;

                return $data;
            } else {
                return true;
            }
        } else {
            $this->files[$layer] = $file;
        }

        $this->_decodeInput($data);
        $this->configuration[$layer] = $data;
        $this->_setupChannels();
        if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) {
            $this->_registry[$layer] = &new PEAR_Registry($phpdir);
            $this->_registry[$layer]->setConfig($this, false);
            $this->_regInitialized[$layer] = false;
        } else {
            unset($this->_registry[$layer]);
        }
        return true;
    }

    // }}}

    /**
     * @param string url to the remote config file, like ftp://www.example.com/pear/config.ini
     * @return true|PEAR_Error
     */
    function readFTPConfigFile($path)
    {
        do { // poor man's try
            if (!class_exists('PEAR_FTP')) {
                if (!class_exists('PEAR_Common')) {
                    require_once 'PEAR/Common.php';
                }
                if (PEAR_Common::isIncludeable('PEAR/FTP.php')) {
                    require_once 'PEAR/FTP.php';
                }
            }
            if (class_exists('PEAR_FTP')) {
                $this->_ftp = &new PEAR_FTP;
                $this->_ftp->pushErrorHandling(PEAR_ERROR_RETURN);
                $e = $this->_ftp->init($path);
                if (PEAR::isError($e)) {
                    $this->_ftp->popErrorHandling();
                    return $e;
                }
                $tmp = System::mktemp('-d');
                PEAR_Common::addTempFile($tmp);
                $e = $this->_ftp->get(basename($path), $tmp . DIRECTORY_SEPARATOR .
                    'pear.ini', false, FTP_BINARY);
                if (PEAR::isError($e)) {
                    $this->_ftp->popErrorHandling();
                    return $e;
                }
                PEAR_Common::addTempFile($tmp . DIRECTORY_SEPARATOR . 'pear.ini');
                $this->_ftp->disconnect();
                $this->_ftp->popErrorHandling();
                $this->files['ftp'] = $tmp . DIRECTORY_SEPARATOR . 'pear.ini';
                $e = $this->readConfigFile(null, 'ftp');
                if (PEAR::isError($e)) {
                    return $e;
                }
                $fail = array();
                foreach ($this->configuration_info as $key => $val) {
                    if (in_array($this->getGroup($key),
                          array('File Locations', 'File Locations (Advanced)')) &&
                          $this->getType($key) == 'directory') {
                        // any directory configs must be set for this to work
                        if (!isset($this->configuration['ftp'][$key])) {
                            $fail[] = $key;
                        }
                    }
                }
                if (count($fail)) {
                    $fail = '"' . implode('", "', $fail) . '"';
                    unset($this->files['ftp']);
                    unset($this->configuration['ftp']);
                    return PEAR::raiseError('ERROR: Ftp configuration file must set all ' .
                        'directory configuration variables.  These variables were not set: ' .
                        $fail);
                } else {
                    return true;
                }
            } else {
                return PEAR::raiseError('PEAR_RemoteInstaller must be installed to use remote config');
            }
        } while (false); // poor man's catch
        unset($this->files['ftp']);
        return PEAR::raiseError('no remote host specified');
    }

    // {{{ _setupChannels()
    
    /**
     * Reads the existing configurations and creates the _channels array from it
     */
    function _setupChannels()
    {
        $set = array_flip(array_values($this->_channels));
        foreach ($this->configuration as $layer => $data) {
            $i = 1000;
            if (isset($data['__channels']) && is_array($data['__channels'])) {
                foreach ($data['__channels'] as $channel => $info) {
                    $set[$channel] = $i++;
                }
            }
        }
        $this->_channels = array_values(array_flip($set));
        $this->setChannels($this->_channels);
    }

    // }}}
    // {{{ deleteChannel(channel)

    function deleteChannel($channel)
    {
        foreach ($this->configuration as $layer => $data) {
            if (isset($data['__channels'])) {
                if (isset($data['__channels'][strtolower($channel)])) {
                    unset($this->configuration[$layer]['__channels'][strtolower($channel)]);
                }
            }
        }
        $this->_channels = array_flip($this->_channels);
        unset($this->_channels[strtolower($channel)]);
        $this->_channels = array_flip($this->_channels);
    }

    // }}}
    // {{{ mergeConfigFile(file, [override], [layer])

    /**
     * Merges data into a config layer from a file.  Does the same
     * thing as readConfigFile, except it does not replace all
     * existing values in the config layer.
     * @param string file to read from
     * @param bool whether to overwrite existing data (default TRUE)
     * @param string config layer to insert data into ('user' or 'system')
     * @param string if true, errors are returned if file opening fails
     * @return bool TRUE on success or a PEAR error on failure
     */
    function mergeConfigFile($file, $override = true, $layer = 'user', $strict = true)
    {
        if (empty($this->files[$layer])) {
            return $this->raiseError("unknown config layer `$layer'");
        }
        if ($file === null) {
            $file = $this->files[$layer];
        }
        $data = $this->_readConfigDataFrom($file);
        if (PEAR::isError($data)) {
            if ($strict) {
                $this->_errorsFound++;
                $this->lastError = $data;

                return $data;
            } else {
                return true;
            }
        }
        $this->_decodeInput($data);
        if ($override) {
            $this->configuration[$layer] =
                PEAR_Config::arrayMergeRecursive($this->configuration[$layer], $data);
        } else {
            $this->configuration[$layer] =
                PEAR_Config::arrayMergeRecursive($data, $this->configuration[$layer]);
        }
        $this->_setupChannels();
        if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) {
            $this->_registry[$layer] = &new PEAR_Registry($phpdir);
            $this->_registry[$layer]->setConfig($this, false);
            $this->_regInitialized[$layer] = false;
        } else {
            unset($this->_registry[$layer]);
        }
        return true;
    }

    // }}}
    // {{{ arrayMergeRecursive($arr2, $arr1)
    /**
     * @param array
     * @param array
     * @return array
     * @static
     */
    function arrayMergeRecursive($arr2, $arr1)
    {
        $ret = array();
        foreach ($arr2 as $key => $data) {
            if (!isset($arr1[$key])) {
                $ret[$key] = $data;
                unset($arr1[$key]);
                continue;
            }
            if (is_array($data)) {
                if (!is_array($arr1[$key])) {
                    $ret[$key] = $arr1[$key];
                    unset($arr1[$key]);
                    continue;
                }
                $ret[$key] = PEAR_Config::arrayMergeRecursive($arr1[$key], $arr2[$key]);
                unset($arr1[$key]);
            }
        }
        return array_merge($ret, $arr1);
    }

    // }}}
    // {{{ writeConfigFile([file], [layer])

    /**
     * Writes data into a config layer from a file.
     *
     * @param string|null file to read from, or null for default
     * @param string config layer to insert data into ('user' or
     *               'system')
     * @param string|null data to write to config file or null for internal data [DEPRECATED]
     * @return bool TRUE on success or a PEAR error on failure
     */
    function writeConfigFile($file = null, $layer = 'user', $data = null)
    {
        $this->_lazyChannelSetup($layer);
        if ($layer == 'both' || $layer == 'all') {
            foreach ($this->files as $type => $file) {
                $err = $this->writeConfigFile($file, $type, $data);
                if (PEAR::isError($err)) {
                    return $err;
                }
            }
            return true;
        }
        if (empty($this->files[$layer])) {
            return $this->raiseError("unknown config file type `$layer'");
        }
        if ($file === null) {
            $file = $this->files[$layer];
        }
        $data = ($data === null) ? $this->configuration[$layer] : $data;
        $this->_encodeOutput($data);
        $opt = array('-p', dirname($file));
        if (!@System::mkDir($opt)) {
            return $this->raiseError("could not create directory: " . dirname($file));
        }
        if (file_exists($file) && is_file($file) && !is_writeable($file)) {
            return $this->raiseError("no write access to $file!");
        }
        $fp = @fopen($file, "w");
        if (!$fp) {
            return $this->raiseError("PEAR_Config::writeConfigFile fopen('$file','w') failed ($php_errormsg)");
        }
        $contents = "#PEAR_Config 0.9\n" . serialize($data);
        if (!@fwrite($fp, $contents)) {
            return $this->raiseError("PEAR_Config::writeConfigFile: fwrite failed ($php_errormsg)");
        }
        return true;
    }

    // }}}
    // {{{ _readConfigDataFrom(file)

    /**
     * Reads configuration data from a file and returns the parsed data
     * in an array.
     *
     * @param string file to read from
     *
     * @return array configuration data or a PEAR error on failure
     *
     * @access private
     */
    function _readConfigDataFrom($file)
    {
        $fp = false;
        if (file_exists($file)) {
            $fp = @fopen($file, "r");
        }
        if (!$fp) {
            return $this->raiseError("PEAR_Config::readConfigFile fopen('$file','r') failed");
        }
        $size = filesize($file);
        $rt = get_magic_quotes_runtime();
        set_magic_quotes_runtime(0);
        fclose($fp);
        $contents = file_get_contents($file);
        if (empty($contents)) {
            return $this->raiseError('Configuration file "' . $file . '" is empty');
        }
        
        set_magic_quotes_runtime($rt);

        $version = false;
        if (preg_match('/^#PEAR_Config\s+(\S+)\s+/si', $contents, $matches)) {
            $version = $matches[1];
            $contents = substr($contents, strlen($matches[0]));
        } else {
            // Museum config file
            if (substr($contents,0,2) == 'a:') {
                $version = '0.1';
            }
        }
        if ($version && version_compare("$version", '1', '<')) {

            // no '@', it is possible that unserialize
            // raises a notice but it seems to block IO to
            // STDOUT if a '@' is used and a notice is raise
            $data = unserialize($contents);

            if (!is_array($data) && !$data) {
                if ($contents == serialize(false)) {
                    $data = array();
                } else {
                    $err = $this->raiseError("PEAR_Config: bad data in $file");
                    return $err;
                }
            }
            if (!is_array($data)) {
                if (strlen(trim($contents)) > 0) {
                    $error = "PEAR_Config: bad data in $file";
                    $err = $this->raiseError($error);
                    return $err;
                } else {
                    $data = array();
                }
            }
        // add parsing of newer formats here...
        } else {
            $err = $this->raiseError("$file: unknown version `$version'");
            return $err; 
        }
        return $data;
    }

    // }}}
    // {{{ getConfFile(layer)
    /**
    * Gets the file used for storing the config for a layer
    *
    * @param string $layer 'user' or 'system'
    */

    function getConfFile($layer)
    {
        return $this->files[$layer];
    }

    // }}}

    /**
     * @param string Configuration class name, used for detecting duplicate calls
     * @param array information on a role as parsed from its xml file
     * @return true|PEAR_Error
     * @access private
     */
    function _addConfigVars($class, $vars)
    {
        static $called = array();
        if (isset($called[$class])) {
            return;
        }
        $called[$class] = 1;
        if (count($vars) > 3) {
            return $this->raiseError('Roles can only define 3 new config variables or less');
        }
        foreach ($vars as $name => $var) {
            if (!is_array($var)) {
                return $this->raiseError('Configuration information must be an array');
            }
            if (!isset($var['type'])) {
                return $this->raiseError('Configuration information must contain a type');
            } else {
                if (!in_array($var['type'],
                      array('string', 'mask', 'password', 'directory', 'file', 'set'))) {
                    return $this->raiseError(
                        'Configuration type must be one of directory, file, string, ' .
                        'mask, set, or password');
                }
            }
            if (!isset($var['default'])) {
                return $this->raiseError(
                    'Configuration information must contain a default value ("default" index)');
            } else {
                if (is_array($var['default'])) {
                    $real_default = '';
                    foreach ($var['default'] as $config_var => $val) {
                        if (strpos($config_var, 'text') === 0) {
                            $real_default .= $val;
                        } elseif (strpos($config_var, 'constant') === 0) {
                            if (defined($val)) {
                                $real_default .= constant($val);
                            } else {
                                return $this->raiseError(
                                    'Unknown constant "' . $val . '" requested in ' .
                                    'default value for configuration variable "' .
                                    $name . '"');
                            }
                        } elseif (isset($this->configuration_info[$config_var])) {
                            $real_default .=
                                $this->configuration_info[$config_var]['default'];
                        } else {
                            return $this->raiseError(
                                'Unknown request for "' . $config_var . '" value in ' .
                                'default value for configuration variable "' .
                                $name . '"');
                        }
                    }
                    $var['default'] = $real_default;
                }
                if ($var['type'] == 'integer') {
                    $var['default'] = (integer) $var['default'];
                }
            }
            if (!isset($var['doc'])) {
                return $this->raiseError(
                    'Configuration information must contain a summary ("doc" index)');
            }
            if (!isset($var['prompt'])) {
                return $this->raiseError(
                    'Configuration information must contain a simple prompt ("prompt" index)');
            }
            if (!isset($var['group'])) {
                return $this->raiseError(
                    'Configuration information must contain a simple group ("group" index)');
            }
            if (isset($this->configuration_info[$name])) {
                return $this->raiseError('Configuration variable "' . $name .
                    '" already exists');
            }
            $this->configuration_info[$name] = $var;
            // fix bug #7351: setting custom config variable in a channel fails
            $this->_channelConfigInfo[] = $name;
        }
        return true;
    }

    // {{{ _encodeOutput(&data)

    /**
     * Encodes/scrambles configuration data before writing to files.
     * Currently, 'password' values will be base64-encoded as to avoid
     * that people spot cleartext passwords by accident.
     *
     * @param array (reference) array to encode values in
     *
     * @return bool TRUE on success
     *
     * @access private
     */
    function _encodeOutput(&$data)
    {
        foreach ($data as $key => $value) {
            if ($key == '__channels') {
                foreach ($data['__channels'] as $channel => $blah) {
                    $this->_encodeOutput($data['__channels'][$channel]);
                }
            }
            if (!isset($this->configuration_info[$key])) {
                continue;
            }
            $type = $this->configuration_info[$key]['type'];
            switch ($type) {
                // we base64-encode passwords so they are at least
                // not shown in plain by accident
                case 'password': {
                    $data[$key] = base64_encode($data[$key]);
                    break;
                }
                case 'mask': {
                    $data[$key] = octdec($data[$key]);
                    break;
                }
            }
        }
        return true;
    }

    // }}}
    // {{{ _decodeInput(&data)

    /**
     * Decodes/unscrambles configuration data after reading from files.
     *
     * @param array (reference) array to encode values in
     *
     * @return bool TRUE on success
     *
     * @access private
     *
     * @see PEAR_Config::_encodeOutput
     */
    function _decodeInput(&$data)
    {
        if (!is_array($data)) {
            return true;
        }
        foreach ($data as $key => $value) {
            if ($key == '__channels') {
                foreach ($data['__channels'] as $channel => $blah) {
                    $this->_decodeInput($data['__channels'][$channel]);
                }
            }
            if (!isset($this->configuration_info[$key])) {
                continue;
            }
            $type = $this->configuration_info[$key]['type'];
            switch ($type) {
                case 'password': {
                    $data[$key] = base64_decode($data[$key]);
                    break;
                }
                case 'mask': {
                    $data[$key] = decoct($data[$key]);
                    break;
                }
            }
        }
        return true;
    }

    // }}}
    // {{{ getDefaultChannel([layer])
    /**
     * Retrieve the default channel.
     *
     * On startup, channels are not initialized, so if the default channel is not
     * pear.php.net, then initialize the config.
     * @param string registry layer
     * @return string|false
     */
    function getDefaultChannel($layer = null)
    {
        $ret = false;
        if ($layer === null) {
            foreach ($this->layers as $layer) {
                if (isset($this->configuration[$layer]['default_channel'])) {
                    $ret = $this->configuration[$layer]['default_channel'];
                    break;
                }
            }
        } elseif (isset($this->configuration[$layer]['default_channel'])) {
            $ret = $this->configuration[$layer]['default_channel'];
        }
        if ($ret == 'pear.php.net' && defined('PEAR_RUNTYPE') && PEAR_RUNTYPE == 'pecl') {
            $ret = 'pecl.php.net';
        }
        if ($ret) {
            if ($ret != 'pear.php.net') {
                $this->_lazyChannelSetup();
            }
            return $ret;
        }
        return PEAR_CONFIG_DEFAULT_CHANNEL;
    }

    // {{{ get(key, [layer])
    /**
     * Returns a configuration value, prioritizing layers as per the
     * layers property.
     *
     * @param string config key
     *
     * @return mixed the config value, or NULL if not found
     *
     * @access public
     */
    function get($key, $layer = null, $channel = false)
    {
        if (!isset($this->configuration_info[$key])) {
            return null;
        }
        if ($key == '__channels') {
            return null;
        }
        if ($key == 'default_channel') {
            return $this->getDefaultChannel($layer);
        }
        if (!$channel) {
            $channel = $this->getDefaultChannel();
        } elseif ($channel != 'pear.php.net') {
            $this->_lazyChannelSetup();
        }
        $channel = strtolower($channel);
        
        $test = (in_array($key, $this->_channelConfigInfo)) ?
            $this->_getChannelValue($key, $layer, $channel) :
            null;
        if ($test !== null) {
            if ($this->_installRoot) {
                if (in_array($this->getGroup($key),
                      array('File Locations', 'File Locations (Advanced)')) &&
                      $this->getType($key) == 'directory') {
                    return $this->_prependPath($test, $this->_installRoot);
                }
            }
            return $test;
        }
        if ($layer === null) {
            foreach ($this->layers as $layer) {
                if (isset($this->configuration[$layer][$key])) {
                    $test = $this->configuration[$layer][$key];
                    if ($this->_installRoot) {
                        if (in_array($this->getGroup($key),
                              array('File Locations', 'File Locations (Advanced)')) &&
                              $this->getType($key) == 'directory') {
                            return $this->_prependPath($test, $this->_installRoot);
                        }
                    }
                    if ($key == 'preferred_mirror') {
                        $reg = &$this->getRegistry();
                        if (is_object($reg)) {
                            $chan = &$reg->getChannel($channel);
                            if (PEAR::isError($chan)) {
                                return $channel;
                            }
                            if (!$chan->getMirror($test) && $chan->getName() != $test) {
                                return $channel; // mirror does not exist
                            }
                        }
                    }
                    return $test;
                }
            }
        } elseif (isset($this->configuration[$layer][$key])) {
            $test = $this->configuration[$layer][$key];
            if ($this->_installRoot) {
                if (in_array($this->getGroup($key),
                      array('File Locations', 'File Locations (Advanced)')) &&
                      $this->getType($key) == 'directory') {
                    return $this->_prependPath($test, $this->_installRoot);
                }
            }
            if ($key == 'preferred_mirror') {
                $reg = &$this->getRegistry();
                if (is_object($reg)) {
                    $chan = &$reg->getChannel($channel);
                    if (PEAR::isError($chan)) {
                        return $channel;
                    }
                    if (!$chan->getMirror($test) && $chan->getName() != $test) {
                        return $channel; // mirror does not exist
                    }
                }
            }
            return $test;
        }
        return null;
    }

    // }}}
    // {{{ _getChannelValue(key, value, [layer])
    /**
     * Returns a channel-specific configuration value, prioritizing layers as per the
     * layers property.
     *
     * @param string config key
     *
     * @return mixed the config value, or NULL if not found
     *
     * @access private
     */
    function _getChannelValue($key, $layer, $channel)
    {
        if ($key == '__channels' || $channel == 'pear.php.net') {
            return null;
        }
        $ret = null;
        if ($layer === null) {
            foreach ($this->layers as $ilayer) {
                if (isset($this->configuration[$ilayer]['__channels'][$channel][$key])) {
                    $ret = $this->configuration[$ilayer]['__channels'][$channel][$key];
                    break;
                }
            }
        } elseif (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
            $ret = $this->configuration[$layer]['__channels'][$channel][$key];
        }
        if ($key == 'preferred_mirror') {
            if ($ret !== null) {
                $reg = &$this->getRegistry($layer);
                if (is_object($reg)) {
                    $chan = &$reg->getChannel($channel);
                    if (PEAR::isError($chan)) {
                        return $channel;
                    }
                    if (!$chan->getMirror($ret) && $chan->getName() != $ret) {
                        return $channel; // mirror does not exist
                    }
                }
                return $ret;
            }
            if ($channel != $this->getDefaultChannel($layer)) {
                return $channel; // we must use the channel name as the preferred mirror
                                 // if the user has not chosen an alternate
            } else {
                return $this->getDefaultChannel($layer);
            }
        }
        return $ret;
    }


    // }}}
    // {{{ set(key, value, [layer])

    /**
     * Set a config value in a specific layer (defaults to 'user').
     * Enforces the types defined in the configuration_info array.  An
     * integer config variable will be cast to int, and a set config
     * variable will be validated against its legal values.
     *
     * @param string config key
     * @param string config value
     * @param string (optional) config layer
     * @param string channel to set this value for, or null for global value
     * @return bool TRUE on success, FALSE on failure
     */
    function set($key, $value, $layer = 'user', $channel = false)
    {
        if ($key == '__channels') {
            return false;
        }
        if (!isset($this->configuration[$layer])) {
            return false;
        }
        if ($key == 'default_channel') {
            // can only set this value globally
            $channel = 'pear.php.net';
            if ($value != 'pear.php.net') {
                $this->_lazyChannelSetup($layer);
            }
        }
        if ($key == 'preferred_mirror') {
            if ($channel == '__uri') {
                return false; // can't set the __uri pseudo-channel's mirror
            }
            $reg = &$this->getRegistry($layer);
            if (is_object($reg)) {
                $chan = &$reg->getChannel($channel ? $channel : 'pear.php.net');
                if (PEAR::isError($chan)) {
                    return false;
                }
                if (!$chan->getMirror($value) && $chan->getName() != $value) {
                    return false; // mirror does not exist
                }
            }
        }
        if (!isset($this->configuration_info[$key])) {
            return false;
        }
        extract($this->configuration_info[$key]);
        switch ($type) {
            case 'integer':
                $value = (int)$value;
                break;
            case 'set': {
                // If a valid_set is specified, require the value to
                // be in the set.  If there is no valid_set, accept
                // any value.
                if ($valid_set) {
                    reset($valid_set);
                    if ((key($valid_set) === 0 && !in_array($value, $valid_set)) ||
                        (key($valid_set) !== 0 && empty($valid_set[$value])))
                    {
                        return false;
                    }
                }
                break;
            }
        }
        if (!$channel) {
            $channel = $this->get('default_channel', null, 'pear.php.net');
        }
        if (!in_array($channel, $this->_channels)) {
            $this->_lazyChannelSetup($layer);
            $reg = &$this->getRegistry($layer);
            if ($reg) {
                $channel = $reg->channelName($channel);
            }
            if (!in_array($channel, $this->_channels)) {
                return false;
            }
        }
        if ($channel != 'pear.php.net') {
            if (in_array($key, $this->_channelConfigInfo)) {
                $this->configuration[$layer]['__channels'][$channel][$key] = $value;
                return true;
            } else {
                return false;
            }
        } else {
            if ($key == 'default_channel') {
                if (!isset($reg)) {
                    $reg = &$this->getRegistry($layer);
                    if (!$reg) {
                        $reg = &$this->getRegistry();
                    }
                }
                if ($reg) {
                    $value = $reg->channelName($value);
                }
                if (!$value) {
                    return false;
                }
            }
        }
        $this->configuration[$layer][$key] = $value;
        if ($key == 'php_dir' && !$this->_noRegistry) {
            if (!isset($this->_registry[$layer]) ||
                  $value != $this->_registry[$layer]->install_dir) {
                $this->_registry[$layer] = &new PEAR_Registry($value);
                $this->_regInitialized[$layer] = false;
                $this->_registry[$layer]->setConfig($this, false);
            }
        }
        return true;
    }

    // }}}
    function _lazyChannelSetup($uselayer = false)
    {
        if ($this->_noRegistry) {
            return;
        }
        $merge = false;
        foreach ($this->_registry as $layer => $p) {
            if ($uselayer && $uselayer != $layer) {
                continue;
            }
            if (!$this->_regInitialized[$layer]) {
                if ($layer == 'default' && isset($this->_registry['user']) ||
                      isset($this->_registry['system'])) {
                    // only use the default registry if there are no alternatives
                    continue;
                }
                if (!is_object($this->_registry[$layer])) {
                    if ($phpdir = $this->get('php_dir', $layer, 'pear.php.net')) {
                        $this->_registry[$layer] = &new PEAR_Registry($phpdir);
                        $this->_registry[$layer]->setConfig($this, false);
                        $this->_regInitialized[$layer] = false;
                    } else {
                        unset($this->_registry[$layer]);
                        return;
                    }
                }
                $this->setChannels($this->_registry[$layer]->listChannels(), $merge);
                $this->_regInitialized[$layer] = true;
                $merge = true;
            }
        }
    }
    // {{{ setChannels()
    
    /**
     * Set the list of channels.
     *
     * This should be set via a call to {@link PEAR_Registry::listChannels()}
     * @param array
     * @param bool
     * @return bool success of operation
     */
    function setChannels($channels, $merge = false)
    {
        if (!is_array($channels)) {
            return false;
        }
        if ($merge) {
            $this->_channels = array_merge($this->_channels, $channels);
        } else {
            $this->_channels = $channels;
        }
        foreach ($channels as $channel) {
            $channel = strtolower($channel);
            if ($channel == 'pear.php.net') {
                continue;
            }
            foreach ($this->layers as $layer) {
                if (!isset($this->configuration[$layer]['__channels'])) {
                    $this->configuration[$layer]['__channels'] = array();
                }
                if (!isset($this->configuration[$layer]['__channels'][$channel])
                      || !is_array($this->configuration[$layer]['__channels'][$channel])) {
                    $this->configuration[$layer]['__channels'][$channel] = array();
                }
            }
        }
        return true;
    }

    // }}}
    // {{{ getType(key)

    /**
     * Get the type of a config value.
     *
     * @param string  config key
     *
     * @return string type, one of "string", "integer", "file",
     * "directory", "set" or "password".
     *
     * @access public
     *
     */
    function getType($key)
    {
        if (isset($this->configuration_info[$key])) {
            return $this->configuration_info[$key]['type'];
        }
        return false;
    }

    // }}}
    // {{{ getDocs(key)

    /**
     * Get the documentation for a config value.
     *
     * @param string  config key
     *
     * @return string documentation string
     *
     * @access public
     *
     */
    function getDocs($key)
    {
        if (isset($this->configuration_info[$key])) {
            return $this->configuration_info[$key]['doc'];
        }
        return false;
    }
       // }}}
    // {{{ getPrompt(key)

    /**
     * Get the short documentation for a config value.
     *
     * @param string  config key
     *
     * @return string short documentation string
     *
     * @access public
     *
     */
    function getPrompt($key)
    {
        if (isset($this->configuration_info[$key])) {
            return $this->configuration_info[$key]['prompt'];
        }
        return false;
    }
    // }}}
    // {{{ getGroup(key)

    /**
     * Get the parameter group for a config key.
     *
     * @param string  config key
     *
     * @return string parameter group
     *
     * @access public
     *
     */
    function getGroup($key)
    {
        if (isset($this->configuration_info[$key])) {
            return $this->configuration_info[$key]['group'];
        }
        return false;
    }

    // }}}
    // {{{ getGroups()

    /**
     * Get the list of parameter groups.
     *
     * @return array list of parameter groups
     *
     * @access public
     *
     */
    function getGroups()
    {
        $tmp = array();
        foreach ($this->configuration_info as $key => $info) {
            $tmp[$info['group']] = 1;
        }
        return array_keys($tmp);
    }

    // }}}
    // {{{ getGroupKeys()

    /**
     * Get the list of the parameters in a group.
     *
     * @param string $group parameter group
     *
     * @return array list of parameters in $group
     *
     * @access public
     *
     */
    function getGroupKeys($group)
    {
        $keys = array();
        foreach ($this->configuration_info as $key => $info) {
            if ($info['group'] == $group) {
                $keys[] = $key;
            }
        }
        return $keys;
    }

    // }}}
    // {{{ getSetValues(key)

    /**
     * Get the list of allowed set values for a config value.  Returns
     * NULL for config values that are not sets.
     *
     * @param string  config key
     *
     * @return array enumerated array of set values, or NULL if the
     *               config key is unknown or not a set
     *
     * @access public
     *
     */
    function getSetValues($key)
    {
        if (isset($this->configuration_info[$key]) &&
            isset($this->configuration_info[$key]['type']) &&
            $this->configuration_info[$key]['type'] == 'set')
        {
            $valid_set = $this->configuration_info[$key]['valid_set'];
            reset($valid_set);
            if (key($valid_set) === 0) {
                return $valid_set;
            }
            return array_keys($valid_set);
        }
        return null;
    }

    // }}}
    // {{{ getKeys()

    /**
     * Get all the current config keys.
     *
     * @return array simple array of config keys
     *
     * @access public
     */
    function getKeys()
    {
        $keys = array();
        foreach ($this->layers as $layer) {
            $test = $this->configuration[$layer];
            if (isset($test['__channels'])) {
                foreach ($test['__channels'] as $channel => $configs) {
                    $keys = array_merge($keys, $configs);
                }
            }
            unset($test['__channels']);
            $keys = array_merge($keys, $test);
        }
        return array_keys($keys);
    }

    // }}}
    // {{{ remove(key, [layer])

    /**
     * Remove the a config key from a specific config layer.
     *
     * @param string config key
     *
     * @param string (optional) config layer
     *
     * @return bool TRUE on success, FALSE on failure
     *
     * @access public
     */
    function remove($key, $layer = 'user')
    {
        $channel = $this->getDefaultChannel();
        if ($channel !== 'pear.php.net') {
            if (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
                unset($this->configuration[$layer]['__channels'][$channel][$key]);
                return true;
            }
        }
        if (isset($this->configuration[$layer][$key])) {
            unset($this->configuration[$layer][$key]);
            return true;
        }
        return false;
    }

    // }}}
    // {{{ removeLayer(layer)

    /**
     * Temporarily remove an entire config layer.  USE WITH CARE!
     *
     * @param string config key
     *
     * @param string (optional) config layer
     *
     * @return bool TRUE on success, FALSE on failure
     *
     * @access public
     */
    function removeLayer($layer)
    {
        if (isset($this->configuration[$layer])) {
            $this->configuration[$layer] = array();
            return true;
        }
        return false;
    }

    // }}}
    // {{{ store([layer])

    /**
     * Stores configuration data in a layer.
     *
     * @param string config layer to store
     *
     * @return bool TRUE on success, or PEAR error on failure
     *
     * @access public
     */
    function store($layer = 'user', $data = null)
    {
        return $this->writeConfigFile(null, $layer, $data);
    }

    // }}}
    // {{{ toDefault(key)

    /**
     * Unset the user-defined value of a config key, reverting the
     * value to the system-defined one.
     *
     * @param string config key
     *
     * @return bool TRUE on success, FALSE on failure
     *
     * @access public
     */
    function toDefault($key)
    {
        trigger_error("PEAR_Config::toDefault() deprecated, use PEAR_Config::remove() instead", E_USER_NOTICE);
        return $this->remove($key, 'user');
    }

    // }}}
    // {{{ definedBy(key)

    /**
     * Tells what config layer that gets to define a key.
     *
     * @param string config key
     * @param boolean return the defining channel
     *
     * @return string|array the config layer, or an empty string if not found.
     *
     *         if $returnchannel, the return is an array array('layer' => layername,
     *         'channel' => channelname), or an empty string if not found
     *
     * @access public
     */
    function definedBy($key, $returnchannel = false)
    {
        foreach ($this->layers as $layer) {
            $channel = $this->getDefaultChannel();
            if ($channel !== 'pear.php.net') {
                if (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
                    if ($returnchannel) {
                        return array('layer' => $layer, 'channel' => $channel);
                    }
                    return $layer;
                }
            }
            if (isset($this->configuration[$layer][$key])) {
                if ($returnchannel) {
                    return array('layer' => $layer, 'channel' => 'pear.php.net');
                }
                return $layer;
            }
        }
        return '';
    }

    // }}}
    // {{{ isDefaulted(key)

    /**
     * Tells whether a config value has a system-defined value.
     *
     * @param string   config key
     *
     * @return bool
     *
     * @access public
     *
     * @deprecated
     */
    function isDefaulted($key)
    {
        trigger_error("PEAR_Config::isDefaulted() deprecated, use PEAR_Config::definedBy() instead", E_USER_NOTICE);
        return $this->definedBy($key) == 'system';
    }

    // }}}
    // {{{ isDefined(key)

    /**
     * Tells whether a given key exists as a config value.
     *
     * @param string config key
     *
     * @return bool whether <config key> exists in this object
     *
     * @access public
     */
    function isDefined($key)
    {
        foreach ($this->layers as $layer) {
            if (isset($this->configuration[$layer][$key])) {
                return true;
            }
        }
        return false;
    }

    // }}}
    // {{{ isDefinedLayer(key)

    /**
     * Tells whether a given config layer exists.
     *
     * @param string config layer
     *
     * @return bool whether <config layer> exists in this object
     *
     * @access public
     */
    function isDefinedLayer($layer)
    {
        return isset($this->configuration[$layer]);
    }

    // }}}
    // {{{ getLayers()

    /**
     * Returns the layers defined (except the 'default' one)
     *
     * @return array of the defined layers
     */
    function getLayers()
    {
        $cf = $this->configuration;
        unset($cf['default']);
        return array_keys($cf);
    }

    // }}}
    // {{{ apiVersion()
    function apiVersion()
    {
        return '1.1';
    }
    // }}}

    /**
     * @return PEAR_Registry
     */
    function &getRegistry($use = null)
    {
        if ($use === null) {
            $layer = 'user';
        } else {
            $layer = $use;
        }
        if (isset($this->_registry[$layer])) {
            return $this->_registry[$layer];
        } elseif ($use === null && isset($this->_registry['system'])) {
            return $this->_registry['system'];
        } elseif ($use === null && isset($this->_registry['default'])) {
            return $this->_registry['default'];
        } elseif ($use) {
            $a = false;
            return $a;
        } else {
            // only go here if null was passed in
            echo "CRITICAL ERROR: Registry could not be initialized from any value";
            exit(1);
        }
    }
    /**
     * This is to allow customization like the use of installroot
     * @param PEAR_Registry
     * @return bool
     */
    function setRegistry(&$reg, $layer = 'user')
    {
        if ($this->_noRegistry) {
            return false;
        }
        if (!in_array($layer, array('user', 'system'))) {
            return false;
        }
        $this->_registry[$layer] = &$reg;
        if (is_object($reg)) {
            $this->_registry[$layer]->setConfig($this, false);
        }
        return true;
    }

    function noRegistry()
    {
        $this->_noRegistry = true;
    }

    /**
     * @return PEAR_Remote
     */
    function &getRemote()
    {
        $remote = &new PEAR_Remote($this);
        return $remote;
    }

    /**
     * @return PEAR_REST
     */
    function &getREST($version, $options = array())
    {
        $version = str_replace('.', '', $version);
        if (!class_exists($class = 'PEAR_REST_' . $version)) {
            require_once 'PEAR/REST/' . $version . '.php';
        }
        $remote = &new $class($this, $options);
        return $remote;
    }

    /**
     * The ftp server is set in {@link readFTPConfigFile()}.  It exists only if a
     * remote configuration file has been specified
     * @return PEAR_FTP|false
     */
    function &getFTP()
    {
        if (isset($this->_ftp)) {
            return $this->_ftp;
        } else {
            $a = false;
            return $a;
        }
    }

    // {{{ _prependPath($path, $prepend)

    function _prependPath($path, $prepend)
    {
        if (strlen($prepend) > 0) {
            if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
                if (preg_match('/^[a-z]:/i', $prepend)) {
                    $prepend = substr($prepend, 2);
                } elseif ($prepend{0} != '\\') {
                    $prepend = "\\$prepend";
                }
                $path = substr($path, 0, 2) . $prepend . substr($path, 2);
            } else {
                $path = $prepend . $path;
            }
        }
        return $path;
    }
    // }}}

    /**
     * @param string|false installation directory to prepend to all _dir variables, or false to
     *                     disable
     */
    function setInstallRoot($root)
    {
        if (substr($root, -1) == DIRECTORY_SEPARATOR) {
            $root = substr($root, 0, -1);
        }
        $old = $this->_installRoot;
        $this->_installRoot = $root;
        if (($old != $root) && !$this->_noRegistry) {
            foreach (array_keys($this->_registry) as $layer) {
                if ($layer == 'ftp' || !isset($this->_registry[$layer])) {
                    continue;
                }
                $this->_registry[$layer] =
                    &new PEAR_Registry($this->get('php_dir', $layer, 'pear.php.net'));
                $this->_registry[$layer]->setConfig($this, false);
                $this->_regInitialized[$layer] = false;
            }
        }
    }
}

?>
PEAR-1.7.2/PEAR/Dependency.php100644   1750   1750       41343 11014312742  11030 <?php
/**
 * PEAR_Dependency
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * THIS FILE IS DEPRECATED IN FAVOR OF DEPENDENCY2.PHP, AND IS NOT USED IN THE INSTALLER
 * 
 * @category   pear
 * @package    PEAR
 * @author     Tomas V.V.Cox <cox@idecnet.com>
 * @author     Stig Bakken <ssb@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Dependency.php,v 1.43 2008/01/03 20:26:34 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

require_once "PEAR.php";
require_once "OS/Guess.php";

define('PEAR_DEPENDENCY_MISSING',        -1);
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);
define('PEAR_DEPENDENCY_CONFLICT_OPTIONAL',       -7);
define('PEAR_DEPENDENCY_UPGRADE_MINOR_OPTIONAL',  -8);
define('PEAR_DEPENDENCY_UPGRADE_MAJOR_OPTIONAL',  -9);

/**
 * Dependency check for PEAR packages
 *
 * The class is based on the dependency RFC that can be found at
 * http://cvs.php.net/cvs.php/pearweb/rfc. It requires PHP >= 4.1
 *
 * @author Tomas V.V.Vox <cox@idecnet.com>
 * @author Stig Bakken <ssb@php.net>
 */
class PEAR_Dependency
{
    // {{{ constructor
    /**
     * Constructor
     *
     * @access public
     * @param  object Registry object
     * @return void
     */
    function PEAR_Dependency(&$registry)
    {
        $this->registry = &$registry;
    }

    // }}}
    // {{{ callCheckMethod()

    /**
    * 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
    * @return boolean
    */
    function callCheckMethod(&$errmsg, $opts)
    {
        $rel = isset($opts['rel']) ? $opts['rel'] : 'has';
        $req = isset($opts['version']) ? $opts['version'] : null;
        $name = isset($opts['name']) ? $opts['name'] : null;
        $channel = isset($opts['channel']) ? $opts['channel'] : 'pear.php.net';
        $opt = (isset($opts['optional']) && $opts['optional'] == 'yes') ?
            $opts['optional'] : null;
        $errmsg = '';
        switch ($opts['type']) {
            case 'pkg':
                return $this->checkPackage($errmsg, $name, $req, $rel, $opt, $channel);
                break;
            case 'ext':
                return $this->checkExtension($errmsg, $name, $req, $rel, $opt);
                break;
            case 'php':
                return $this->checkPHP($errmsg, $req, $rel);
                break;
            case 'prog':
                return $this->checkProgram($errmsg, $name);
                break;
            case 'os':
                return $this->checkOS($errmsg, $name);
                break;
            case 'sapi':
                return $this->checkSAPI($errmsg, $name);
                break;
            case 'zend':
                return $this->checkZend($errmsg, $name);
                break;
            default:
                return "'{$opts['type']}' dependency type not supported";
        }
    }

    // }}}
    // {{{ checkPackage()

    /**
     * Package dependencies check method
     *
     * @param string $errmsg    Empty string, it will be populated with an error message, if any
     * @param string $name      Name of the package to test
     * @param string $req       The package version required
     * @param string $relation  How to compare versions with each other
     * @param bool   $opt       Whether the relationship is optional
     * @param string $channel   Channel name
     *
     * @return mixed bool false if no error or the error string
     */
    function checkPackage(&$errmsg, $name, $req = null, $relation = 'has',
                          $opt = false, $channel = 'pear.php.net')
    {
        if (is_string($req) && substr($req, 0, 2) == 'v.') {
            $req = substr($req, 2);
        }
        switch ($relation) {
            case 'has':
                if (!$this->registry->packageExists($name, $channel)) {
                    if ($opt) {
                        $errmsg = "package `$channel/$name' is recommended to utilize some features.";
                        return PEAR_DEPENDENCY_MISSING_OPTIONAL;
                    }
                    $errmsg = "requires package `$channel/$name'";
                    return PEAR_DEPENDENCY_MISSING;
                }
                return false;
            case 'not':
                if ($this->registry->packageExists($name, $channel)) {
                    $errmsg = "conflicts with package `$channel/$name'";
                    return PEAR_DEPENDENCY_CONFLICT;
                }
                return false;
            case 'lt':
            case 'le':
            case 'eq':
            case 'ne':
            case 'ge':
            case 'gt':
                $version = $this->registry->packageInfo($name, 'version', $channel);
                if (!$this->registry->packageExists($name, $channel)
                    || !version_compare("$version", "$req", $relation))
                {
                    $code = $this->codeFromRelation($relation, $version, $req, $opt);
                    if ($opt) {
                        $errmsg = "package `$channel/$name' version " . $this->signOperator($relation) .
                            " $req is recommended to utilize some features.";
                        if ($version) {
                            $errmsg .= "  Installed version is $version";
                        }
                        return $code;
                    }
                    $errmsg = "requires package `$channel/$name' " .
                        $this->signOperator($relation) . " $req";
                    return $code;
                }
                return false;
        }
        $errmsg = "relation '$relation' with requirement '$req' is not supported (name=$channel/$name)";
        return PEAR_DEPENDENCY_BAD_DEPENDENCY;
    }

    // }}}
    // {{{ checkPackageUninstall()

    /**
     * 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
     * @param string $channel   Channel name of the package
     *
     * @return bool true if there were errors
     */
    function checkPackageUninstall(&$error, &$warning, $package, $channel = 'pear.php.net')
    {
        $channel = strtolower($channel);
        $error = null;
        $channels = $this->registry->listAllPackages();
        foreach ($channels as $channelname => $packages) {
            foreach ($packages as $pkg) {
                if ($pkg == $package && $channel == $channelname) {
                    continue;
                }
                $deps = $this->registry->packageInfo($pkg, 'release_deps', $channel);
                if (empty($deps)) {
                    continue;
                }
                foreach ($deps as $dep) {
                    $depchannel = isset($dep['channel']) ? $dep['channel'] : 'pear.php.net';
                    if ($dep['type'] == 'pkg' && (strcasecmp($dep['name'], $package) == 0) &&
                          ($depchannel == $channel)) {
                        if ($dep['rel'] == 'ne') {
                            continue;
                        }
                        if (isset($dep['optional']) && $dep['optional'] == 'yes') {
                            $warning .= "\nWarning: Package '$depchannel/$pkg' optionally depends on '$channel:/package'";
                        } else {
                            $error .= "Package '$depchannel/$pkg' depends on '$channel/$package'\n";
                        }
                    }
                }
            }
        }
        return ($error) ? true : false;
    }

    // }}}
    // {{{ checkExtension()

    /**
     * Extension dependencies check method
     *
     * @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',
        $opt = false)
    {
        if ($relation == 'not') {
            if (extension_loaded($name)) {
                $errmsg = "conflicts with  PHP extension '$name'";
                return PEAR_DEPENDENCY_CONFLICT;
            } else {
                return false;
            }
        }

        if (!extension_loaded($name)) {
            if ($relation == 'ne') {
                return false;
            }
            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;
        }
        if ($relation == 'has') {
            return false;
        }
        $code = false;
        if (is_string($req) && substr($req, 0, 2) == 'v.') {
            $req = substr($req, 2);
        }
        $ext_ver = phpversion($name);
        $operator = $relation;
        // Force params to be strings, otherwise the comparation will fail (ex. 0.9==0.90)
        if (!version_compare("$ext_ver", "$req", $operator)) {
            $errmsg = "'$name' PHP extension version " .
                $this->signOperator($operator) . " $req is required";
            $code = $this->codeFromRelation($relation, $ext_ver, $req, $opt);
            if ($opt) {
                $errmsg = "'$name' PHP extension version " . $this->signOperator($operator) .
                    " $req is recommended to utilize some features";
                return $code;
            }
        }
        return $code;
    }

    // }}}
    // {{{ checkOS()

    /**
     * Operating system  dependencies check method
     *
     * @param string $os  Name of the operating system
     *
     * @return mixed bool false if no error or the error string
     */
    function checkOS(&$errmsg, $os)
    {
        // XXX Fixme: Implement a more flexible way, like
        // comma separated values or something similar to PEAR_OS
        static $myos;
        if (empty($myos)) {
            $myos = new OS_Guess();
        }
        // only 'has' relation is currently supported
        if ($myos->matchSignature($os)) {
            return false;
        }
        $errmsg = "'$os' operating system not supported";
        return PEAR_DEPENDENCY_CONFLICT;
    }

    // }}}
    // {{{ checkPHP()

    /**
     * PHP version check method
     *
     * @param string $req   which version to compare
     * @param string $relation  how to compare the version
     *
     * @return mixed bool false if no error or the error string
     */
    function checkPHP(&$errmsg, $req, $relation = 'ge')
    {
        // this would be a bit stupid, but oh well :)
        if ($relation == 'has') {
            return false;
        }
        if ($relation == 'not') {
            $errmsg = "Invalid dependency - 'not' is allowed when specifying PHP, you must run PHP in PHP";
            return PEAR_DEPENDENCY_BAD_DEPENDENCY;
        }
        if (substr($req, 0, 2) == 'v.') {
            $req = substr($req,2, strlen($req) - 2);
        }
        $php_ver = phpversion();
        $operator = $relation;
        if (!version_compare("$php_ver", "$req", $operator)) {
            $errmsg = "PHP version " . $this->signOperator($operator) .
                " $req is required";
            return PEAR_DEPENDENCY_CONFLICT;
        }
        return false;
    }

    // }}}
    // {{{ checkProgram()

    /**
     * External program check method.  Looks for executable files in
     * directories listed in the PATH environment variable.
     *
     * @param string $program   which program to look for
     *
     * @return mixed bool false if no error or the error string
     */
    function checkProgram(&$errmsg, $program)
    {
        // XXX FIXME honor safe mode
        $exe_suffix = OS_WINDOWS ? '.exe' : '';
        $path_elements = explode(PATH_SEPARATOR, getenv('PATH'));
        foreach ($path_elements as $dir) {
            $file = $dir . DIRECTORY_SEPARATOR . $program . $exe_suffix;
            if (file_exists($file) && is_executable($file)) {
                return false;
            }
        }
        $errmsg = "'$program' program is not present in the PATH";
        return PEAR_DEPENDENCY_MISSING;
    }

    // }}}
    // {{{ checkSAPI()

    /**
     * SAPI backend check method.  Version comparison is not yet
     * available here.
     *
     * @param string $name      name of SAPI backend
     * @param string $req   which version to compare
     * @param string $relation  how to compare versions (currently
     *                          hardcoded to 'has')
     * @return mixed bool false if no error or the error string
     */
    function checkSAPI(&$errmsg, $name, $req = null, $relation = 'has')
    {
        // XXX Fixme: There is no way to know if the user has or
        // not other SAPI backends installed than the installer one

        $sapi_backend = php_sapi_name();
        // Version comparisons not supported, sapi backends don't have
        // version information yet.
        if ($sapi_backend == $name) {
            return false;
        }
        $errmsg = "'$sapi_backend' SAPI backend not supported";
        return PEAR_DEPENDENCY_CONFLICT;
    }

    // }}}
    // {{{ checkZend()

    /**
     * Zend version check method
     *
     * @param string $req   which version to compare
     * @param string $relation  how to compare the version
     *
     * @return mixed bool false if no error or the error string
     */
    function checkZend(&$errmsg, $req, $relation = 'ge')
    {
        if (substr($req, 0, 2) == 'v.') {
            $req = substr($req,2, strlen($req) - 2);
        }
        $zend_ver = zend_version();
        $operator = substr($relation,0,2);
        if (!version_compare("$zend_ver", "$req", $operator)) {
            $errmsg = "Zend version " . $this->signOperator($operator) .
                " $req is required";
            return PEAR_DEPENDENCY_CONFLICT;
        }
        return false;
    }

    // }}}
    // {{{ signOperator()

    /**
     * Converts text comparing operators to them sign equivalents
     *
     * Example: 'ge' to '>='
     *
     * @access public
     * @param  string Operator
     * @return string Sign equivalent
     */
    function signOperator($operator)
    {
        switch($operator) {
            case 'lt': return '<';
            case 'le': return '<=';
            case 'gt': return '>';
            case 'ge': return '>=';
            case 'eq': return '==';
            case 'ne': return '!=';
            default:
                return $operator;
        }
    }

    // }}}
    // {{{ codeFromRelation()

    /**
     * Convert relation into corresponding code
     *
     * @access public
     * @param  string Relation
     * @param  string Version
     * @param  string Requirement
     * @param  bool   Optional dependency indicator
     * @return integer
     */
    function codeFromRelation($relation, $version, $req, $opt = false)
    {
        $code = PEAR_DEPENDENCY_BAD_DEPENDENCY;
        switch ($relation) {
            case 'gt': case 'ge': case 'eq':
                // upgrade
                $have_major = preg_replace('/\D.*/', '', $version);
                $need_major = preg_replace('/\D.*/', '', $req);
                if ($need_major > $have_major) {
                    $code = $opt ? PEAR_DEPENDENCY_UPGRADE_MAJOR_OPTIONAL :
                                   PEAR_DEPENDENCY_UPGRADE_MAJOR;
                } else {
                    $code = $opt ? PEAR_DEPENDENCY_UPGRADE_MINOR_OPTIONAL :
                                   PEAR_DEPENDENCY_UPGRADE_MINOR;
                }
                break;
            case 'lt': case 'le': case 'ne':
                $code = $opt ? PEAR_DEPENDENCY_CONFLICT_OPTIONAL :
                               PEAR_DEPENDENCY_CONFLICT;
                break;
        }
        return $code;
    }

    // }}}
}
?>
PEAR-1.7.2/PEAR/DependencyDB.php100644   1750   1750       62131 11014312742  11234 <?php
/**
 * PEAR_DependencyDB, advanced installed packages dependency database
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Tomas V. V. Cox <cox@idecnet.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: DependencyDB.php,v 1.37 2008/01/03 20:26:35 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * Needed for error handling
 */
require_once 'PEAR.php';
require_once 'PEAR/Config.php';

$GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'] = array();
/**
 * Track dependency relationships between installed packages
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @author     Tomas V.V.Cox <cox@idec.net.com>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_DependencyDB
{
    // {{{ properties

    /**
     * This is initialized by {@link setConfig()}
     * @var PEAR_Config
     * @access private
     */
    var $_config;
    /**
     * This is initialized by {@link setConfig()}
     * @var PEAR_Registry
     * @access private
     */
    var $_registry;
    /**
     * Filename of the dependency DB (usually .depdb)
     * @var string
     * @access private
     */
    var $_depdb = false;
    /**
     * File name of the lockfile (usually .depdblock)
     * @var string
     * @access private
     */
    var $_lockfile = false;
    /**
     * Open file resource for locking the lockfile
     * @var resource|false
     * @access private
     */
    var $_lockFp = false;
    /**
     * API version of this class, used to validate a file on-disk
     * @var string
     * @access private
     */
    var $_version = '1.0';
    /**
     * Cached dependency database file
     * @var array|null
     * @access private
     */
    var $_cache;

    // }}}
    // {{{ & singleton()

    /**
     * Get a raw dependency database.  Calls setConfig() and assertDepsDB()
     * @param PEAR_Config
     * @param string|false full path to the dependency database, or false to use default
     * @return PEAR_DependencyDB|PEAR_Error
     * @static
     */
    function &singleton(&$config, $depdb = false)
    {
        if (!isset($GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE']
              [$config->get('php_dir', null, 'pear.php.net')])) {
            $a = new PEAR_DependencyDB;
            $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE']
              [$config->get('php_dir', null, 'pear.php.net')] = &$a;
            $a->setConfig($config, $depdb);
            if (PEAR::isError($e = $a->assertDepsDB())) {
                return $e;
            }
        }
        return $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE']
              [$config->get('php_dir', null, 'pear.php.net')];
    }

    /**
     * Set up the registry/location of dependency DB
     * @param PEAR_Config|false
     * @param string|false full path to the dependency database, or false to use default
     */
    function setConfig(&$config, $depdb = false)
    {
        if (!$config) {
            $this->_config = &PEAR_Config::singleton();
        } else {
            $this->_config = &$config;
        }
        $this->_registry = &$this->_config->getRegistry();
        if (!$depdb) {
            $this->_depdb = $this->_config->get('php_dir', null, 'pear.php.net') .
                DIRECTORY_SEPARATOR . '.depdb';
        } else {
            $this->_depdb = $depdb;
        }
        $this->_lockfile = dirname($this->_depdb) . DIRECTORY_SEPARATOR . '.depdblock';
    }
    // }}}

    function hasWriteAccess()
    {
        if (!file_exists($this->_depdb)) {
            $dir = $this->_depdb;
            while ($dir && $dir != '.') {
                $dir = dirname($dir); // cd ..
                if ($dir != '.' && file_exists($dir)) {
                    if (is_writeable($dir)) {
                        return true;
                    } else {
                        return false;
                    }
                }
            }
            return false;
        }
        return is_writeable($this->_depdb);
    }

    // {{{ assertDepsDB()

    /**
     * Create the dependency database, if it doesn't exist.  Error if the database is
     * newer than the code reading it.
     * @return void|PEAR_Error
     */
    function assertDepsDB()
    {
        if (!is_file($this->_depdb)) {
            $this->rebuildDB();
        } else {
            $depdb = $this->_getDepDB();
            // Datatype format has been changed, rebuild the Deps DB
            if ($depdb['_version'] < $this->_version) {
                $this->rebuildDB();
            }
            if ($depdb['_version']{0} > $this->_version{0}) {
                return PEAR::raiseError('Dependency database is version ' .
                    $depdb['_version'] . ', and we are version ' .
                    $this->_version . ', cannot continue');
            }
        }
    }

    /**
     * Get a list of installed packages that depend on this package
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
     * @return array|false
     */
    function getDependentPackages(&$pkg)
    {
        $data = $this->_getDepDB();
        if (is_object($pkg)) {
            $channel = strtolower($pkg->getChannel());
            $package = strtolower($pkg->getPackage());
        } else {
            $channel = strtolower($pkg['channel']);
            $package = strtolower($pkg['package']);
        }
        if (isset($data['packages'][$channel][$package])) {
            return $data['packages'][$channel][$package];
        }
        return false;
    }

    /**
     * Get a list of the actual dependencies of installed packages that depend on
     * a package.
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
     * @return array|false
     */
    function getDependentPackageDependencies(&$pkg)
    {
        $data = $this->_getDepDB();
        if (is_object($pkg)) {
            $channel = strtolower($pkg->getChannel());
            $package = strtolower($pkg->getPackage());
        } else {
            $channel = strtolower($pkg['channel']);
            $package = strtolower($pkg['package']);
        }
        $depend = $this->getDependentPackages($pkg);
        if (!$depend) {
            return false;
        }
        $dependencies = array();
        foreach ($depend as $info) {
            $temp = $this->getDependencies($info);
            foreach ($temp as $dep) {
                if (strtolower($dep['dep']['channel']) == strtolower($channel) &&
                      strtolower($dep['dep']['name']) == strtolower($package)) {
                    if (!isset($dependencies[$info['channel']])) {
                        $dependencies[$info['channel']] = array();
                    }
                    if (!isset($dependencies[$info['channel']][$info['package']])) {
                        $dependencies[$info['channel']][$info['package']] = array();
                    }
                    $dependencies[$info['channel']][$info['package']][] = $dep;
                }
            }
        }
        return $dependencies;
    }

    /**
     * Get a list of dependencies of this installed package
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
     * @return array|false
     */
    function getDependencies(&$pkg)
    {
        if (is_object($pkg)) {
            $channel = strtolower($pkg->getChannel());
            $package = strtolower($pkg->getPackage());
        } else {
            $channel = strtolower($pkg['channel']);
            $package = strtolower($pkg['package']);
        }
        $data = $this->_getDepDB();
        if (isset($data['dependencies'][$channel][$package])) {
            return $data['dependencies'][$channel][$package];
        }
        return false;
    }

    /**
     * Determine whether $parent depends on $child, near or deep
     * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2
     * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2
     */
    function dependsOn($parent, $child)
    {
        $c = array();
        $this->_getDepDB();
        return $this->_dependsOn($parent, $child, $c);
    }
    
    function _dependsOn($parent, $child, &$checked)
    {
        if (is_object($parent)) {
            $channel = strtolower($parent->getChannel());
            $package = strtolower($parent->getPackage());
        } else {
            $channel = strtolower($parent['channel']);
            $package = strtolower($parent['package']);
        }
        if (is_object($child)) {
            $depchannel = strtolower($child->getChannel());
            $deppackage = strtolower($child->getPackage());
        } else {
            $depchannel = strtolower($child['channel']);
            $deppackage = strtolower($child['package']);
        }
        if (isset($checked[$channel][$package][$depchannel][$deppackage])) {
            return false; // avoid endless recursion
        }
        $checked[$channel][$package][$depchannel][$deppackage] = true;
        if (!isset($this->_cache['dependencies'][$channel][$package])) {
            return false;
        }
        foreach ($this->_cache['dependencies'][$channel][$package] as $info) {
            if (isset($info['dep']['uri'])) {
                if (is_object($child)) {
                    if ($info['dep']['uri'] == $child->getURI()) {
                        return true;
                    }
                } elseif (isset($child['uri'])) {
                    if ($info['dep']['uri'] == $child['uri']) {
                        return true;
                    }
                }
                return false;
            }
            if (strtolower($info['dep']['channel']) == strtolower($depchannel) &&
                  strtolower($info['dep']['name']) == strtolower($deppackage)) {
                return true;
            }
        }
        foreach ($this->_cache['dependencies'][$channel][$package] as $info) {
            if (isset($info['dep']['uri'])) {
                if ($this->_dependsOn(array(
                        'uri' => $info['dep']['uri'],
                        'package' => $info['dep']['name']), $child, $checked)) {
                    return true;
                }
            } else {
                if ($this->_dependsOn(array(
                        'channel' => $info['dep']['channel'],
                        'package' => $info['dep']['name']), $child, $checked)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Register dependencies of a package that is being installed or upgraded
     * @param PEAR_PackageFile_v2|PEAR_PackageFile_v2
     */
    function installPackage(&$package)
    {
        $data = $this->_getDepDB();
        unset($this->_cache);
        $this->_setPackageDeps($data, $package);
        $this->_writeDepDB($data);
    }

    /**
     * Remove dependencies of a package that is being uninstalled, or upgraded.
     *
     * Upgraded packages first uninstall, then install
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array If an array, then it must have
     *        indices 'channel' and 'package'
     */
    function uninstallPackage(&$pkg)
    {
        $data = $this->_getDepDB();
        unset($this->_cache);
        if (is_object($pkg)) {
            $channel = strtolower($pkg->getChannel());
            $package = strtolower($pkg->getPackage());
        } else {
            $channel = strtolower($pkg['channel']);
            $package = strtolower($pkg['package']);
        }
        if (!isset($data['dependencies'][$channel][$package])) {
            return true;
        }
        foreach ($data['dependencies'][$channel][$package] as $dep) {
            $found = false;
            if (isset($dep['dep']['uri'])) {
                $depchannel = '__uri';
            } else {
                $depchannel = strtolower($dep['dep']['channel']);
            }
            if (isset($data['packages'][$depchannel][strtolower($dep['dep']['name'])])) {
                foreach ($data['packages'][$depchannel][strtolower($dep['dep']['name'])] as
                      $i => $info) {
                    if ($info['channel'] == $channel &&
                          $info['package'] == $package) {
                        $found = true;
                        break;
                    }
                }
            }
            if ($found) {
                unset($data['packages'][$depchannel][strtolower($dep['dep']['name'])][$i]);
                if (!count($data['packages'][$depchannel][strtolower($dep['dep']['name'])])) {
                    unset($data['packages'][$depchannel][strtolower($dep['dep']['name'])]);
                    if (!count($data['packages'][$depchannel])) {
                        unset($data['packages'][$depchannel]);
                    }
                } else {
                    $data['packages'][$depchannel][strtolower($dep['dep']['name'])] =
                        array_values(
                            $data['packages'][$depchannel][strtolower($dep['dep']['name'])]);
                }
            }
        }
        unset($data['dependencies'][$channel][$package]);
        if (!count($data['dependencies'][$channel])) {
            unset($data['dependencies'][$channel]);
        }
        if (!count($data['dependencies'])) {
            unset($data['dependencies']);
        }
        if (!count($data['packages'])) {
            unset($data['packages']);
        }
        $this->_writeDepDB($data);
    }

    /**
     * Rebuild the dependency DB by reading registry entries.
     * @return true|PEAR_Error
     */
    function rebuildDB()
    {
        $depdb = array('_version' => $this->_version);
        if (!$this->hasWriteAccess()) {
            // allow startup for read-only with older Registry
            return $depdb;
        }
        $packages = $this->_registry->listAllPackages();
        if (PEAR::isError($packages)) {
            return $packages;
        }
        foreach ($packages as $channel => $ps) {
            foreach ($ps as $package) {
                $package = $this->_registry->getPackage($package, $channel);
                if (PEAR::isError($package)) {
                    return $package;
                }
                $this->_setPackageDeps($depdb, $package);
            }
        }
        $error = $this->_writeDepDB($depdb);
        if (PEAR::isError($error)) {
            return $error;
        }
        $this->_cache = $depdb;
        return true;
    }

    /**
     * Register usage of the dependency DB to prevent race conditions
     * @param int one of the LOCK_* constants
     * @return true|PEAR_Error
     * @access private
     */
    function _lock($mode = LOCK_EX)
    {
        if (!eregi('Windows 9', php_uname())) {
            if ($mode != LOCK_UN && is_resource($this->_lockFp)) {
                // XXX does not check type of lock (LOCK_SH/LOCK_EX)
                return true;
            }
            $open_mode = 'w';
            // XXX People reported problems with LOCK_SH and 'w'
            if ($mode === LOCK_SH) {
                if (!file_exists($this->_lockfile)) {
                    touch($this->_lockfile);
                } elseif (!is_file($this->_lockfile)) {
                    return PEAR::raiseError('could not create Dependency lock file, ' .
                        'it exists and is not a regular file');
                }
                $open_mode = 'r';
            }

            if (!is_resource($this->_lockFp)) {
                $this->_lockFp = @fopen($this->_lockfile, $open_mode);
            }
            if (!is_resource($this->_lockFp)) {
                return PEAR::raiseError("could not create Dependency lock file" .
                                         (isset($php_errormsg) ? ": " . $php_errormsg : ""));
            }
            if (!(int)flock($this->_lockFp, $mode)) {
                switch ($mode) {
                    case LOCK_SH: $str = 'shared';    break;
                    case LOCK_EX: $str = 'exclusive'; break;
                    case LOCK_UN: $str = 'unlock';    break;
                    default:      $str = 'unknown';   break;
                }
                return PEAR::raiseError("could not acquire $str lock ($this->_lockfile)");
            }
        }
        return true;
    }

    /**
     * Release usage of dependency DB
     * @return true|PEAR_Error
     * @access private
     */
    function _unlock()
    {
        $ret = $this->_lock(LOCK_UN);
        if (is_resource($this->_lockFp)) {
            fclose($this->_lockFp);
        }
        $this->_lockFp = null;
        return $ret;
    }

    /**
     * Load the dependency database from disk, or return the cache
     * @return array|PEAR_Error
     */
    function _getDepDB()
    {
        if (!$this->hasWriteAccess()) {
            return array('_version' => $this->_version);
        }
        if (isset($this->_cache)) {
            return $this->_cache;
        }
        if (!$fp = fopen($this->_depdb, 'r')) {
            $err = PEAR::raiseError("Could not open dependencies file `".$this->_depdb."'");
            return $err;
        }
        $rt = get_magic_quotes_runtime();
        set_magic_quotes_runtime(0);
        clearstatcache();
        fclose($fp);
        $data = unserialize(file_get_contents($this->_depdb));
        set_magic_quotes_runtime($rt);
        $this->_cache = $data;
        return $data;
    }

    /**
     * Write out the dependency database to disk
     * @param array the database
     * @return true|PEAR_Error
     * @access private
     */
    function _writeDepDB(&$deps)
    {
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
            return $e;
        }
        if (!$fp = fopen($this->_depdb, 'wb')) {
            $this->_unlock();
            return PEAR::raiseError("Could not open dependencies file `".$this->_depdb."' for writing");
        }
        $rt = get_magic_quotes_runtime();
        set_magic_quotes_runtime(0);
        fwrite($fp, serialize($deps));
        set_magic_quotes_runtime($rt);
        fclose($fp);
        $this->_unlock();
        $this->_cache = $deps;
        return true;
    }

    /**
     * Register all dependencies from a package in the dependencies database, in essence
     * "installing" the package's dependency information
     * @param array the database
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @access private
     */
    function _setPackageDeps(&$data, &$pkg)
    {
        $pkg->setConfig($this->_config);
        if ($pkg->getPackagexmlVersion() == '1.0') {
            $gen = &$pkg->getDefaultGenerator();
            $deps = $gen->dependenciesToV2();
        } else {
            $deps = $pkg->getDeps(true);
        }
        if (!$deps) {
            return;
        }
        if (!is_array($data)) {
            $data = array();
        }
        if (!isset($data['dependencies'])) {
            $data['dependencies'] = array();
        }
        if (!isset($data['dependencies'][strtolower($pkg->getChannel())])) {
            $data['dependencies'][strtolower($pkg->getChannel())] = array();
        }
        $data['dependencies'][strtolower($pkg->getChannel())][strtolower($pkg->getPackage())]
            = array();
        if (isset($deps['required']['package'])) {
            if (!isset($deps['required']['package'][0])) {
                $deps['required']['package'] = array($deps['required']['package']);
            }
            foreach ($deps['required']['package'] as $dep) {
                $this->_registerDep($data, $pkg, $dep, 'required');
            }
        }
        if (isset($deps['optional']['package'])) {
            if (!isset($deps['optional']['package'][0])) {
                $deps['optional']['package'] = array($deps['optional']['package']);
            }
            foreach ($deps['optional']['package'] as $dep) {
                $this->_registerDep($data, $pkg, $dep, 'optional');
            }
        }
        if (isset($deps['required']['subpackage'])) {
            if (!isset($deps['required']['subpackage'][0])) {
                $deps['required']['subpackage'] = array($deps['required']['subpackage']);
            }
            foreach ($deps['required']['subpackage'] as $dep) {
                $this->_registerDep($data, $pkg, $dep, 'required');
            }
        }
        if (isset($deps['optional']['subpackage'])) {
            if (!isset($deps['optional']['subpackage'][0])) {
                $deps['optional']['subpackage'] = array($deps['optional']['subpackage']);
            }
            foreach ($deps['optional']['subpackage'] as $dep) {
                $this->_registerDep($data, $pkg, $dep, 'optional');
            }
        }
        if (isset($deps['group'])) {
            if (!isset($deps['group'][0])) {
                $deps['group'] = array($deps['group']);
            }
            foreach ($deps['group'] as $group) {
                if (isset($group['package'])) {
                    if (!isset($group['package'][0])) {
                        $group['package'] = array($group['package']);
                    }
                    foreach ($group['package'] as $dep) {
                        $this->_registerDep($data, $pkg, $dep, 'optional',
                            $group['attribs']['name']);
                    }
                }
                if (isset($group['subpackage'])) {
                    if (!isset($group['subpackage'][0])) {
                        $group['subpackage'] = array($group['subpackage']);
                    }
                    foreach ($group['subpackage'] as $dep) {
                        $this->_registerDep($data, $pkg, $dep, 'optional',
                            $group['attribs']['name']);
                    }
                }
            }
        }
        if ($data['dependencies'][strtolower($pkg->getChannel())]
              [strtolower($pkg->getPackage())] == array()) {
            unset($data['dependencies'][strtolower($pkg->getChannel())]
              [strtolower($pkg->getPackage())]);
            if (!count($data['dependencies'][strtolower($pkg->getChannel())])) {
                unset($data['dependencies'][strtolower($pkg->getChannel())]);
            }
        }
    }

    /**
     * @param array the database
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @param array the specific dependency
     * @param required|optional whether this is a required or an optional dep
     * @param string|false dependency group this dependency is from, or false for ordinary dep
     */
    function _registerDep(&$data, &$pkg, $dep, $type, $group = false)
    {
        $info = array(
            'dep' => $dep,
            'type' => $type,
            'group' => $group);

        if (isset($dep['channel'])) {
            $depchannel = $dep['channel'];
        } else {
            $depchannel = '__uri';
        }
        if (!isset($data['dependencies'])) {
            $data['dependencies'] = array();
        }
        if (!isset($data['dependencies'][strtolower($pkg->getChannel())])) {
            $data['dependencies'][strtolower($pkg->getChannel())] = array();
        }
        if (!isset($data['dependencies'][strtolower($pkg->getChannel())][strtolower($pkg->getPackage())])) {
            $data['dependencies'][strtolower($pkg->getChannel())][strtolower($pkg->getPackage())] = array();
        }
        $data['dependencies'][strtolower($pkg->getChannel())][strtolower($pkg->getPackage())][]
            = $info;
        if (isset($data['packages'][strtolower($depchannel)][strtolower($dep['name'])])) {
            $found = false;
            foreach ($data['packages'][strtolower($depchannel)][strtolower($dep['name'])]
                  as $i => $p) {
                if ($p['channel'] == strtolower($pkg->getChannel()) &&
                      $p['package'] == strtolower($pkg->getPackage())) {
                    $found = true;
                    break;
                }
            }
            if (!$found) {
                $data['packages'][strtolower($depchannel)][strtolower($dep['name'])][]
                    = array('channel' => strtolower($pkg->getChannel()),
                            'package' => strtolower($pkg->getPackage()));
            }
        } else {
            if (!isset($data['packages'])) {
                $data['packages'] = array();
            }
            if (!isset($data['packages'][strtolower($depchannel)])) {
                $data['packages'][strtolower($depchannel)] = array();
            }
            if (!isset($data['packages'][strtolower($depchannel)][strtolower($dep['name'])])) {
                $data['packages'][strtolower($depchannel)][strtolower($dep['name'])] = array();
            }
            $data['packages'][strtolower($depchannel)][strtolower($dep['name'])][]
                = array('channel' => strtolower($pkg->getChannel()),
                        'package' => strtolower($pkg->getPackage()));
        }
    }
}
?>PEAR-1.7.2/PEAR/Dependency2.php100644   1750   1750      150313 11014312742  11130 <?php
/**
 * PEAR_Dependency2, advanced dependency validation
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Dependency2.php,v 1.56 2008/01/03 20:26:35 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * Required for the PEAR_VALIDATE_* constants
 */
require_once 'PEAR/Validate.php';

/**
 * Dependency check for PEAR packages
 *
 * This class handles both version 1.0 and 2.0 dependencies
 * WARNING: *any* changes to this class must be duplicated in the
 * test_PEAR_Dependency2 class found in tests/PEAR_Dependency2/setup.php.inc,
 * or unit tests will not actually validate the changes
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Dependency2
{
    /**
     * One of the PEAR_VALIDATE_* states
     * @see PEAR_VALIDATE_NORMAL
     * @var integer
     */
    var $_state;
    /**
     * Command-line options to install/upgrade/uninstall commands
     * @param array
     */
    var $_options;
    /**
     * @var OS_Guess
     */
    var $_os;
    /**
     * @var PEAR_Registry
     */
    var $_registry;
    /**
     * @var PEAR_Config
     */
    var $_config;
    /**
     * @var PEAR_DependencyDB
     */
    var $_dependencydb;
    /**
     * Output of PEAR_Registry::parsedPackageName()
     * @var array
     */
    var $_currentPackage;
    /**
     * @param PEAR_Config
     * @param array installation options
     * @param array format of PEAR_Registry::parsedPackageName()
     * @param int installation state (one of PEAR_VALIDATE_*)
     */
    function PEAR_Dependency2(&$config, $installoptions, $package,
                              $state = PEAR_VALIDATE_INSTALLING)
    {
        $this->_config = &$config;
        if (!class_exists('PEAR_DependencyDB')) {
            require_once 'PEAR/DependencyDB.php';
        }
        if (isset($installoptions['packagingroot'])) {
            // make sure depdb is in the right location
            $config->setInstallRoot($installoptions['packagingroot']);
        }
        $this->_registry = &$config->getRegistry();
        $this->_dependencydb = &PEAR_DependencyDB::singleton($config);
        if (isset($installoptions['packagingroot'])) {
            $config->setInstallRoot(false);
        }
        $this->_options = $installoptions;
        $this->_state = $state;
        if (!class_exists('OS_Guess')) {
            require_once 'OS/Guess.php';
        }
        $this->_os = new OS_Guess;
        $this->_currentPackage = $package;
    }

    function _getExtraString($dep)
    {
        $extra = ' (';
        if (isset($dep['uri'])) {
            return '';
        }
        if (isset($dep['recommended'])) {
            $extra .= 'recommended version ' . $dep['recommended'];
        } else {
            if (isset($dep['min'])) {
                $extra .= 'version >= ' . $dep['min'];
            }
            if (isset($dep['max'])) {
                if ($extra != ' (') {
                    $extra .= ', ';
                }
                $extra .= 'version <= ' . $dep['max'];
            }
            if (isset($dep['exclude'])) {
                if (!is_array($dep['exclude'])) {
                    $dep['exclude'] = array($dep['exclude']);
                }
                if ($extra != ' (') {
                    $extra .= ', ';
                }
                $extra .= 'excluded versions: ';
                foreach ($dep['exclude'] as $i => $exclude) {
                    if ($i) {
                        $extra .= ', ';
                    }
                    $extra .= $exclude;
                }
            }
        }
        $extra .= ')';
        if ($extra == ' ()') {
            $extra = '';
        }
        return $extra;
    }

    /**
     * This makes unit-testing a heck of a lot easier
     */
    function getPHP_OS()
    {
        return PHP_OS;
    }

    /**
     * This makes unit-testing a heck of a lot easier
     */
    function getsysname()
    {
        return $this->_os->getSysname();
    }

    /**
     * Specify a dependency on an OS.  Use arch for detailed os/processor information
     *
     * There are two generic OS dependencies that will be the most common, unix and windows.
     * Other options are linux, freebsd, darwin (OS X), sunos, irix, hpux, aix
     */
    function validateOsDependency($dep)
    {
        if ($this->_state != PEAR_VALIDATE_INSTALLING &&
              $this->_state != PEAR_VALIDATE_DOWNLOADING) {
            return true;
        }
        if (isset($dep['conflicts'])) {
            $not = true;
        } else {
            $not = false;
        }
        if ($dep['name'] == '*') {
            return true;
        }
        switch (strtolower($dep['name'])) {
            case 'windows' :
                if ($not) {
                    if (strtolower(substr($this->getPHP_OS(), 0, 3)) == 'win') {
                        if (!isset($this->_options['nodeps']) &&
                              !isset($this->_options['force'])) {
                            return $this->raiseError("Cannot install %s on Windows");
                        } else {
                            return $this->warning("warning: Cannot install %s on Windows");
                        }
                    }
                } else {
                    if (strtolower(substr($this->getPHP_OS(), 0, 3)) != 'win') {
                        if (!isset($this->_options['nodeps']) &&
                              !isset($this->_options['force'])) {
                            return $this->raiseError("Can only install %s on Windows");
                        } else {
                            return $this->warning("warning: Can only install %s on Windows");
                        }
                    }
                }
            break;
            case 'unix' :
                $unices = array('linux', 'freebsd', 'darwin', 'sunos', 'irix', 'hpux', 'aix');
                if ($not) {
                    if (in_array($this->getSysname(), $unices)) {
                        if (!isset($this->_options['nodeps']) &&
                              !isset($this->_options['force'])) {
                            return $this->raiseError("Cannot install %s on any Unix system");
                        } else {
                            return $this->warning(
                                "warning: Cannot install %s on any Unix system");
                        }
                    }
                } else {
                    if (!in_array($this->getSysname(), $unices)) {
                        if (!isset($this->_options['nodeps']) &&
                              !isset($this->_options['force'])) {
                            return $this->raiseError("Can only install %s on a Unix system");
                        } else {
                            return $this->warning(
                                "warning: Can only install %s on a Unix system");
                        }
                    }
                }
            break;
            default :
                if ($not) {
                    if (strtolower($dep['name']) == strtolower($this->getSysname())) {
                        if (!isset($this->_options['nodeps']) &&
                              !isset($this->_options['force'])) {
                            return $this->raiseError('Cannot install %s on ' . $dep['name'] .
                                ' operating system');
                        } else {
                            return $this->warning('warning: Cannot install %s on ' .
                                $dep['name'] . ' operating system');
                        }
                    }
                } else {
                    if (strtolower($dep['name']) != strtolower($this->getSysname())) {
                        if (!isset($this->_options['nodeps']) &&
                              !isset($this->_options['force'])) {
                            return $this->raiseError('Cannot install %s on ' .
                                $this->getSysname() .
                                ' operating system, can only install on ' . $dep['name']);
                        } else {
                            return $this->warning('warning: Cannot install %s on ' .
                                $this->getSysname() .
                                ' operating system, can only install on ' . $dep['name']);
                        }
                    }
                }
        }
        return true;
    }

    /**
     * This makes unit-testing a heck of a lot easier
     */
    function matchSignature($pattern)
    {
        return $this->_os->matchSignature($pattern);
    }

    /**
     * Specify a complex dependency on an OS/processor/kernel version,
     * Use OS for simple operating system dependency.
     *
     * This is the only dependency that accepts an eregable pattern.  The pattern
     * will be matched against the php_uname() output parsed by OS_Guess
     */
    function validateArchDependency($dep)
    {
        if ($this->_state != PEAR_VALIDATE_INSTALLING) {
            return true;
        }
        if (isset($dep['conflicts'])) {
            $not = true;
        } else {
            $not = false;
        }
        if (!$this->matchSignature($dep['pattern'])) {
            if (!$not) {
                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                    return $this->raiseError('%s Architecture dependency failed, does not ' .
                        'match "' . $dep['pattern'] . '"');
                } else {
                    return $this->warning('warning: %s Architecture dependency failed, does ' .
                        'not match "' . $dep['pattern'] . '"');
                }
            }
            return true;
        } else {
            if ($not) {
                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                    return $this->raiseError('%s Architecture dependency failed, required "' .
                        $dep['pattern'] . '"');
                } else {
                    return $this->warning('warning: %s Architecture dependency failed, ' .
                        'required "' . $dep['pattern'] . '"');
                }
            }
            return true;
        }
    }

    /**
     * This makes unit-testing a heck of a lot easier
     */
    function extension_loaded($name)
    {
        return extension_loaded($name);
    }

    /**
     * This makes unit-testing a heck of a lot easier
     */
    function phpversion($name = null)
    {
        if ($name !== null) {
            return phpversion($name);
        } else {
            return phpversion();
        }
    }

    function validateExtensionDependency($dep, $required = true)
    {
        if ($this->_state != PEAR_VALIDATE_INSTALLING &&
              $this->_state != PEAR_VALIDATE_DOWNLOADING) {
            return true;
        }
        $loaded = $this->extension_loaded($dep['name']);
        $extra = $this->_getExtraString($dep);
        if (isset($dep['exclude'])) {
            if (!is_array($dep['exclude'])) {
                $dep['exclude'] = array($dep['exclude']);
            }
        }
        if (!isset($dep['min']) && !isset($dep['max']) &&
              !isset($dep['recommended']) && !isset($dep['exclude'])) {
            if ($loaded) {
                if (isset($dep['conflicts'])) {
                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                        return $this->raiseError('%s conflicts with PHP extension "' .
                            $dep['name'] . '"' . $extra);
                    } else {
                        return $this->warning('warning: %s conflicts with PHP extension "' .
                            $dep['name'] . '"' . $extra);
                    }
                }
                return true;
            } else {
                if (isset($dep['conflicts'])) {
                    return true;
                }
                if ($required) {
                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                        return $this->raiseError('%s requires PHP extension "' .
                            $dep['name'] . '"' . $extra);
                    } else {
                        return $this->warning('warning: %s requires PHP extension "' .
                            $dep['name'] . '"' . $extra);
                    }
                } else {
                    return $this->warning('%s can optionally use PHP extension "' .
                        $dep['name'] . '"' . $extra);
                }
            }
        }
        if (!$loaded) {
            if (isset($dep['conflicts'])) {
                return true;
            }
            if (!$required) {
                return $this->warning('%s can optionally use PHP extension "' .
                    $dep['name'] . '"' . $extra);
            } else {
                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                    return $this->raiseError('%s requires PHP extension "' . $dep['name'] .
                        '"' . $extra);
                }
                    return $this->warning('warning: %s requires PHP extension "' . $dep['name'] .
                        '"' . $extra);
            }
        }
        $version = (string) $this->phpversion($dep['name']);
        if (empty($version)) {
            $version = '0';
        }
        $fail = false;
        if (isset($dep['min'])) {
            if (!version_compare($version, $dep['min'], '>=')) {
                $fail = true;
            }
        }
        if (isset($dep['max'])) {
            if (!version_compare($version, $dep['max'], '<=')) {
                $fail = true;
            }
        }
        if ($fail && !isset($dep['conflicts'])) {
            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                return $this->raiseError('%s requires PHP extension "' . $dep['name'] .
                    '"' . $extra . ', installed version is ' . $version);
            } else {
                return $this->warning('warning: %s requires PHP extension "' . $dep['name'] .
                    '"' . $extra . ', installed version is ' . $version);
            }
        } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail && isset($dep['conflicts'])) {
            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                return $this->raiseError('%s conflicts with PHP extension "' .
                    $dep['name'] . '"' . $extra . ', installed version is ' . $version);
            } else {
                return $this->warning('warning: %s conflicts with PHP extension "' .
                    $dep['name'] . '"' . $extra . ', installed version is ' . $version);
            }
        }
        if (isset($dep['exclude'])) {
            foreach ($dep['exclude'] as $exclude) {
                if (version_compare($version, $exclude, '==')) {
                    if (isset($dep['conflicts'])) {
                        continue;
                    }
                    if (!isset($this->_options['nodeps']) &&
                          !isset($this->_options['force'])) {
                        return $this->raiseError('%s is not compatible with PHP extension "' .
                            $dep['name'] . '" version ' .
                            $exclude);
                    } else {
                        return $this->warning('warning: %s is not compatible with PHP extension "' .
                            $dep['name'] . '" version ' .
                            $exclude);
                    }
                } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) {
                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                        return $this->raiseError('%s conflicts with PHP extension "' .
                            $dep['name'] . '"' . $extra . ', installed version is ' . $version);
                    } else {
                        return $this->warning('warning: %s conflicts with PHP extension "' .
                            $dep['name'] . '"' . $extra . ', installed version is ' . $version);
                    }
                }
            }
        }
        if (isset($dep['recommended'])) {
            if (version_compare($version, $dep['recommended'], '==')) {
                return true;
            } else {
                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                    return $this->raiseError('%s dependency: PHP extension ' . $dep['name'] .
                        ' version "' . $version . '"' .
                        ' is not the recommended version "' . $dep['recommended'] .
                        '", but may be compatible, use --force to install');
                } else {
                    return $this->warning('warning: %s dependency: PHP extension ' .
                        $dep['name'] . ' version "' . $version . '"' .
                        ' is not the recommended version "' . $dep['recommended'].'"');
                }
            }
        }
        return true;
    }

    function validatePhpDependency($dep)
    {
        if ($this->_state != PEAR_VALIDATE_INSTALLING &&
              $this->_state != PEAR_VALIDATE_DOWNLOADING) {
            return true;
        }
        $version = $this->phpversion();
        $extra = $this->_getExtraString($dep);
        if (isset($dep['exclude'])) {
            if (!is_array($dep['exclude'])) {
                $dep['exclude'] = array($dep['exclude']);
            }
        }
        if (isset($dep['min'])) {
            if (!version_compare($version, $dep['min'], '>=')) {
                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                    return $this->raiseError('%s requires PHP' .
                        $extra . ', installed version is ' . $version);
                } else {
                    return $this->warning('warning: %s requires PHP' .
                        $extra . ', installed version is ' . $version);
                }
            }
        }
        if (isset($dep['max'])) {
            if (!version_compare($version, $dep['max'], '<=')) {
                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                    return $this->raiseError('%s requires PHP' .
                        $extra . ', installed version is ' . $version);
                } else {
                    return $this->warning('warning: %s requires PHP' .
                        $extra . ', installed version is ' . $version);
                }
            }
        }
        if (isset($dep['exclude'])) {
            foreach ($dep['exclude'] as $exclude) {
                if (version_compare($version, $exclude, '==')) {
                    if (!isset($this->_options['nodeps']) &&
                          !isset($this->_options['force'])) {
                        return $this->raiseError('%s is not compatible with PHP version ' .
                            $exclude);
                    } else {
                        return $this->warning(
                            'warning: %s is not compatible with PHP version ' .
                            $exclude);
                    }
                }
            }
        }
        return true;
    }

    /**
     * This makes unit-testing a heck of a lot easier
     */
    function getPEARVersion()
    {
        return '1.7.2';
    }

    function validatePearinstallerDependency($dep)
    {
        $pearversion = $this->getPEARVersion();
        $extra = $this->_getExtraString($dep);
        if (isset($dep['exclude'])) {
            if (!is_array($dep['exclude'])) {
                $dep['exclude'] = array($dep['exclude']);
            }
        }
        if (version_compare($pearversion, $dep['min'], '<')) {
            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                return $this->raiseError('%s requires PEAR Installer' . $extra .
                    ', installed version is ' . $pearversion);
            } else {
                return $this->warning('warning: %s requires PEAR Installer' . $extra .
                    ', installed version is ' . $pearversion);
            }
        }
        if (isset($dep['max'])) {
            if (version_compare($pearversion, $dep['max'], '>')) {
                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                    return $this->raiseError('%s requires PEAR Installer' . $extra .
                        ', installed version is ' . $pearversion);
                } else {
                    return $this->warning('warning: %s requires PEAR Installer' . $extra .
                        ', installed version is ' . $pearversion);
                }
            }
        }
        if (isset($dep['exclude'])) {
            if (!isset($dep['exclude'][0])) {
                $dep['exclude'] = array($dep['exclude']);
            }
            foreach ($dep['exclude'] as $exclude) {
                if (version_compare($exclude, $pearversion, '==')) {
                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                        return $this->raiseError('%s is not compatible with PEAR Installer ' .
                            'version ' . $exclude);
                    } else {
                        return $this->warning('warning: %s is not compatible with PEAR ' .
                            'Installer version ' . $exclude);
                    }
                }
            }
        }
        return true;
    }

    function validateSubpackageDependency($dep, $required, $params)
    {
        return $this->validatePackageDependency($dep, $required, $params);
    }

    /**
     * @param array dependency information (2.0 format)
     * @param boolean whether this is a required dependency
     * @param array a list of downloaded packages to be installed, if any
     * @param boolean if true, then deps on pear.php.net that fail will also check
     *                against pecl.php.net packages to accomodate extensions that have
     *                moved to pecl.php.net from pear.php.net
     */
    function validatePackageDependency($dep, $required, $params, $depv1 = false)
    {
        if ($this->_state != PEAR_VALIDATE_INSTALLING &&
              $this->_state != PEAR_VALIDATE_DOWNLOADING) {
            return true;
        }
        if (isset($dep['providesextension'])) {
            if ($this->extension_loaded($dep['providesextension'])) {
                $save = $dep;
                $subdep = $dep;
                $subdep['name'] = $subdep['providesextension'];
                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                $ret = $this->validateExtensionDependency($subdep, $required);
                PEAR::popErrorHandling();
                if (!PEAR::isError($ret)) {
                    return true;
                }
            }
        }
        if ($this->_state == PEAR_VALIDATE_INSTALLING) {
            return $this->_validatePackageInstall($dep, $required, $depv1);
        }
        if ($this->_state == PEAR_VALIDATE_DOWNLOADING) {
            return $this->_validatePackageDownload($dep, $required, $params, $depv1);
        }
    }

    function _validatePackageDownload($dep, $required, $params, $depv1 = false)
    {
        $dep['package'] = $dep['name'];
        if (isset($dep['uri'])) {
            $dep['channel'] = '__uri';
        }
        $depname = $this->_registry->parsedPackageNameToString($dep, true);
        $found = false;
        foreach ($params as $param) {
            if ($param->isEqual(
                  array('package' => $dep['name'],
                        'channel' => $dep['channel']))) {
                $found = true;
                break;
            }
            if ($depv1 && $dep['channel'] == 'pear.php.net') {
                if ($param->isEqual(
                  array('package' => $dep['name'],
                        'channel' => 'pecl.php.net'))) {
                    $found = true;
                    break;
                }
            }
        }
        if (!$found && isset($dep['providesextension'])) {
            foreach ($params as $param) {
                if ($param->isExtension($dep['providesextension'])) {
                    $found = true;
                    break;
                }
            }
        }
        if ($found) {
            $version = $param->getVersion();
            $installed = false;
            $downloaded = true;
        } else {
            if ($this->_registry->packageExists($dep['name'], $dep['channel'])) {
                $installed = true;
                $downloaded = false;
                $version = $this->_registry->packageinfo($dep['name'], 'version',
                    $dep['channel']);
            } else {
                if ($dep['channel'] == 'pecl.php.net' && $this->_registry->packageExists($dep['name'],
                      'pear.php.net')) {
                    $installed = true;
                    $downloaded = false;
                    $version = $this->_registry->packageinfo($dep['name'], 'version',
                        'pear.php.net');
                } else {
                    $version = 'not installed or downloaded';
                    $installed = false;
                    $downloaded = false;
                }
            }
        }
        $extra = $this->_getExtraString($dep);
        if (isset($dep['exclude'])) {
            if (!is_array($dep['exclude'])) {
                $dep['exclude'] = array($dep['exclude']);
            }
        }
        if (!isset($dep['min']) && !isset($dep['max']) &&
              !isset($dep['recommended']) && !isset($dep['exclude'])) {
            if ($installed || $downloaded) {
                $installed = $installed ? 'installed' : 'downloaded';
                if (isset($dep['conflicts'])) {
                    if ($version) {
                        $rest = ", $installed version is " . $version;
                    } else {
                        $rest = '';
                    }
                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                        return $this->raiseError('%s conflicts with package "' . $depname . '"' .
                            $extra . $rest);
                    } else {
                        return $this->warning('warning: %s conflicts with package "' . $depname . '"' .
                            $extra . $rest);
                    }
                }
                return true;
            } else {
                if (isset($dep['conflicts'])) {
                    return true;
                }
                if ($required) {
                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                        return $this->raiseError('%s requires package "' . $depname . '"' .
                            $extra);
                    } else {
                        return $this->warning('warning: %s requires package "' . $depname . '"' .
                            $extra);
                    }
                } else {
                    return $this->warning('%s can optionally use package "' . $depname . '"' .
                        $extra);
                }
            }
        }
        if (!$installed && !$downloaded) {
            if (isset($dep['conflicts'])) {
                return true;
            }
            if ($required) {
                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                    return $this->raiseError('%s requires package "' . $depname . '"' .
                        $extra);
                } else {
                    return $this->warning('warning: %s requires package "' . $depname . '"' .
                        $extra);
                }
            } else {
                return $this->warning('%s can optionally use package "' . $depname . '"' .
                    $extra);
            }
        }
        $fail = false;
        if (isset($dep['min'])) {
            if (version_compare($version, $dep['min'], '<')) {
                $fail = true;
            }
        }
        if (isset($dep['max'])) {
            if (version_compare($version, $dep['max'], '>')) {
                $fail = true;
            }
        }
        if ($fail && !isset($dep['conflicts'])) {
            $installed = $installed ? 'installed' : 'downloaded';
            $dep['package'] = $dep['name'];
            $dep = $this->_registry->parsedPackageNameToString($dep, true);
            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                return $this->raiseError('%s requires package "' . $depname . '"' .
                    $extra . ", $installed version is " . $version);
            } else {
                return $this->warning('warning: %s requires package "' . $depname . '"' .
                    $extra . ", $installed version is " . $version);
            }
        } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail &&
              isset($dep['conflicts']) && !isset($dep['exclude'])) {
            $installed = $installed ? 'installed' : 'downloaded';
            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra .
                    ", $installed version is " . $version);
            } else {
                return $this->warning('warning: %s conflicts with package "' . $depname . '"' .
                    $extra . ", $installed version is " . $version);
            }
        }
        if (isset($dep['exclude'])) {
            $installed = $installed ? 'installed' : 'downloaded';
            foreach ($dep['exclude'] as $exclude) {
                if (version_compare($version, $exclude, '==') && !isset($dep['conflicts'])) {
                    if (!isset($this->_options['nodeps']) &&
                          !isset($this->_options['force'])) {
                        return $this->raiseError('%s is not compatible with ' .
                            $installed . ' package "' .
                            $depname . '" version ' .
                            $exclude);
                    } else {
                        return $this->warning('warning: %s is not compatible with ' .
                            $installed . ' package "' .
                            $depname . '" version ' .
                            $exclude);
                    }
                } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) {
                    $installed = $installed ? 'installed' : 'downloaded';
                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                        return $this->raiseError('%s conflicts with package "' . $depname . '"' .
                            $extra . ", $installed version is " . $version);
                    } else {
                        return $this->warning('warning: %s conflicts with package "' . $depname . '"' .
                            $extra . ", $installed version is " . $version);
                    }
                }
            }
        }
        if (isset($dep['recommended'])) {
            $installed = $installed ? 'installed' : 'downloaded';
            if (version_compare($version, $dep['recommended'], '==')) {
                return true;
            } else {
                if (!$found && $installed) {
                    $param = $this->_registry->getPackage($dep['name'], $dep['channel']);
                }
                if ($param) {
                    $found = false;
                    foreach ($params as $parent) {
                        if ($parent->isEqual($this->_currentPackage)) {
                            $found = true;
                            break;
                        }
                    }
                    if ($found) {
                        if ($param->isCompatible($parent)) {
                            return true;
                        }
                    } else { // this is for validPackage() calls
                        $parent = $this->_registry->getPackage($this->_currentPackage['package'],
                            $this->_currentPackage['channel']);
                        if ($parent !== null) {
                            if ($param->isCompatible($parent)) {
                                return true;
                            }
                        }
                    }
                }
                if (!isset($this->_options['nodeps']) && !isset($this->_options['force']) &&
                      !isset($this->_options['loose'])) {
                    return $this->raiseError('%s dependency package "' . $depname .
                        '" ' . $installed . ' version ' . $version . 
                        ' is not the recommended version ' . $dep['recommended'] .
                        ', but may be compatible, use --force to install');
                } else {
                    return $this->warning('warning: %s dependency package "' . $depname .
                        '" ' . $installed . ' version ' . $version .
                        ' is not the recommended version ' . $dep['recommended']);
                }
            }
        }
        return true;
    }

    function _validatePackageInstall($dep, $required, $depv1 = false)
    {
        return $this->_validatePackageDownload($dep, $required, array(), $depv1);
    }

    /**
     * Verify that uninstalling packages passed in to command line is OK.
     *
     * @param PEAR_Installer $dl
     * @return PEAR_Error|true
     */
    function validatePackageUninstall(&$dl)
    {
        if (PEAR::isError($this->_dependencydb)) {
            return $this->_dependencydb;
        }
        $params = array();
        // construct an array of "downloaded" packages to fool the package dependency checker
        // into using these to validate uninstalls of circular dependencies
        $downloaded = &$dl->getUninstallPackages();
        foreach ($downloaded as $i => $pf) {
            if (!class_exists('PEAR_Downloader_Package')) {
                require_once 'PEAR/Downloader/Package.php';
            }
            $dp = &new PEAR_Downloader_Package($dl);
            $dp->setPackageFile($downloaded[$i]);
            $params[$i] = &$dp;
        }
        // check cache
        $memyselfandI = strtolower($this->_currentPackage['channel']) . '/' .
            strtolower($this->_currentPackage['package']);
        if (isset($dl->___uninstall_package_cache)) {
            $badpackages = $dl->___uninstall_package_cache;
            if (isset($badpackages[$memyselfandI]['warnings'])) {
                foreach ($badpackages[$memyselfandI]['warnings'] as $warning) {
                    $dl->log(0, $warning[0]);
                }
            }
            if (isset($badpackages[$memyselfandI]['errors'])) {
                foreach ($badpackages[$memyselfandI]['errors'] as $error) {
                    if (is_array($error)) {
                        $dl->log(0, $error[0]);
                    } else {
                        $dl->log(0, $error->getMessage());
                    }
                }
                if (isset($this->_options['nodeps']) || isset($this->_options['force'])) {
                    return $this->warning(
                        'warning: %s should not be uninstalled, other installed packages depend ' .
                        'on this package');
                } else {
                    return $this->raiseError(
                        '%s cannot be uninstalled, other installed packages depend on this package');
                }
            }
            return true;
        }
        // first, list the immediate parents of each package to be uninstalled
        $perpackagelist = array();
        $allparents = array();
        foreach ($params as $i => $param) {
            $a = array('channel' => strtolower($param->getChannel()),
                      'package' => strtolower($param->getPackage()));
            $deps = $this->_dependencydb->getDependentPackages($a);
            if ($deps) {
                foreach ($deps as $d) {
                    $pardeps = $this->_dependencydb->getDependencies($d);
                    foreach ($pardeps as $dep) {
                        if (strtolower($dep['dep']['channel']) == $a['channel'] &&
                              strtolower($dep['dep']['name']) == $a['package']) {
                            if (!isset($perpackagelist[$a['channel'] . '/' . $a['package']])) {
                                $perpackagelist[$a['channel'] . '/' . $a['package']] = array();
                            }
                            $perpackagelist[$a['channel'] . '/' . $a['package']][]
                                = array($d['channel'] . '/' . $d['package'], $dep);
                            if (!isset($allparents[$d['channel'] . '/' . $d['package']])) {
                                $allparents[$d['channel'] . '/' . $d['package']] = array();
                            }
                            if (!isset($allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']])) {
                                $allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']] = array();
                            }
                            $allparents[$d['channel'] . '/' . $d['package']]
                                       [$a['channel'] . '/' . $a['package']][]
                                = array($d, $dep);
                        }
                    }
                }
            }
        }
        // next, remove any packages from the parents list that are not installed
        $remove = array();
        foreach ($allparents as $parent => $d1) {
            foreach ($d1 as $d) {
                if ($this->_registry->packageExists($d[0][0]['package'], $d[0][0]['channel'])) {
                    continue;
                }
                $remove[$parent] = true;
            }
        }
        // next remove any packages from the parents list that are not passed in for
        // uninstallation
        foreach ($allparents as $parent => $d1) {
            foreach ($d1 as $d) {
                foreach ($params as $param) {
                    if (strtolower($param->getChannel()) == $d[0][0]['channel'] &&
                          strtolower($param->getPackage()) == $d[0][0]['package']) {
                        // found it
                        continue 3;
                    }
                }
                $remove[$parent] = true;
            }
        }
        // remove all packages whose dependencies fail
        // save which ones failed for error reporting
        $badchildren = array();
        do {
            $fail = false;
            foreach ($remove as $package => $unused) {
                if (!isset($allparents[$package])) {
                    continue;
                }
                foreach ($allparents[$package] as $kid => $d1) {
                    foreach ($d1 as $depinfo) {
                        if ($depinfo[1]['type'] != 'optional') {
                            if (isset($badchildren[$kid])) {
                                continue;
                            }
                            $badchildren[$kid] = true;
                            $remove[$kid] = true;
                            $fail = true;
                            continue 2;
                        }
                    }
                }
                if ($fail) {
                    // start over, we removed some children
                    continue 2;
                }
            }
        } while ($fail);
        // next, construct the list of packages that can't be uninstalled
        $badpackages = array();
        $save = $this->_currentPackage;
        foreach ($perpackagelist as $package => $packagedeps) {
            foreach ($packagedeps as $parent) {
                if (!isset($remove[$parent[0]])) {
                    continue;
                }
                $packagename = $this->_registry->parsePackageName($parent[0]);
                $packagename['channel'] = $this->_registry->channelAlias($packagename['channel']);
                $pa = $this->_registry->getPackage($packagename['package'], $packagename['channel']);
                $packagename['package'] = $pa->getPackage();
                $this->_currentPackage = $packagename;
                // parent is not present in uninstall list, make sure we can actually
                // uninstall it (parent dep is optional)
                $parentname['channel'] = $this->_registry->channelAlias($parent[1]['dep']['channel']);
                $pa = $this->_registry->getPackage($parent[1]['dep']['name'], $parent[1]['dep']['channel']);
                $parentname['package'] = $pa->getPackage();
                $parent[1]['dep']['package'] = $parentname['package'];
                $parent[1]['dep']['channel'] = $parentname['channel'];
                if ($parent[1]['type'] == 'optional') {
                    $test = $this->_validatePackageUninstall($parent[1]['dep'], false, $dl);
                    if ($test !== true) {
                        $badpackages[$package]['warnings'][] = $test;
                    }
                } else {
                    $test = $this->_validatePackageUninstall($parent[1]['dep'], true, $dl);
                    if ($test !== true) {
                        $badpackages[$package]['errors'][] = $test;
                    }
                }
            }
        }
        $this->_currentPackage = $save;
        $dl->___uninstall_package_cache = $badpackages;
        if (isset($badpackages[$memyselfandI])) {
            if (isset($badpackages[$memyselfandI]['warnings'])) {
                foreach ($badpackages[$memyselfandI]['warnings'] as $warning) {
                    $dl->log(0, $warning[0]);
                }
            }
            if (isset($badpackages[$memyselfandI]['errors'])) {
                foreach ($badpackages[$memyselfandI]['errors'] as $error) {
                    if (is_array($error)) {
                        $dl->log(0, $error[0]);
                    } else {
                        $dl->log(0, $error->getMessage());
                    }
                }
                if (isset($this->_options['nodeps']) || isset($this->_options['force'])) {
                    return $this->warning(
                        'warning: %s should not be uninstalled, other installed packages depend ' .
                        'on this package');
                } else {
                    return $this->raiseError(
                        '%s cannot be uninstalled, other installed packages depend on this package');
                }
            }
        }
        return true;
    }

    function _validatePackageUninstall($dep, $required, $dl)
    {
        $depname = $this->_registry->parsedPackageNameToString($dep, true);
        $version = $this->_registry->packageinfo($dep['package'], 'version',
            $dep['channel']);
        if (!$version) {
            return true;
        }
        $extra = $this->_getExtraString($dep);
        if (isset($dep['exclude'])) {
            if (!is_array($dep['exclude'])) {
                $dep['exclude'] = array($dep['exclude']);
            }
        }
        if (isset($dep['conflicts'])) {
            return true; // uninstall OK - these packages conflict (probably installed with --force)
        }
        if (!isset($dep['min']) && !isset($dep['max'])) {
            if ($required) {
                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                    return $this->raiseError('"' . $depname . '" is required by ' .
                        'installed package %s' . $extra);
                } else {
                    return $this->warning('warning: "' . $depname . '" is required by ' .
                        'installed package %s' . $extra);
                }
            } else {
                return $this->warning('"' . $depname . '" can be optionally used by ' .
                        'installed package %s' . $extra);
            }
        }
        $fail = false;
        if (isset($dep['min'])) {
            if (version_compare($version, $dep['min'], '>=')) {
                $fail = true;
            }
        }
        if (isset($dep['max'])) {
            if (version_compare($version, $dep['max'], '<=')) {
                $fail = true;
            }
        }
        // we re-use this variable, preserve the original value
        $saverequired = $required;
        if ($required) {
            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                return $this->raiseError($depname . $extra . ' is required by installed package' .
                    ' "%s"');
            } else {
                return $this->raiseError('warning: ' . $depname . $extra .
                    ' is required by installed package "%s"');
            }
        } else {
            return $this->warning($depname . $extra . ' can be optionally used by installed package' .
                    ' "%s"');
        }
        return true;
    }

    /**
     * validate a downloaded package against installed packages
     * 
     * As of PEAR 1.4.3, this will only validate
     *
     * @param array|PEAR_Downloader_Package|PEAR_PackageFile_v1|PEAR_PackageFile_v2
     *              $pkg package identifier (either
     *                   array('package' => blah, 'channel' => blah) or an array with
     *                   index 'info' referencing an object)
     * @param PEAR_Downloader $dl
     * @param array $params full list of packages to install
     * @return true|PEAR_Error
     */
    function validatePackage($pkg, &$dl, $params = array())
    {
        if (is_array($pkg) && isset($pkg['info'])) {
            $deps = $this->_dependencydb->getDependentPackageDependencies($pkg['info']);
        } else {
            $deps = $this->_dependencydb->getDependentPackageDependencies($pkg);
        }
        $fail = false;
        if ($deps) {
            if (!class_exists('PEAR_Downloader_Package')) {
                require_once 'PEAR/Downloader/Package.php';
            }
            $dp = &new PEAR_Downloader_Package($dl);
            if (is_object($pkg)) {
                $dp->setPackageFile($pkg);
            } else {
                $dp->setDownloadURL($pkg);
            }
            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
            foreach ($deps as $channel => $info) {
                foreach ($info as $package => $ds) {
                    foreach ($params as $packd) {
                        if (strtolower($packd->getPackage()) == strtolower($package) &&
                              $packd->getChannel() == $channel) {
                            $dl->log(3, 'skipping installed package check of "' .
                                        $this->_registry->parsedPackageNameToString(
                                            array('channel' => $channel, 'package' => $package),
                                            true) .
                                        '", version "' . $packd->getVersion() . '" will be ' .
                                        'downloaded and installed');
                            continue 2; // jump to next package
                        }
                    }
                    foreach ($ds as $d) {
                        $checker = &new PEAR_Dependency2($this->_config, $this->_options,
                            array('channel' => $channel, 'package' => $package), $this->_state);
                        $dep = $d['dep'];
                        $required = $d['type'] == 'required';
                        $ret = $checker->_validatePackageDownload($dep, $required, array(&$dp));
                        if (is_array($ret)) {
                            $dl->log(0, $ret[0]);
                        } elseif (PEAR::isError($ret)) {
                            $dl->log(0, $ret->getMessage());
                            $fail = true;
                        }
                    }
                }
            }
            PEAR::popErrorHandling();
        }
        if ($fail) {
            return $this->raiseError(
                '%s cannot be installed, conflicts with installed packages');
        }
        return true;
    }

    /**
     * validate a package.xml 1.0 dependency
     */
    function validateDependency1($dep, $params = array())
    {
        if (!isset($dep['optional'])) {
            $dep['optional'] = 'no';
        }
        list($newdep, $type) = $this->normalizeDep($dep);
        if (!$newdep) {
            return $this->raiseError("Invalid Dependency");
        }
        if (method_exists($this, "validate{$type}Dependency")) {
            return $this->{"validate{$type}Dependency"}($newdep, $dep['optional'] == 'no',
                $params, true);
        }
    }

    /**
     * Convert a 1.0 dep into a 2.0 dep
     */
    function normalizeDep($dep)
    {
        $types = array(
            'pkg' => 'Package',
            'ext' => 'Extension',
            'os' => 'Os',
            'php' => 'Php'
        );
        if (isset($types[$dep['type']])) {
            $type = $types[$dep['type']];
        } else {
            return array(false, false);
        }
        $newdep = array();
        switch ($type) {
            case 'Package' :
                $newdep['channel'] = 'pear.php.net';
            case 'Extension' :
            case 'Os' :
                $newdep['name'] = $dep['name'];
            break;
        }
        $dep['rel'] = PEAR_Dependency2::signOperator($dep['rel']);
        switch ($dep['rel']) {
            case 'has' :
                return array($newdep, $type);
            break;
            case 'not' :
                $newdep['conflicts'] = true;
            break;
            case '>=' :
            case '>' :
                $newdep['min'] = $dep['version'];
                if ($dep['rel'] == '>') {
                    $newdep['exclude'] = $dep['version'];
                }
            break;
            case '<=' :
            case '<' :
                $newdep['max'] = $dep['version'];
                if ($dep['rel'] == '<') {
                    $newdep['exclude'] = $dep['version'];
                }
            break;
            case 'ne' :
            case '!=' :
                $newdep['min'] = '0';
                $newdep['max'] = '100000';
                $newdep['exclude'] = $dep['version'];
            break;
            case '==' :
                $newdep['min'] = $dep['version'];
                $newdep['max'] = $dep['version'];
            break;
        }
        if ($type == 'Php') {
            if (!isset($newdep['min'])) {
                $newdep['min'] = '4.2.0';
            }
            if (!isset($newdep['max'])) {
                $newdep['max'] = '6.0.0';
            }
        }
        return array($newdep, $type);
    }

    /**
     * Converts text comparing operators to them sign equivalents
     *
     * Example: 'ge' to '>='
     *
     * @access public
     * @param  string Operator
     * @return string Sign equivalent
     */
    function signOperator($operator)
    {
        switch($operator) {
            case 'lt': return '<';
            case 'le': return '<=';
            case 'gt': return '>';
            case 'ge': return '>=';
            case 'eq': return '==';
            case 'ne': return '!=';
            default:
                return $operator;
        }
    }

    function raiseError($msg)
    {
        if (isset($this->_options['ignore-errors'])) {
            return $this->warning($msg);
        }
        return PEAR::raiseError(sprintf($msg, $this->_registry->parsedPackageNameToString(
            $this->_currentPackage, true)));
    }

    function warning($msg)
    {
        return array(sprintf($msg, $this->_registry->parsedPackageNameToString(
            $this->_currentPackage, true)));
    }
}
?>PEAR-1.7.2/PEAR/Downloader.php100644   1750   1750      210776 11014312742  11100 <?php
/**
 * PEAR_Downloader, the PEAR Installer's download utility class
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V. V. Cox <cox@idecnet.com>
 * @author     Martin Jansen <mj@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Downloader.php,v 1.138 2008/04/11 01:16:40 dufuz Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.3.0
 */

/**
 * Needed for constants, extending
 */
require_once 'PEAR/Common.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 download anything from the internet (PEAR Packages,
 * static URLs, xml files)
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V. V. Cox <cox@idecnet.com>
 * @author     Martin Jansen <mj@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.3.0
 */
class PEAR_Downloader extends PEAR_Common
{
    /**
     * @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
     *  - nocompress    : download uncompressed tarballs
     * @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();

    /**
     * @var boolean
     * @access private
     */
    var $_internalDownload = false;

    /**
     * Temporary variable used in sorting packages by dependency in {@link sortPkgDeps()}
     * @var array
     * @access private
     */
    var $_packageSortTree;

    /**
     * Temporary directory, or configuration value where downloads will occur
     * @var string
     */
    var $_downloadDir;
    // {{{ PEAR_Downloader()

    /**
     * @param PEAR_Frontend_*
     * @param array
     * @param PEAR_Config
     */
    function PEAR_Downloader(&$ui, $options, &$config)
    {
        parent::PEAR_Common();
        $this->_options = $options;
        $this->config = &$config;
        $this->_preferredState = $this->config->get('preferred_state');
        $this->ui = &$ui;
        if (!$this->_preferredState) {
            // don't inadvertantly use a non-set preferred_state
            $this->_preferredState = null;
        }

        if (isset($this->_options['installroot'])) {
            $this->config->setInstallRoot($this->_options['installroot']);
        }
        $this->_registry = &$config->getRegistry();
        $this->_remote = &$config->getRemote();

        if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
            $this->_installed = $this->_registry->listAllPackages();
            foreach ($this->_installed as $key => $unused) {
                if (!count($unused)) {
                    continue;
                }
                $strtolower = create_function('$a','return strtolower($a);');
                array_walk($this->_installed[$key], $strtolower);
            }
        }
    }

    /**
     * Attempt to discover a channel's remote capabilities from
     * its server name
     * @param string
     * @return boolean
     */
    function discover($channel)
    {
        $this->log(1, 'Attempting to discover channel "' . $channel . '"...');
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        $callback = $this->ui ? array(&$this, '_downloadCallback') : null;
        if (!class_exists('System')) {
            require_once 'System.php';
        }
        $a = $this->downloadHttp('http://' . $channel . '/channel.xml', $this->ui,
            System::mktemp(array('-d')), $callback, false);
        PEAR::popErrorHandling();
        if (PEAR::isError($a)) {
            return false;
        }
        list($a, $lastmodified) = $a;
        if (!class_exists('PEAR_ChannelFile')) {
            require_once 'PEAR/ChannelFile.php';
        }
        $b = new PEAR_ChannelFile;
        if ($b->fromXmlFile($a)) {
            unlink($a);
            if ($this->config->get('auto_discover')) {
                $this->_registry->addChannel($b, $lastmodified);
                $alias = $b->getName();
                if ($b->getName() == $this->_registry->channelName($b->getAlias())) {
                    $alias = $b->getAlias();
                }
                $this->log(1, 'Auto-discovered channel "' . $channel .
                    '", alias "' . $alias . '", adding to registry');
            }
            return true;
        }
        unlink($a);
        return false;
    }

    /**
     * For simpler unit-testing
     * @param PEAR_Downloader
     * @return PEAR_Downloader_Package
     */
    function &newDownloaderPackage(&$t)
    {
        if (!class_exists('PEAR_Downloader_Package')) {
            require_once 'PEAR/Downloader/Package.php';
        }
        $a = &new PEAR_Downloader_Package($t);
        return $a;
    }

    /**
     * For simpler unit-testing
     * @param PEAR_Config
     * @param array
     * @param array
     * @param int
     */
    function &getDependency2Object(&$c, $i, $p, $s)
    {
        if (!class_exists('PEAR_Dependency2')) {
            require_once 'PEAR/Dependency2.php';
        }
        $z = &new PEAR_Dependency2($c, $i, $p, $s);
        return $z;
    }

    function &download($params)
    {
        if (!count($params)) {
            $a = array();
            return $a;
        }
        if (!isset($this->_registry)) {
            $this->_registry = &$this->config->getRegistry();
        }
        if (!isset($this->_remote)) {
            $this->_remote = &$this->config->getRemote();
        }
        $channelschecked = array();
        // convert all parameters into PEAR_Downloader_Package objects
        foreach ($params as $i => $param) {
            $params[$i] = &$this->newDownloaderPackage($this);
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            $err = $params[$i]->initialize($param);
            PEAR::staticPopErrorHandling();
            if (!$err) {
                // skip parameters that were missed by preferred_state
                continue;
            }
            if (PEAR::isError($err)) {
                if (!isset($this->_options['soft'])) {
                    $this->log(0, $err->getMessage());
                }
                $params[$i] = false;
                if (is_object($param)) {
                    $param = $param->getChannel() . '/' . $param->getPackage();
                }
                $this->pushError('Package "' . $param . '" is not valid',
                    PEAR_INSTALLER_SKIPPED);
            } else {
                do {
                    if ($params[$i] && $params[$i]->getType() == 'local') {
                        // bug #7090
                        // skip channel.xml check for local packages
                        break;
                    }
                    if ($params[$i] && !isset($channelschecked[$params[$i]->getChannel()]) &&
                          !isset($this->_options['offline'])) {
                        $channelschecked[$params[$i]->getChannel()] = true;
                        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
                        if (!class_exists('System')) {
                            require_once 'System.php';
                        }
                        $curchannel = &$this->_registry->getChannel($params[$i]->getChannel());
                        if (PEAR::isError($curchannel)) {
                            PEAR::staticPopErrorHandling();
                            return $this->raiseError($curchannel);
                        }
                        if (PEAR::isError($dir = $this->getDownloadDir())) {
                            PEAR::staticPopErrorHandling();
                            break;
                        }
                        $mirror = $this->config->get('preferred_mirror', null,
                                                     $params[$i]->getChannel());
                        $a = $this->downloadHttp('http://' . $mirror .
                            '/channel.xml', $this->ui, $dir, null, $curchannel->lastModified());

                        PEAR::staticPopErrorHandling();
                        if (PEAR::isError($a) || !$a) {
                            break;
                        }
                        $this->log(0, 'WARNING: channel "' . $params[$i]->getChannel() . '" has ' .
                            'updated its protocols, use "channel-update ' . $params[$i]->getChannel() .
                            '" to update');
                    }
                } while (false);
                if ($params[$i] && !isset($this->_options['downloadonly'])) {
                    if (isset($this->_options['packagingroot'])) {
                        $checkdir = $this->_prependPath(
                            $this->config->get('php_dir', null, $params[$i]->getChannel()),
                            $this->_options['packagingroot']);
                    } else {
                        $checkdir = $this->config->get('php_dir',
                            null, $params[$i]->getChannel());
                    }
                    while ($checkdir && $checkdir != '/' && !file_exists($checkdir)) {
                        $checkdir = dirname($checkdir);
                    }
                    if ($checkdir == '.') {
                        $checkdir = '/';
                    }
                    if (!is_writeable($checkdir)) {
                        return PEAR::raiseError('Cannot install, php_dir for channel "' .
                            $params[$i]->getChannel() . '" is not writeable by the current user');
                    }
                }
            }
        }
        unset($channelschecked);
        PEAR_Downloader_Package::removeDuplicates($params);
        if (!count($params)) {
            $a = array();
            return $a;
        }
        if (!isset($this->_options['nodeps']) && !isset($this->_options['offline'])) {
            $reverify = true;
            while ($reverify) {
                $reverify = false;
                foreach ($params as $i => $param) {
                    //PHP Bug 40768 / PEAR Bug #10944
                    //Nested foreaches fail in PHP 5.2.1
                    key($params);
                    $ret = $params[$i]->detectDependencies($params);
                    if (PEAR::isError($ret)) {
                        $reverify = true;
                        $params[$i] = false;
                        PEAR_Downloader_Package::removeDuplicates($params);
                        if (!isset($this->_options['soft'])) {
                            $this->log(0, $ret->getMessage());
                        }
                        continue 2;
                    }
                }
            }
        }
        if (isset($this->_options['offline'])) {
            $this->log(3, 'Skipping dependency download check, --offline specified');
        }
        if (!count($params)) {
            $a = array();
            return $a;
        }
        while (PEAR_Downloader_Package::mergeDependencies($params));
        PEAR_Downloader_Package::removeDuplicates($params, true);
        $errorparams = array();
        if (PEAR_Downloader_Package::detectStupidDuplicates($params, $errorparams)) {
            if (count($errorparams)) {
                foreach ($errorparams as $param) {
                    $name = $this->_registry->parsedPackageNameToString($param->getParsedPackage());
                    $this->pushError('Duplicate package ' . $name . ' found', PEAR_INSTALLER_FAILED);
                }
                $a = array();
                return $a;
            }
        }
        PEAR_Downloader_Package::removeInstalled($params);
        if (!count($params)) {
            $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED);
            $a = array();
            return $a;
        }
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        $err = $this->analyzeDependencies($params);
        PEAR::popErrorHandling();
        if (!count($params)) {
            $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED);
            $a = array();
            return $a;
        }
        $ret = array();
        $newparams = array();
        if (isset($this->_options['pretend'])) {
            return $params;
        }
        $somefailed = false;
        foreach ($params as $i => $package) {
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            $pf = &$params[$i]->download();
            PEAR::staticPopErrorHandling();
            if (PEAR::isError($pf)) {
                if (!isset($this->_options['soft'])) {
                    $this->log(1, $pf->getMessage());
                    $this->log(0, 'Error: cannot download "' .
                        $this->_registry->parsedPackageNameToString($package->getParsedPackage(),
                            true) .
                        '"');
                }
                $somefailed = true;
                continue;
            }
            $newparams[] = &$params[$i];
            $ret[] = array('file' => $pf->getArchiveFile(),
                                   'info' => &$pf,
                                   'pkg' => $pf->getPackage());
        }
        if ($somefailed) {
            // remove params that did not download successfully
            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
            $err = $this->analyzeDependencies($newparams, true);
            PEAR::popErrorHandling();
            if (!count($newparams)) {
                $this->pushError('Download failed', PEAR_INSTALLER_FAILED);
                $a = array();
                return $a;
            }
        }
        $this->_downloadedPackages = $ret;
        return $newparams;
    }

    /**
     * @param array all packages to be installed
     */
    function analyzeDependencies(&$params, $force = false)
    {
        $hasfailed = $failed = false;
        if (isset($this->_options['downloadonly'])) {
            return;
        }
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        $redo = true;
        $reset = false;
        while ($redo) {
            $redo = false;
            foreach ($params as $i => $param) {
                $deps = $param->getDeps();
                if (!$deps) {
                    $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(),
                        $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING);
                    if ($param->getType() == 'xmlrpc') {
                        $send = $param->getDownloadURL();
                    } else {
                        $send = $param->getPackageFile();
                    }
                    $installcheck = $depchecker->validatePackage($send, $this, $params);
                    if (PEAR::isError($installcheck)) {
                        if (!isset($this->_options['soft'])) {
                            $this->log(0, $installcheck->getMessage());
                        }
                        $hasfailed = true;
                        $params[$i] = false;
                        $reset = true;
                        $redo = true;
                        $failed = false;
                        PEAR_Downloader_Package::removeDuplicates($params);
                        continue 2;
                    }
                    continue;
                }
                if (!$reset && $param->alreadyValidated() && !$force) {
                    continue;
                }
                if (count($deps)) {
                    $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(),
                        $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING);
                    if ($param->getType() == 'xmlrpc') {
                        $send = $param->getDownloadURL();
                    } else {
                        $send = $param->getPackageFile();
                    }
                    $installcheck = $depchecker->validatePackage($send, $this, $params);
                    if (PEAR::isError($installcheck)) {
                        if (!isset($this->_options['soft'])) {
                            $this->log(0, $installcheck->getMessage());
                        }
                        $hasfailed = true;
                        $params[$i] = false;
                        $reset = true;
                        $redo = true;
                        $failed = false;
                        PEAR_Downloader_Package::removeDuplicates($params);
                        continue 2;
                    }
                    $failed = false;
                    if (isset($deps['required'])) {
                        foreach ($deps['required'] as $type => $dep) {
                            // note: Dependency2 will never return a PEAR_Error if ignore-errors
                            // is specified, so soft is needed to turn off logging
                            if (!isset($dep[0])) {
                                if (PEAR::isError($e = $depchecker->{"validate{$type}Dependency"}($dep,
                                      true, $params))) {
                                    $failed = true;
                                    if (!isset($this->_options['soft'])) {
                                        $this->log(0, $e->getMessage());
                                    }
                                } elseif (is_array($e) && !$param->alreadyValidated()) {
                                    if (!isset($this->_options['soft'])) {
                                        $this->log(0, $e[0]);
                                    }
                                }
                            } else {
                                foreach ($dep as $d) {
                                    if (PEAR::isError($e =
                                          $depchecker->{"validate{$type}Dependency"}($d,
                                          true, $params))) {
                                        $failed = true;
                                        if (!isset($this->_options['soft'])) {
                                            $this->log(0, $e->getMessage());
                                        }
                                    } elseif (is_array($e) && !$param->alreadyValidated()) {
                                        if (!isset($this->_options['soft'])) {
                                            $this->log(0, $e[0]);
                                        }
                                    }
                                }
                            }
                        }
                        if (isset($deps['optional'])) {
                            foreach ($deps['optional'] as $type => $dep) {
                                if (!isset($dep[0])) {
                                    if (PEAR::isError($e =
                                          $depchecker->{"validate{$type}Dependency"}($dep,
                                          false, $params))) {
                                        $failed = true;
                                        if (!isset($this->_options['soft'])) {
                                            $this->log(0, $e->getMessage());
                                        }
                                    } elseif (is_array($e) && !$param->alreadyValidated()) {
                                        if (!isset($this->_options['soft'])) {
                                            $this->log(0, $e[0]);
                                        }
                                    }
                                } else {
                                    foreach ($dep as $d) {
                                        if (PEAR::isError($e =
                                              $depchecker->{"validate{$type}Dependency"}($d,
                                              false, $params))) {
                                            $failed = true;
                                            if (!isset($this->_options['soft'])) {
                                                $this->log(0, $e->getMessage());
                                            }
                                        } elseif (is_array($e) && !$param->alreadyValidated()) {
                                            if (!isset($this->_options['soft'])) {
                                                $this->log(0, $e[0]);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        $groupname = $param->getGroup();
                        if (isset($deps['group']) && $groupname) {
                            if (!isset($deps['group'][0])) {
                                $deps['group'] = array($deps['group']);
                            }
                            $found = false;
                            foreach ($deps['group'] as $group) {
                                if ($group['attribs']['name'] == $groupname) {
                                    $found = true;
                                    break;
                                }
                            }
                            if ($found) {
                                unset($group['attribs']);
                                foreach ($group as $type => $dep) {
                                    if (!isset($dep[0])) {
                                        if (PEAR::isError($e =
                                              $depchecker->{"validate{$type}Dependency"}($dep,
                                              false, $params))) {
                                            $failed = true;
                                            if (!isset($this->_options['soft'])) {
                                                $this->log(0, $e->getMessage());
                                            }
                                        } elseif (is_array($e) && !$param->alreadyValidated()) {
                                            if (!isset($this->_options['soft'])) {
                                                $this->log(0, $e[0]);
                                            }
                                        }
                                    } else {
                                        foreach ($dep as $d) {
                                            if (PEAR::isError($e =
                                                  $depchecker->{"validate{$type}Dependency"}($d,
                                                  false, $params))) {
                                                $failed = true;
                                                if (!isset($this->_options['soft'])) {
                                                    $this->log(0, $e->getMessage());
                                                }
                                            } elseif (is_array($e) && !$param->alreadyValidated()) {
                                                if (!isset($this->_options['soft'])) {
                                                    $this->log(0, $e[0]);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    } else {
                        foreach ($deps as $dep) {
                            if (PEAR::isError($e = $depchecker->validateDependency1($dep, $params))) {
                                $failed = true;
                                if (!isset($this->_options['soft'])) {
                                    $this->log(0, $e->getMessage());
                                }
                            } elseif (is_array($e) && !$param->alreadyValidated()) {
                                if (!isset($this->_options['soft'])) {
                                    $this->log(0, $e[0]);
                                }
                            }
                        }
                    }
                    $params[$i]->setValidated();
                }
                if ($failed) {
                    $hasfailed = true;
                    $params[$i] = false;
                    $reset = true;
                    $redo = true;
                    $failed = false;
                    PEAR_Downloader_Package::removeDuplicates($params);
                    continue 2;
                }
            }
        }
        PEAR::staticPopErrorHandling();
        if ($hasfailed && (isset($this->_options['ignore-errors']) ||
              isset($this->_options['nodeps']))) {
            // this is probably not needed, but just in case
            if (!isset($this->_options['soft'])) {
                $this->log(0, 'WARNING: dependencies failed');
            }
        }
    }

    /**
     * Retrieve the directory that downloads will happen in
     * @access private
     * @return string
     */
    function getDownloadDir()
    {
        if (isset($this->_downloadDir)) {
            return $this->_downloadDir;
        }
        $downloaddir = $this->config->get('download_dir');
        if (empty($downloaddir) || (is_dir($downloaddir) && !is_writable($downloaddir))) {
            if  (is_dir($downloaddir) && !is_writable($downloaddir)) {
                $this->log(0, 'WARNING: configuration download directory "' . $downloaddir .
                    '" is not writeable.  Change download_dir config variable to ' .
                    'a writeable dir to avoid this warning');
            }
            if (!class_exists('System')) {
                require_once 'System.php';
            }
            if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
                return $downloaddir;
            }
            $this->log(3, '+ tmp dir created at ' . $downloaddir);
        }
        if (!is_writable($downloaddir)) {
            if (PEAR::isError(System::mkdir(array('-p', $downloaddir))) ||
                  !is_writable($downloaddir)) {
                return PEAR::raiseError('download directory "' . $downloaddir .
                    '" is not writeable.  Change download_dir config variable to ' .
                    'a writeable dir');
            }
        }
        return $this->_downloadDir = $downloaddir;
    }

    function setDownloadDir($dir)
    {
        if (!@is_writable($dir)) {
            if (PEAR::isError(System::mkdir(array('-p', $dir)))) {
                return PEAR::raiseError('download directory "' . $dir .
                    '" is not writeable.  Change download_dir config variable to ' .
                    'a writeable dir');
            }
        }
        $this->_downloadDir = $dir;
    }

    // }}}
    // {{{ configSet()
    function configSet($key, $value, $layer = 'user', $channel = false)
    {
        $this->config->set($key, $value, $layer, $channel);
        $this->_preferredState = $this->config->get('preferred_state', null, $channel);
        if (!$this->_preferredState) {
            // don't inadvertantly use a non-set preferred_state
            $this->_preferredState = null;
        }
    }

    // }}}
    // {{{ setOptions()
    function setOptions($options)
    {
        $this->_options = $options;
    }

    // }}}
    // {{{ setOptions()
    function getOptions()
    {
        return $this->_options;
    }

    // }}}

    /**
     * For simpler unit-testing
     * @param PEAR_Config
     * @param int
     * @param string
     */
    function &getPackagefileObject(&$c, $d, $t = false)
    {
        if (!class_exists('PEAR_PackageFile')) {
            require_once 'PEAR/PackageFile.php';
        }
        $a = &new PEAR_PackageFile($c, $d, $t);
        return $a;
    }

    // {{{ _getPackageDownloadUrl()

    /**
     * @param array output of {@link parsePackageName()}
     * @access private
     */
    function _getPackageDownloadUrl($parr)
    {
        $curchannel = $this->config->get('default_channel');
        $this->configSet('default_channel', $parr['channel']);
        // getDownloadURL returns an array.  On error, it only contains information
        // on the latest release as array(version, info).  On success it contains
        // array(version, info, download url string)
        $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state');
        if (!$this->_registry->channelExists($parr['channel'])) {
            do {
                if ($this->config->get('auto_discover')) {
                    if ($this->discover($parr['channel'])) {
                        break;
                    }
                }
                $this->configSet('default_channel', $curchannel);
                return PEAR::raiseError('Unknown remote channel: ' . $remotechannel);
            } while (false);
        }
        $chan = &$this->_registry->getChannel($parr['channel']);
        if (PEAR::isError($chan)) {
            return $chan;
        }
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        $version = $this->_registry->packageInfo($parr['package'], 'version',
            $parr['channel']);
        PEAR::staticPopErrorHandling();
        $base2 = false;
        if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
              (($base2 = $chan->getBaseURL('REST1.3', $this->config->get('preferred_mirror'))) ||
              ($base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))))) {
            if ($base2) {
                $rest = &$this->config->getREST('1.3', $this->_options);
                $base = $base2;
            } else {
                $rest = &$this->config->getREST('1.0', $this->_options);
            }
            if (!isset($parr['version']) && !isset($parr['state']) && $version
                  && !PEAR::isError($version)
                  && !isset($this->_options['downloadonly'])) {
                $url = $rest->getDownloadURL($base, $parr, $state, $version, $chan->getName());
            } else {
                $url = $rest->getDownloadURL($base, $parr, $state, false, $chan->getName());
            }
            if (PEAR::isError($url)) {
                $this->configSet('default_channel', $curchannel);
                return $url;
            }
            if ($parr['channel'] != $curchannel) {
                $this->configSet('default_channel', $curchannel);
            }
            if (!is_array($url)) {
                return $url;
            }
            $url['raw'] = false; // no checking is necessary for REST
            if (!is_array($url['info'])) {
                return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' .
                    'this should never happen');
            }
            if (!isset($this->_options['force']) &&
                  !isset($this->_options['downloadonly']) &&
                  $version &&
                  !PEAR::isError($version) &&
                  !isset($parr['group'])) {
                if (version_compare($version, $url['version'], '>=')) {
                    return PEAR::raiseError($this->_registry->parsedPackageNameToString(
                        $parr, true) . ' is already installed and is newer than detected ' .
                        'release version ' . $url['version'], -976);
                }
            }
            if (isset($url['info']['required']) || $url['compatible']) {
                require_once 'PEAR/PackageFile/v2.php';
                $pf = new PEAR_PackageFile_v2;
                $pf->setRawChannel($parr['channel']);
                if ($url['compatible']) {
                    $pf->setRawCompatible($url['compatible']);
                }
            } else {
                require_once 'PEAR/PackageFile/v1.php';
                $pf = new PEAR_PackageFile_v1;
            }
            $pf->setRawPackage($url['package']);
            $pf->setDeps($url['info']);
            if ($url['compatible']) {
                $pf->setCompatible($url['compatible']);
            }
            $pf->setRawState($url['stability']);
            $url['info'] = &$pf;
            if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
                $ext = '.tar';
            } else {
                $ext = '.tgz';
            }
            if (is_array($url)) {
                if (isset($url['url'])) {
                    $url['url'] .= $ext;
                }
            }
            return $url;
        } elseif ($chan->supports('xmlrpc', 'package.getDownloadURL', false, '1.1')) {
            // don't install with the old version information unless we're doing a plain
            // vanilla simple installation.  If the user says to install a particular
            // version or state, ignore the current installed version
            if (!isset($parr['version']) && !isset($parr['state']) && $version
                  && !isset($this->_options['downloadonly'])) {
                $url = $this->_remote->call('package.getDownloadURL', $parr, $state, $version);
            } else {
                $url = $this->_remote->call('package.getDownloadURL', $parr, $state);
            }
        } else {
            $url = $this->_remote->call('package.getDownloadURL', $parr, $state);
        }
        if (PEAR::isError($url)) {
            return $url;
        }
        if ($parr['channel'] != $curchannel) {
            $this->configSet('default_channel', $curchannel);
        }
        if (isset($url['__PEAR_ERROR_CLASS__'])) {
            return PEAR::raiseError($url['message']);
        }
        if (!is_array($url)) {
            return $url;
        }
        $url['raw'] = $url['info'];
        if (isset($this->_options['downloadonly'])) {
            $pkg = &$this->getPackagefileObject($this->config, $this->debug);
        } else {
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            if (PEAR::isError($dir = $this->getDownloadDir())) {
                PEAR::staticPopErrorHandling();
                return $dir;
            }
            PEAR::staticPopErrorHandling();
            $pkg = &$this->getPackagefileObject($this->config, $this->debug, $dir);
        }
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        $pinfo = &$pkg->fromXmlString($url['info'], PEAR_VALIDATE_DOWNLOADING, 'remote');
        PEAR::staticPopErrorHandling();
        if (PEAR::isError($pinfo)) {
            if (!isset($this->_options['soft'])) {
                $this->log(0, $pinfo->getMessage());
            }
            return PEAR::raiseError('Remote package.xml is not valid - this should never happen');
        }
        $url['info'] = &$pinfo;
        if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
            $ext = '.tar';
        } else {
            $ext = '.tgz';
        }
        if (is_array($url)) {
            if (isset($url['url'])) {
                $url['url'] .= $ext;
            }
        }
        return $url;
    }
    // }}}
    // {{{ getDepPackageDownloadUrl()

    /**
     * @param array dependency array
     * @access private
     */
    function _getDepPackageDownloadUrl($dep, $parr)
    {
        $xsdversion = isset($dep['rel']) ? '1.0' : '2.0';
        $curchannel = $this->config->get('default_channel');
        if (isset($dep['uri'])) {
            $xsdversion = '2.0';
            $chan = &$this->_registry->getChannel('__uri');
            if (PEAR::isError($chan)) {
                return $chan;
            }
            $version = $this->_registry->packageInfo($dep['name'], 'version', '__uri');
            $this->configSet('default_channel', '__uri');
        } else {
            if (isset($dep['channel'])) {
                $remotechannel = $dep['channel'];
            } else {
                $remotechannel = 'pear.php.net';
            }
            if (!$this->_registry->channelExists($remotechannel)) {
                do {
                    if ($this->config->get('auto_discover')) {
                        if ($this->discover($remotechannel)) {
                            break;
                        }
                    }
                    return PEAR::raiseError('Unknown remote channel: ' . $remotechannel);
                } while (false);
            }
            $chan = &$this->_registry->getChannel($remotechannel);
            if (PEAR::isError($chan)) {
                return $chan;
            }
            $version = $this->_registry->packageInfo($dep['name'], 'version',
                $remotechannel);
            $this->configSet('default_channel', $remotechannel);
        }
        $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state');
        if (isset($parr['state']) && isset($parr['version'])) {
            unset($parr['state']);
        }
        if (isset($dep['uri'])) {
            $info = &$this->newDownloaderPackage($this);
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            $err = $info->initialize($dep);
            PEAR::staticPopErrorHandling();
            if (!$err) {
                // skip parameters that were missed by preferred_state
                return PEAR::raiseError('Cannot initialize dependency');
            }
            if (PEAR::isError($err)) {
                if (!isset($this->_options['soft'])) {
                    $this->log(0, $err->getMessage());
                }
                if (is_object($info)) {
                    $param = $info->getChannel() . '/' . $info->getPackage();
                }
                return PEAR::raiseError('Package "' . $param . '" is not valid');
            }
            return $info;
        } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) &&
              $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
            $rest = &$this->config->getREST('1.0', $this->_options);
            $url = $rest->getDepDownloadURL($base, $xsdversion, $dep, $parr,
                    $state, $version, $chan->getName());
            if (PEAR::isError($url)) {
                return $url;
            }
            if ($parr['channel'] != $curchannel) {
                $this->configSet('default_channel', $curchannel);
            }
            if (!is_array($url)) {
                return $url;
            }
            $url['raw'] = false; // no checking is necessary for REST
            if (!is_array($url['info'])) {
                return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' .
                    'this should never happen');
            }
            if (isset($url['info']['required'])) {
                if (!class_exists('PEAR_PackageFile_v2')) {
                    require_once 'PEAR/PackageFile/v2.php';
                }
                $pf = new PEAR_PackageFile_v2;
                $pf->setRawChannel($remotechannel);
            } else {
                if (!class_exists('PEAR_PackageFile_v1')) {
                    require_once 'PEAR/PackageFile/v1.php';
                }
                $pf = new PEAR_PackageFile_v1;
            }
            $pf->setRawPackage($url['package']);
            $pf->setDeps($url['info']);
            if ($url['compatible']) {
                $pf->setCompatible($url['compatible']);
            }
            $pf->setRawState($url['stability']);
            $url['info'] = &$pf;
            if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
                $ext = '.tar';
            } else {
                $ext = '.tgz';
            }
            if (is_array($url)) {
                if (isset($url['url'])) {
                    $url['url'] .= $ext;
                }
            }
            return $url;
        } elseif ($chan->supports('xmlrpc', 'package.getDepDownloadURL', false, '1.1')) {
            if ($version) {
                $url = $this->_remote->call('package.getDepDownloadURL', $xsdversion, $dep, $parr,
                    $state, $version);
            } else {
                $url = $this->_remote->call('package.getDepDownloadURL', $xsdversion, $dep, $parr,
                    $state);
            }
        } else {
            $url = $this->_remote->call('package.getDepDownloadURL', $xsdversion, $dep, $parr, $state);
        }
        if ($this->config->get('default_channel') != $curchannel) {
            $this->configSet('default_channel', $curchannel);
        }
        if (!is_array($url)) {
            return $url;
        }
        if (isset($url['__PEAR_ERROR_CLASS__'])) {
            return PEAR::raiseError($url['message']);
        }
        $url['raw'] = $url['info'];
        $pkg = &$this->getPackagefileObject($this->config, $this->debug);
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        $pinfo = &$pkg->fromXmlString($url['info'], PEAR_VALIDATE_DOWNLOADING, 'remote');
        PEAR::staticPopErrorHandling();
        if (PEAR::isError($pinfo)) {
            if (!isset($this->_options['soft'])) {
                $this->log(0, $pinfo->getMessage());
            }
            return PEAR::raiseError('Remote package.xml is not valid - this should never happen');
        }
        $url['info'] = &$pinfo;
        if (is_array($url)) {
            if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
                $ext = '.tar';
            } else {
                $ext = '.tgz';
            }
            if (isset($url['url'])) {
                $url['url'] .= $ext;
            }
        }
        return $url;
    }
    // }}}
    // {{{ getPackageDownloadUrl()

    /**
     * @deprecated in favor of _getPackageDownloadUrl
     */
    function getPackageDownloadUrl($package, $version = null, $channel = false)
    {
        if ($version) {
            $package .= "-$version";
        }
        if ($this === null || $this->_registry === null) {
            $package = "http://pear.php.net/get/$package";
        } else {
            $chan = $this->_registry->getChannel($channel);
            if (PEAR::isError($chan)) {
                return '';
            }
            $package = "http://" . $chan->getServer() . "/get/$package";
        }
        if (!extension_loaded("zlib")) {
            $package .= '?uncompress=yes';
        }
        return $package;
    }

    // }}}
    // {{{ 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;
    }

    // }}}
    // {{{ _downloadCallback()

    function _downloadCallback($msg, $params = null)
    {
        switch ($msg) {
            case 'saveas':
                $this->log(1, "downloading $params ...");
                break;
            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':
                if($params[1] == -1) {
                    $length = "Unknown size";
                } else {
                    $length = number_format($params[1], 0, '', ',')." bytes";
                }
                $this->log(1, "Starting to download {$params[0]} ($length)");
                break;
        }
        if (method_exists($this->ui, '_downloadCallback'))
            $this->ui->_downloadCallback($msg, $params);
    }

    // }}}
    // {{{ _prependPath($path, $prepend)

    function _prependPath($path, $prepend)
    {
        if (strlen($prepend) > 0) {
            if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
                if (preg_match('/^[a-z]:/i', $prepend)) {
                    $prepend = substr($prepend, 2);
                } elseif ($prepend{0} != '\\') {
                    $prepend = "\\$prepend";
                }
                $path = substr($path, 0, 2) . $prepend . substr($path, 2);
            } else {
                $path = $prepend . $path;
            }
        }
        return $path;
    }
    // }}}
    // {{{ 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;
    }

    // }}}

    /**
     * for BC
     */
    function sortPkgDeps(&$packages, $uninstall = false)
    {
        $uninstall ?
            $this->sortPackagesForUninstall($packages) :
            $this->sortPackagesForInstall($packages);
    }

    /**
     * Sort a list of arrays of array(downloaded packagefilename) by dependency.
     *
     * This uses the topological sort method from graph theory, and the
     * Structures_Graph package to properly sort dependencies for installation.
     * @param array an array of downloaded PEAR_Downloader_Packages
     * @return array array of array(packagefilename, package.xml contents)
     */
    function sortPackagesForInstall(&$packages)
    {
        require_once 'Structures/Graph.php';
        require_once 'Structures/Graph/Node.php';
        require_once 'Structures/Graph/Manipulator/TopologicalSorter.php';
        $depgraph = new Structures_Graph(true);
        $nodes = array();
        $reg = &$this->config->getRegistry();
        foreach ($packages as $i => $package) {
            $pname = $reg->parsedPackageNameToString(
                array(
                    'channel' => $package->getChannel(),
                    'package' => strtolower($package->getPackage()),
                ));
            $nodes[$pname] = new Structures_Graph_Node;
            $nodes[$pname]->setData($packages[$i]);
            $depgraph->addNode($nodes[$pname]);
        }
        $deplinks = array();
        foreach ($nodes as $package => $node) {
            $pf = &$node->getData();
            $pdeps = $pf->getDeps(true);
            if (!$pdeps) {
                continue;
            }
            if ($pf->getPackagexmlVersion() == '1.0') {
                foreach ($pdeps as $dep) {
                    if ($dep['type'] != 'pkg' ||
                          (isset($dep['optional']) && $dep['optional'] == 'yes')) {
                        continue;
                    }
                    $dname = $reg->parsedPackageNameToString(
                          array(
                              'channel' => 'pear.php.net',
                              'package' => strtolower($dep['name']),
                          ));
                    if (isset($nodes[$dname]))
                    {
                        if (!isset($deplinks[$dname])) {
                            $deplinks[$dname] = array();
                        }
                        $deplinks[$dname][$package] = 1;
                        // dependency is in installed packages
                        continue;
                    }
                    $dname = $reg->parsedPackageNameToString(
                          array(
                              'channel' => 'pecl.php.net',
                              'package' => strtolower($dep['name']),
                          ));
                    if (isset($nodes[$dname]))
                    {
                        if (!isset($deplinks[$dname])) {
                            $deplinks[$dname] = array();
                        }
                        $deplinks[$dname][$package] = 1;
                        // dependency is in installed packages
                        continue;
                    }
                }
            } else {
                // the only ordering we care about is:
                // 1) subpackages must be installed before packages that depend on them
                // 2) required deps must be installed before packages that depend on them
                if (isset($pdeps['required']['subpackage'])) {
                    $t = $pdeps['required']['subpackage'];
                    if (!isset($t[0])) {
                        $t = array($t);
                    }
                    $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
                }
                if (isset($pdeps['group'])) {
                    if (!isset($pdeps['group'][0])) {
                        $pdeps['group'] = array($pdeps['group']);
                    }
                    foreach ($pdeps['group'] as $group) {
                        if (isset($group['subpackage'])) {
                            $t = $group['subpackage'];
                            if (!isset($t[0])) {
                                $t = array($t);
                            }
                            $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
                        }
                    }
                }
                if (isset($pdeps['optional']['subpackage'])) {
                    $t = $pdeps['optional']['subpackage'];
                    if (!isset($t[0])) {
                        $t = array($t);
                    }
                    $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
                }
                if (isset($pdeps['required']['package'])) {
                    $t = $pdeps['required']['package'];
                    if (!isset($t[0])) {
                        $t = array($t);
                    }
                    $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
                }
                if (isset($pdeps['group'])) {
                    if (!isset($pdeps['group'][0])) {
                        $pdeps['group'] = array($pdeps['group']);
                    }
                    foreach ($pdeps['group'] as $group) {
                        if (isset($group['package'])) {
                            $t = $group['package'];
                            if (!isset($t[0])) {
                                $t = array($t);
                            }
                            $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
                        }
                    }
                }
            }
        }
        $this->_detectDepCycle($deplinks);
        foreach ($deplinks as $dependent => $parents) {
            foreach ($parents as $parent => $unused) {
                $nodes[$dependent]->connectTo($nodes[$parent]);
            }
        }
        $installOrder = Structures_Graph_Manipulator_TopologicalSorter::sort($depgraph);
        $ret = array();
        for ($i = 0; $i < count($installOrder); $i++) {
            foreach ($installOrder[$i] as $index => $sortedpackage) {
                $data = &$installOrder[$i][$index]->getData();
                $ret[] = &$nodes[$reg->parsedPackageNameToString(
                          array(
                              'channel' => $data->getChannel(),
                              'package' => strtolower($data->getPackage()),
                          ))]->getData();
            }
        }
        $packages = $ret;
        return;
    }

    /**
     * Detect recursive links between dependencies and break the cycles
     *
     * @param array
     * @access private
     */
    function _detectDepCycle(&$deplinks)
    {
        do {
            $keepgoing = false;
            foreach ($deplinks as $dep => $parents) {
                foreach ($parents as $parent => $unused) {
                    // reset the parent cycle detector
                    $this->_testCycle(null, null, null);
                    if ($this->_testCycle($dep, $deplinks, $parent)) {
                        $keepgoing = true;
                        unset($deplinks[$dep][$parent]);
                        if (count($deplinks[$dep]) == 0) {
                            unset($deplinks[$dep]);
                        }
                        continue 3;
                    }
                }
            }
        } while ($keepgoing);
    }

    function _testCycle($test, $deplinks, $dep)
    {
        static $visited = array();
        if ($test === null) {
            $visited = array();
            return;
        }
        // this happens when a parent has a dep cycle on another dependency
        // but the child is not part of the cycle
        if (isset($visited[$dep])) {
            return false;
        }
        $visited[$dep] = 1;
        if ($test == $dep) {
            return true;
        }
        if (isset($deplinks[$dep])) {
            if (in_array($test, array_keys($deplinks[$dep]), true)) {
                return true;
            }
            foreach ($deplinks[$dep] as $parent => $unused) {
                if ($this->_testCycle($test, $deplinks, $parent)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Set up the dependency for installation parsing
     *
     * @param array $t dependency information
     * @param PEAR_Registry $reg
     * @param array $deplinks list of dependency links already established
     * @param array $nodes all existing package nodes
     * @param string $package parent package name
     * @access private
     */
    function _setupGraph($t, $reg, &$deplinks, &$nodes, $package)
    {
        foreach ($t as $dep) {
            $depchannel = !isset($dep['channel']) ?
                '__uri': $dep['channel'];
            $dname = $reg->parsedPackageNameToString(
                  array(
                      'channel' => $depchannel,
                      'package' => strtolower($dep['name']),
                  ));
            if (isset($nodes[$dname]))
            {
                if (!isset($deplinks[$dname])) {
                    $deplinks[$dname] = array();
                }
                $deplinks[$dname][$package] = 1;
            }
        }
    }

    function _dependsOn($a, $b)
    {
        return $this->_checkDepTree(strtolower($a->getChannel()), strtolower($a->getPackage()),
            $b);
    }

    function _checkDepTree($channel, $package, $b, $checked = array())
    {
        $checked[$channel][$package] = true;
        if (!isset($this->_depTree[$channel][$package])) {
            return false;
        }
        if (isset($this->_depTree[$channel][$package][strtolower($b->getChannel())]
              [strtolower($b->getPackage())])) {
            return true;
        }
        foreach ($this->_depTree[$channel][$package] as $ch => $packages) {
            foreach ($packages as $pa => $true) {
                if ($this->_checkDepTree($ch, $pa, $b, $checked)) {
                    return true;
                }
            }
        }
        return false;
    }

    function _sortInstall($a, $b)
    {
        if (!$a->getDeps() && !$b->getDeps()) {
            return 0; // neither package has dependencies, order is insignificant
        }
        if ($a->getDeps() && !$b->getDeps()) {
            return 1; // $a must be installed after $b because $a has dependencies
        }
        if (!$a->getDeps() && $b->getDeps()) {
            return -1; // $b must be installed after $a because $b has dependencies
        }
        // both packages have dependencies
        if ($this->_dependsOn($a, $b)) {
            return 1;
        }
        if ($this->_dependsOn($b, $a)) {
            return -1;
        }
        return 0;
    }

    /**
     * Download a file through HTTP.  Considers suggested file name in
     * Content-disposition: header and can run a callback function for
     * different events.  The callback will be called with two
     * parameters: the callback type, and parameters.  The implemented
     * callback types are:
     *
     *  'setup'       called at the very beginning, parameter is a UI object
     *                that should be used for all output
     *  'message'     the parameter is a string with an informational message
     *  'saveas'      may be used to save with a different file name, the
     *                parameter is the filename that is about to be used.
     *                If a 'saveas' callback returns a non-empty string,
     *                that file name will be used as the filename instead.
     *                Note that $save_dir will not be affected by this, only
     *                the basename of the file.
     *  'start'       download is starting, parameter is number of bytes
     *                that are expected, or -1 if unknown
     *  'bytesread'   parameter is the number of bytes read so far
     *  'done'        download is complete, parameter is the total number
     *                of bytes read
     *  'connfailed'  if the TCP/SSL connection fails, this callback is called
     *                with array(host,port,errno,errmsg)
     *  'writefailed' if writing to disk fails, this callback is called
     *                with array(destfile,errmsg)
     *
     * If an HTTP proxy has been configured (http_proxy PEAR_Config
     * setting), the proxy will be used.
     *
     * @param string  $url       the URL to download
     * @param object  $ui        PEAR_Frontend_* instance
     * @param object  $config    PEAR_Config instance
     * @param string  $save_dir  directory to save file in
     * @param mixed   $callback  function/method to call for status
     *                           updates
     * @param false|string|array $lastmodified header values to check against for caching
     *                           use false to return the header values from this download
     * @param false|array $accept Accept headers to send
     * @param false|string $channel Channel to use for retrieving authentication
     * @return string|array  Returns the full path of the downloaded file or a PEAR
     *                       error on failure.  If the error is caused by
     *                       socket-related errors, the error object will
     *                       have the fsockopen error code available through
     *                       getCode().  If caching is requested, then return the header
     *                       values.
     *
     * @access public
     */
    function downloadHttp($url, &$ui, $save_dir = '.', $callback = null, $lastmodified = null,
                          $accept = false, $channel = false)
    {
        static $redirect = 0;
        // allways reset , so we are clean case of error
        $wasredirect = $redirect;
        $redirect = 0;
        if ($callback) {
            call_user_func($callback, 'setup', array(&$ui));
        }
        $info = parse_url($url);
        if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) {
            return PEAR::raiseError('Cannot download non-http URL "' . $url . '"');
        }
        if (!isset($info['host'])) {
            return PEAR::raiseError('Cannot download from non-URL "' . $url . '"');
        } else {
            $host = isset($info['host']) ? $info['host'] : null;
            $port = isset($info['port']) ? $info['port'] : null;
            $path = isset($info['path']) ? $info['path'] : null;
        }
        if (isset($this)) {
            $config = &$this->config;
        } else {
            $config = &PEAR_Config::singleton();
        }
        $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
        if ($config->get('http_proxy') &&
              $proxy = parse_url($config->get('http_proxy'))) {
            $proxy_host = isset($proxy['host']) ? $proxy['host'] : null;
            if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') {
                $proxy_host = 'ssl://' . $proxy_host;
            }
            $proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080;
            $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null;
            $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null;

            if ($callback) {
                call_user_func($callback, 'message', "Using HTTP proxy $host:$port");
            }
        }
        if (empty($port)) {
            if (isset($info['scheme']) && $info['scheme'] == 'https') {
                $port = 443;
            } else {
                $port = 80;
            }
        }
        if ($proxy_host != '') {
            $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr);
            if (!$fp) {
                if ($callback) {
                    call_user_func($callback, 'connfailed', array($proxy_host, $proxy_port,
                                                                  $errno, $errstr));
                }
                return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", $errno);
            }
            if ($lastmodified === false || $lastmodified) {
                $request = "GET $url HTTP/1.1\r\n";
            } else {
                $request = "GET $url HTTP/1.0\r\n";
            }
        } else {
            if (isset($info['scheme']) && $info['scheme'] == 'https') {
                $host = 'ssl://' . $host;
            }
            $fp = @fsockopen($host, $port, $errno, $errstr);
            if (!$fp) {
                if ($callback) {
                    call_user_func($callback, 'connfailed', array($host, $port,
                                                                  $errno, $errstr));
                }
                return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno);
            }
            if ($lastmodified === false || $lastmodified) {
                $request = "GET $path HTTP/1.1\r\n";
                $request .= "Host: $host:$port\r\n";
            } else {
                $request = "GET $path HTTP/1.0\r\n";
                $request .= "Host: $host\r\n";
            }
        }
        $ifmodifiedsince = '';
        if (is_array($lastmodified)) {
            if (isset($lastmodified['Last-Modified'])) {
                $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n";
            }
            if (isset($lastmodified['ETag'])) {
                $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n";
            }
        } else {
            $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : '');
        }
        $request .= $ifmodifiedsince . "User-Agent: PEAR/1.7.2/PHP/" .
            PHP_VERSION . "\r\n";
        if (isset($this)) { // only pass in authentication for non-static calls
            $username = $config->get('username', null, $channel);
            $password = $config->get('password', null, $channel);
            if ($username && $password) {
                $tmp = base64_encode("$username:$password");
                $request .= "Authorization: Basic $tmp\r\n";
            }
        }
        if ($proxy_host != '' && $proxy_user != '') {
            $request .= 'Proxy-Authorization: Basic ' .
                base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n";
        }
        if ($accept) {
            $request .= 'Accept: ' . implode(', ', $accept) . "\r\n";
        }
        $request .= "Connection: close\r\n";
        $request .= "\r\n";
        fwrite($fp, $request);
        $headers = array();
        $reply = 0;
        while (trim($line = fgets($fp, 1024))) {
            if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) {
                $headers[strtolower($matches[1])] = trim($matches[2]);
            } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
                $reply = (int) $matches[1];
                if ($reply == 304 && ($lastmodified || ($lastmodified === false))) {
                    return false;
                }
                if (! in_array($reply, array(200, 301, 302, 303, 305, 307))) {
                    return PEAR::raiseError("File http://$host:$port$path not valid (received: $line)");
                }
            }
        }
        if ($reply != 200) {
            if (isset($headers['location'])) {
                if ($wasredirect < 5) {
                    $redirect = $wasredirect + 1;
                    return $this->downloadHttp($headers['location'],
                            $ui, $save_dir, $callback, $lastmodified, $accept);
                } else {
                    return PEAR::raiseError("File http://$host:$port$path not valid (redirection looped more than 5 times)");
                }
            } else {
                return PEAR::raiseError("File http://$host:$port$path not valid (redirected but no location)");
            }
        }
        if (isset($headers['content-disposition']) &&
            preg_match('/\sfilename=\"([^;]*\S)\"\s*(;|\\z)/', $headers['content-disposition'], $matches)) {
            $save_as = basename($matches[1]);
        } else {
            $save_as = basename($url);
        }
        if ($callback) {
            $tmp = call_user_func($callback, 'saveas', $save_as);
            if ($tmp) {
                $save_as = $tmp;
            }
        }
        $dest_file = $save_dir . DIRECTORY_SEPARATOR . $save_as;
        if (!$wp = @fopen($dest_file, 'wb')) {
            fclose($fp);
            if ($callback) {
                call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg));
            }
            return PEAR::raiseError("could not open $dest_file for writing");
        }
        if (isset($headers['content-length'])) {
            $length = $headers['content-length'];
        } else {
            $length = -1;
        }
        $bytes = 0;
        if ($callback) {
            call_user_func($callback, 'start', array(basename($dest_file), $length));
        }
        while ($data = fread($fp, 1024)) {
            $bytes += strlen($data);
            if ($callback) {
                call_user_func($callback, 'bytesread', $bytes);
            }
            if (!@fwrite($wp, $data)) {
                fclose($fp);
                if ($callback) {
                    call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg));
                }
                return PEAR::raiseError("$dest_file: write failed ($php_errormsg)");
            }
        }
        fclose($fp);
        fclose($wp);
        if ($callback) {
            call_user_func($callback, 'done', $bytes);
        }
        if ($lastmodified === false || $lastmodified) {
            if (isset($headers['etag'])) {
                $lastmodified = array('ETag' => $headers['etag']);
            }
            if (isset($headers['last-modified'])) {
                if (is_array($lastmodified)) {
                    $lastmodified['Last-Modified'] = $headers['last-modified'];
                } else {
                    $lastmodified = $headers['last-modified'];
                }
            }
            return array($dest_file, $lastmodified, $headers);
        }
        return $dest_file;
    }
}
// }}}

?>
PEAR-1.7.2/PEAR/ErrorStack.php100644   1750   1750      102233 11014312742  11045 <?php
/**
 * Error Stack Implementation
 * 
 * This is an incredibly simple implementation of a very complex error handling
 * facility.  It contains the ability
 * to track multiple errors from multiple packages simultaneously.  In addition,
 * it can track errors of many levels, save data along with the error, context
 * information such as the exact file, line number, class and function that
 * generated the error, and if necessary, it can raise a traditional PEAR_Error.
 * It has built-in support for PEAR::Log, to log errors as they occur
 * 
 * Since version 0.2alpha, it is also possible to selectively ignore errors,
 * through the use of an error callback, see {@link pushCallback()}
 * 
 * Since version 0.3alpha, it is possible to specify the exception class
 * returned from {@link push()}
 *
 * Since version PEAR1.3.2, ErrorStack no longer instantiates an exception class.  This can
 * still be done quite handily in an error callback or by manipulating the returned array
 * @category   Debugging
 * @package    PEAR_ErrorStack
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  2004-2008 Greg Beaver
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: ErrorStack.php,v 1.28 2008/01/03 20:26:35 cellog Exp $
 * @link       http://pear.php.net/package/PEAR_ErrorStack
 */

/**
 * Singleton storage
 * 
 * Format:
 * <pre>
 * array(
 *  'package1' => PEAR_ErrorStack object,
 *  'package2' => PEAR_ErrorStack object,
 *  ...
 * )
 * </pre>
 * @access private
 * @global array $GLOBALS['_PEAR_ERRORSTACK_SINGLETON']
 */
$GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array();

/**
 * Global error callback (default)
 * 
 * This is only used if set to non-false.  * is the default callback for
 * all packages, whereas specific packages may set a default callback
 * for all instances, regardless of whether they are a singleton or not.
 *
 * To exclude non-singletons, only set the local callback for the singleton
 * @see PEAR_ErrorStack::setDefaultCallback()
 * @access private
 * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']
 */
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = array(
    '*' => false,
);

/**
 * Global Log object (default)
 * 
 * This is only used if set to non-false.  Use to set a default log object for
 * all stacks, regardless of instantiation order or location
 * @see PEAR_ErrorStack::setDefaultLogger()
 * @access private
 * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
 */
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = false;

/**
 * Global Overriding Callback
 * 
 * This callback will override any error callbacks that specific loggers have set.
 * Use with EXTREME caution
 * @see PEAR_ErrorStack::staticPushCallback()
 * @access private
 * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
 */
$GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();

/**#@+
 * One of four possible return values from the error Callback
 * @see PEAR_ErrorStack::_errorCallback()
 */
/**
 * If this is returned, then the error will be both pushed onto the stack
 * and logged.
 */
define('PEAR_ERRORSTACK_PUSHANDLOG', 1);
/**
 * If this is returned, then the error will only be pushed onto the stack,
 * and not logged.
 */
define('PEAR_ERRORSTACK_PUSH', 2);
/**
 * If this is returned, then the error will only be logged, but not pushed
 * onto the error stack.
 */
define('PEAR_ERRORSTACK_LOG', 3);
/**
 * If this is returned, then the error is completely ignored.
 */
define('PEAR_ERRORSTACK_IGNORE', 4);
/**
 * If this is returned, then the error is logged and die() is called.
 */
define('PEAR_ERRORSTACK_DIE', 5);
/**#@-*/

/**
 * Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in
 * the singleton method.
 */
define('PEAR_ERRORSTACK_ERR_NONCLASS', 1);

/**
 * Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()}
 * that has no __toString() method
 */
define('PEAR_ERRORSTACK_ERR_OBJTOSTRING', 2);
/**
 * Error Stack Implementation
 *
 * Usage:
 * <code>
 * // global error stack
 * $global_stack = &PEAR_ErrorStack::singleton('MyPackage');
 * // local error stack
 * $local_stack = new PEAR_ErrorStack('MyPackage');
 * </code>
 * @author     Greg Beaver <cellog@php.net>
 * @version    1.7.2
 * @package    PEAR_ErrorStack
 * @category   Debugging
 * @copyright  2004-2008 Greg Beaver
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: ErrorStack.php,v 1.28 2008/01/03 20:26:35 cellog Exp $
 * @link       http://pear.php.net/package/PEAR_ErrorStack
 */
class PEAR_ErrorStack {
    /**
     * Errors are stored in the order that they are pushed on the stack.
     * @since 0.4alpha Errors are no longer organized by error level.
     * This renders pop() nearly unusable, and levels could be more easily
     * handled in a callback anyway
     * @var array
     * @access private
     */
    var $_errors = array();

    /**
     * Storage of errors by level.
     *
     * Allows easy retrieval and deletion of only errors from a particular level
     * @since PEAR 1.4.0dev
     * @var array
     * @access private
     */
    var $_errorsByLevel = array();

    /**
     * Package name this error stack represents
     * @var string
     * @access protected
     */
    var $_package;
    
    /**
     * Determines whether a PEAR_Error is thrown upon every error addition
     * @var boolean
     * @access private
     */
    var $_compat = false;
    
    /**
     * If set to a valid callback, this will be used to generate the error
     * message from the error code, otherwise the message passed in will be
     * used
     * @var false|string|array
     * @access private
     */
    var $_msgCallback = false;
    
    /**
     * If set to a valid callback, this will be used to generate the error
     * context for an error.  For PHP-related errors, this will be a file
     * and line number as retrieved from debug_backtrace(), but can be
     * customized for other purposes.  The error might actually be in a separate
     * configuration file, or in a database query.
     * @var false|string|array
     * @access protected
     */
    var $_contextCallback = false;
    
    /**
     * If set to a valid callback, this will be called every time an error
     * is pushed onto the stack.  The return value will be used to determine
     * whether to allow an error to be pushed or logged.
     * 
     * The return value must be one an PEAR_ERRORSTACK_* constant
     * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
     * @var false|string|array
     * @access protected
     */
    var $_errorCallback = array();
    
    /**
     * PEAR::Log object for logging errors
     * @var false|Log
     * @access protected
     */
    var $_logger = false;
    
    /**
     * Error messages - designed to be overridden
     * @var array
     * @abstract
     */
    var $_errorMsgs = array();
    
    /**
     * Set up a new error stack
     * 
     * @param string   $package name of the package this error stack represents
     * @param callback $msgCallback callback used for error message generation
     * @param callback $contextCallback callback used for context generation,
     *                 defaults to {@link getFileLine()}
     * @param boolean  $throwPEAR_Error
     */
    function PEAR_ErrorStack($package, $msgCallback = false, $contextCallback = false,
                         $throwPEAR_Error = false)
    {
        $this->_package = $package;
        $this->setMessageCallback($msgCallback);
        $this->setContextCallback($contextCallback);
        $this->_compat = $throwPEAR_Error;
    }
    
    /**
     * Return a single error stack for this package.
     * 
     * Note that all parameters are ignored if the stack for package $package
     * has already been instantiated
     * @param string   $package name of the package this error stack represents
     * @param callback $msgCallback callback used for error message generation
     * @param callback $contextCallback callback used for context generation,
     *                 defaults to {@link getFileLine()}
     * @param boolean  $throwPEAR_Error
     * @param string   $stackClass class to instantiate
     * @static
     * @return PEAR_ErrorStack
     */
    function &singleton($package, $msgCallback = false, $contextCallback = false,
                         $throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack')
    {
        if (isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
            return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package];
        }
        if (!class_exists($stackClass)) {
            if (function_exists('debug_backtrace')) {
                $trace = debug_backtrace();
            }
            PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_NONCLASS,
                'exception', array('stackclass' => $stackClass),
                'stack class "%stackclass%" is not a valid class name (should be like PEAR_ErrorStack)',
                false, $trace);
        }
        $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package] =
            new $stackClass($package, $msgCallback, $contextCallback, $throwPEAR_Error);

        return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package];
    }

    /**
     * Internal error handler for PEAR_ErrorStack class
     * 
     * Dies if the error is an exception (and would have died anyway)
     * @access private
     */
    function _handleError($err)
    {
        if ($err['level'] == 'exception') {
            $message = $err['message'];
            if (isset($_SERVER['REQUEST_URI'])) {
                echo '<br />';
            } else {
                echo "\n";
            }
            var_dump($err['context']);
            die($message);
        }
    }
    
    /**
     * Set up a PEAR::Log object for all error stacks that don't have one
     * @param Log $log 
     * @static
     */
    function setDefaultLogger(&$log)
    {
        if (is_object($log) && method_exists($log, 'log') ) {
            $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
        } elseif (is_callable($log)) {
            $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
	}
    }
    
    /**
     * Set up a PEAR::Log object for this error stack
     * @param Log $log 
     */
    function setLogger(&$log)
    {
        if (is_object($log) && method_exists($log, 'log') ) {
            $this->_logger = &$log;
        } elseif (is_callable($log)) {
            $this->_logger = &$log;
        }
    }
    
    /**
     * Set an error code => error message mapping callback
     * 
     * This method sets the callback that can be used to generate error
     * messages for any instance
     * @param array|string Callback function/method
     */
    function setMessageCallback($msgCallback)
    {
        if (!$msgCallback) {
            $this->_msgCallback = array(&$this, 'getErrorMessage');
        } else {
            if (is_callable($msgCallback)) {
                $this->_msgCallback = $msgCallback;
            }
        }
    }
    
    /**
     * Get an error code => error message mapping callback
     * 
     * This method returns the current callback that can be used to generate error
     * messages
     * @return array|string|false Callback function/method or false if none
     */
    function getMessageCallback()
    {
        return $this->_msgCallback;
    }
    
    /**
     * Sets a default callback to be used by all error stacks
     * 
     * This method sets the callback that can be used to generate error
     * messages for a singleton
     * @param array|string Callback function/method
     * @param string Package name, or false for all packages
     * @static
     */
    function setDefaultCallback($callback = false, $package = false)
    {
        if (!is_callable($callback)) {
            $callback = false;
        }
        $package = $package ? $package : '*';
        $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$package] = $callback;
    }
    
    /**
     * Set a callback that generates context information (location of error) for an error stack
     * 
     * This method sets the callback that can be used to generate context
     * information for an error.  Passing in NULL will disable context generation
     * and remove the expensive call to debug_backtrace()
     * @param array|string|null Callback function/method
     */
    function setContextCallback($contextCallback)
    {
        if ($contextCallback === null) {
            return $this->_contextCallback = false;
        }
        if (!$contextCallback) {
            $this->_contextCallback = array(&$this, 'getFileLine');
        } else {
            if (is_callable($contextCallback)) {
                $this->_contextCallback = $contextCallback;
            }
        }
    }
    
    /**
     * Set an error Callback
     * If set to a valid callback, this will be called every time an error
     * is pushed onto the stack.  The return value will be used to determine
     * whether to allow an error to be pushed or logged.
     * 
     * The return value must be one of the ERRORSTACK_* constants.
     * 
     * This functionality can be used to emulate PEAR's pushErrorHandling, and
     * the PEAR_ERROR_CALLBACK mode, without affecting the integrity of
     * the error stack or logging
     * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
     * @see popCallback()
     * @param string|array $cb
     */
    function pushCallback($cb)
    {
        array_push($this->_errorCallback, $cb);
    }
    
    /**
     * Remove a callback from the error callback stack
     * @see pushCallback()
     * @return array|string|false
     */
    function popCallback()
    {
        if (!count($this->_errorCallback)) {
            return false;
        }
        return array_pop($this->_errorCallback);
    }
    
    /**
     * Set a temporary overriding error callback for every package error stack
     *
     * Use this to temporarily disable all existing callbacks (can be used
     * to emulate the @ operator, for instance)
     * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
     * @see staticPopCallback(), pushCallback()
     * @param string|array $cb
     * @static
     */
    function staticPushCallback($cb)
    {
        array_push($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'], $cb);
    }
    
    /**
     * Remove a temporary overriding error callback
     * @see staticPushCallback()
     * @return array|string|false
     * @static
     */
    function staticPopCallback()
    {
        $ret = array_pop($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK']);
        if (!is_array($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'])) {
            $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
        }
        return $ret;
    }
    
    /**
     * Add an error to the stack
     * 
     * If the message generator exists, it is called with 2 parameters.
     *  - the current Error Stack object
     *  - an array that is in the same format as an error.  Available indices
     *    are 'code', 'package', 'time', 'params', 'level', and 'context'
     * 
     * Next, if the error should contain context information, this is
     * handled by the context grabbing method.
     * Finally, the error is pushed onto the proper error stack
     * @param int    $code      Package-specific error code
     * @param string $level     Error level.  This is NOT spell-checked
     * @param array  $params    associative array of error parameters
     * @param string $msg       Error message, or a portion of it if the message
     *                          is to be generated
     * @param array  $repackage If this error re-packages an error pushed by
     *                          another package, place the array returned from
     *                          {@link pop()} in this parameter
     * @param array  $backtrace Protected parameter: use this to pass in the
     *                          {@link debug_backtrace()} that should be used
     *                          to find error context
     * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also
     * thrown.  If a PEAR_Error is returned, the userinfo
     * property is set to the following array:
     * 
     * <code>
     * array(
     *    'code' => $code,
     *    'params' => $params,
     *    'package' => $this->_package,
     *    'level' => $level,
     *    'time' => time(),
     *    'context' => $context,
     *    'message' => $msg,
     * //['repackage' => $err] repackaged error array/Exception class
     * );
     * </code>
     * 
     * Normally, the previous array is returned.
     */
    function push($code, $level = 'error', $params = array(), $msg = false,
                  $repackage = false, $backtrace = false)
    {
        $context = false;
        // grab error context
        if ($this->_contextCallback) {
            if (!$backtrace) {
                $backtrace = debug_backtrace();
            }
            $context = call_user_func($this->_contextCallback, $code, $params, $backtrace);
        }
        
        // save error
        $time = explode(' ', microtime());
        $time = $time[1] + $time[0];
        $err = array(
                'code' => $code,
                'params' => $params,
                'package' => $this->_package,
                'level' => $level,
                'time' => $time,
                'context' => $context,
                'message' => $msg,
               );

        if ($repackage) {
            $err['repackage'] = $repackage;
        }

        // set up the error message, if necessary
        if ($this->_msgCallback) {
            $msg = call_user_func_array($this->_msgCallback,
                                        array(&$this, $err));
            $err['message'] = $msg;
        }        
        $push = $log = true;
        $die = false;
        // try the overriding callback first
        $callback = $this->staticPopCallback();
        if ($callback) {
            $this->staticPushCallback($callback);
        }
        if (!is_callable($callback)) {
            // try the local callback next
            $callback = $this->popCallback();
            if (is_callable($callback)) {
                $this->pushCallback($callback);
            } else {
                // try the default callback
                $callback = isset($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package]) ?
                    $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package] :
                    $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']['*'];
            }
        }
        if (is_callable($callback)) {
            switch(call_user_func($callback, $err)){
            	case PEAR_ERRORSTACK_IGNORE: 
            		return $err;
        		break;
            	case PEAR_ERRORSTACK_PUSH: 
            		$log = false;
        		break;
            	case PEAR_ERRORSTACK_LOG: 
            		$push = false;
        		break;
            	case PEAR_ERRORSTACK_DIE: 
            		$die = true;
        		break;
                // anything else returned has the same effect as pushandlog
            }
        }
        if ($push) {
            array_unshift($this->_errors, $err);
            if (!isset($this->_errorsByLevel[$err['level']])) {
                $this->_errorsByLevel[$err['level']] = array();
            }
            $this->_errorsByLevel[$err['level']][] = &$this->_errors[0];
        }
        if ($log) {
            if ($this->_logger || $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']) {
                $this->_log($err);
            }
        }
        if ($die) {
            die();
        }
        if ($this->_compat && $push) {
            return $this->raiseError($msg, $code, null, null, $err);
        }
        return $err;
    }
    
    /**
     * Static version of {@link push()}
     * 
     * @param string $package   Package name this error belongs to
     * @param int    $code      Package-specific error code
     * @param string $level     Error level.  This is NOT spell-checked
     * @param array  $params    associative array of error parameters
     * @param string $msg       Error message, or a portion of it if the message
     *                          is to be generated
     * @param array  $repackage If this error re-packages an error pushed by
     *                          another package, place the array returned from
     *                          {@link pop()} in this parameter
     * @param array  $backtrace Protected parameter: use this to pass in the
     *                          {@link debug_backtrace()} that should be used
     *                          to find error context
     * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also
     *                          thrown.  see docs for {@link push()}
     * @static
     */
    function staticPush($package, $code, $level = 'error', $params = array(),
                        $msg = false, $repackage = false, $backtrace = false)
    {
        $s = &PEAR_ErrorStack::singleton($package);
        if ($s->_contextCallback) {
            if (!$backtrace) {
                if (function_exists('debug_backtrace')) {
                    $backtrace = debug_backtrace();
                }
            }
        }
        return $s->push($code, $level, $params, $msg, $repackage, $backtrace);
    }
    
    /**
     * Log an error using PEAR::Log
     * @param array $err Error array
     * @param array $levels Error level => Log constant map
     * @access protected
     */
    function _log($err)
    {
        if ($this->_logger) {
            $logger = &$this->_logger;
        } else {
            $logger = &$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'];
        }
        if (is_a($logger, 'Log')) {
            $levels = array(
                'exception' => PEAR_LOG_CRIT,
                'alert' => PEAR_LOG_ALERT,
                'critical' => PEAR_LOG_CRIT,
                'error' => PEAR_LOG_ERR,
                'warning' => PEAR_LOG_WARNING,
                'notice' => PEAR_LOG_NOTICE,
                'info' => PEAR_LOG_INFO,
                'debug' => PEAR_LOG_DEBUG);
            if (isset($levels[$err['level']])) {
                $level = $levels[$err['level']];
            } else {
                $level = PEAR_LOG_INFO;
            }
            $logger->log($err['message'], $level, $err);
        } else { // support non-standard logs
            call_user_func($logger, $err);
        }
    }

    
    /**
     * Pop an error off of the error stack
     * 
     * @return false|array
     * @since 0.4alpha it is no longer possible to specify a specific error
     * level to return - the last error pushed will be returned, instead
     */
    function pop()
    {
        $err = @array_shift($this->_errors);
        if (!is_null($err)) {
            @array_pop($this->_errorsByLevel[$err['level']]);
            if (!count($this->_errorsByLevel[$err['level']])) {
                unset($this->_errorsByLevel[$err['level']]);
            }
        }
        return $err;
    }

    /**
     * Pop an error off of the error stack, static method
     *
     * @param string package name
     * @return boolean
     * @since PEAR1.5.0a1
     */
    function staticPop($package)
    {
        if ($package) {
            if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
                return false;
            }
            return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->pop();
        }
    }

    /**
     * Determine whether there are any errors on the stack
     * @param string|array Level name.  Use to determine if any errors
     * of level (string), or levels (array) have been pushed
     * @return boolean
     */
    function hasErrors($level = false)
    {
        if ($level) {
            return isset($this->_errorsByLevel[$level]);
        }
        return count($this->_errors);
    }
    
    /**
     * Retrieve all errors since last purge
     * 
     * @param boolean set in order to empty the error stack
     * @param string level name, to return only errors of a particular severity
     * @return array
     */
    function getErrors($purge = false, $level = false)
    {
        if (!$purge) {
            if ($level) {
                if (!isset($this->_errorsByLevel[$level])) {
                    return array();
                } else {
                    return $this->_errorsByLevel[$level];
                }
            } else {
                return $this->_errors;
            }
        }
        if ($level) {
            $ret = $this->_errorsByLevel[$level];
            foreach ($this->_errorsByLevel[$level] as $i => $unused) {
                // entries are references to the $_errors array
                $this->_errorsByLevel[$level][$i] = false;
            }
            // array_filter removes all entries === false
            $this->_errors = array_filter($this->_errors);
            unset($this->_errorsByLevel[$level]);
            return $ret;
        }
        $ret = $this->_errors;
        $this->_errors = array();
        $this->_errorsByLevel = array();
        return $ret;
    }
    
    /**
     * Determine whether there are any errors on a single error stack, or on any error stack
     *
     * The optional parameter can be used to test the existence of any errors without the need of
     * singleton instantiation
     * @param string|false Package name to check for errors
     * @param string Level name to check for a particular severity
     * @return boolean
     * @static
     */
    function staticHasErrors($package = false, $level = false)
    {
        if ($package) {
            if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
                return false;
            }
            return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->hasErrors($level);
        }
        foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
            if ($obj->hasErrors($level)) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * Get a list of all errors since last purge, organized by package
     * @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be
     * @param boolean $purge Set to purge the error stack of existing errors
     * @param string  $level Set to a level name in order to retrieve only errors of a particular level
     * @param boolean $merge Set to return a flat array, not organized by package
     * @param array   $sortfunc Function used to sort a merged array - default
     *        sorts by time, and should be good for most cases
     * @static
     * @return array 
     */
    function staticGetErrors($purge = false, $level = false, $merge = false,
                             $sortfunc = array('PEAR_ErrorStack', '_sortErrors'))
    {
        $ret = array();
        if (!is_callable($sortfunc)) {
            $sortfunc = array('PEAR_ErrorStack', '_sortErrors');
        }
        foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
            $test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge, $level);
            if ($test) {
                if ($merge) {
                    $ret = array_merge($ret, $test);
                } else {
                    $ret[$package] = $test;
                }
            }
        }
        if ($merge) {
            usort($ret, $sortfunc);
        }
        return $ret;
    }
    
    /**
     * Error sorting function, sorts by time
     * @access private
     */
    function _sortErrors($a, $b)
    {
        if ($a['time'] == $b['time']) {
            return 0;
        }
        if ($a['time'] < $b['time']) {
            return 1;
        }
        return -1;
    }

    /**
     * Standard file/line number/function/class context callback
     *
     * This function uses a backtrace generated from {@link debug_backtrace()}
     * and so will not work at all in PHP < 4.3.0.  The frame should
     * reference the frame that contains the source of the error.
     * @return array|false either array('file' => file, 'line' => line,
     *         'function' => function name, 'class' => class name) or
     *         if this doesn't work, then false
     * @param unused
     * @param integer backtrace frame.
     * @param array Results of debug_backtrace()
     * @static
     */
    function getFileLine($code, $params, $backtrace = null)
    {
        if ($backtrace === null) {
            return false;
        }
        $frame = 0;
        $functionframe = 1;
        if (!isset($backtrace[1])) {
            $functionframe = 0;
        } else {
            while (isset($backtrace[$functionframe]['function']) &&
                  $backtrace[$functionframe]['function'] == 'eval' &&
                  isset($backtrace[$functionframe + 1])) {
                $functionframe++;
            }
        }
        if (isset($backtrace[$frame])) {
            if (!isset($backtrace[$frame]['file'])) {
                $frame++;
            }
            $funcbacktrace = $backtrace[$functionframe];
            $filebacktrace = $backtrace[$frame];
            $ret = array('file' => $filebacktrace['file'],
                         'line' => $filebacktrace['line']);
            // rearrange for eval'd code or create function errors
            if (strpos($filebacktrace['file'], '(') && 
            	  preg_match(';^(.*?)\((\d+)\) : (.*?)\\z;', $filebacktrace['file'],
                  $matches)) {
                $ret['file'] = $matches[1];
                $ret['line'] = $matches[2] + 0;
            }
            if (isset($funcbacktrace['function']) && isset($backtrace[1])) {
                if ($funcbacktrace['function'] != 'eval') {
                    if ($funcbacktrace['function'] == '__lambda_func') {
                        $ret['function'] = 'create_function() code';
                    } else {
                        $ret['function'] = $funcbacktrace['function'];
                    }
                }
            }
            if (isset($funcbacktrace['class']) && isset($backtrace[1])) {
                $ret['class'] = $funcbacktrace['class'];
            }
            return $ret;
        }
        return false;
    }
    
    /**
     * Standard error message generation callback
     * 
     * This method may also be called by a custom error message generator
     * to fill in template values from the params array, simply
     * set the third parameter to the error message template string to use
     * 
     * The special variable %__msg% is reserved: use it only to specify
     * where a message passed in by the user should be placed in the template,
     * like so:
     * 
     * Error message: %msg% - internal error
     * 
     * If the message passed like so:
     * 
     * <code>
     * $stack->push(ERROR_CODE, 'error', array(), 'server error 500');
     * </code>
     * 
     * The returned error message will be "Error message: server error 500 -
     * internal error"
     * @param PEAR_ErrorStack
     * @param array
     * @param string|false Pre-generated error message template
     * @static
     * @return string
     */
    function getErrorMessage(&$stack, $err, $template = false)
    {
        if ($template) {
            $mainmsg = $template;
        } else {
            $mainmsg = $stack->getErrorMessageTemplate($err['code']);
        }
        $mainmsg = str_replace('%__msg%', $err['message'], $mainmsg);
        if (is_array($err['params']) && count($err['params'])) {
            foreach ($err['params'] as $name => $val) {
                if (is_array($val)) {
                    // @ is needed in case $val is a multi-dimensional array
                    $val = @implode(', ', $val);
                }
                if (is_object($val)) {
                    if (method_exists($val, '__toString')) {
                        $val = $val->__toString();
                    } else {
                        PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING,
                            'warning', array('obj' => get_class($val)),
                            'object %obj% passed into getErrorMessage, but has no __toString() method');
                        $val = 'Object';
                    }
                }
                $mainmsg = str_replace('%' . $name . '%', $val, $mainmsg);
            }
        }
        return $mainmsg;
    }
    
    /**
     * Standard Error Message Template generator from code
     * @return string
     */
    function getErrorMessageTemplate($code)
    {
        if (!isset($this->_errorMsgs[$code])) {
            return '%__msg%';
        }
        return $this->_errorMsgs[$code];
    }
    
    /**
     * Set the Error Message Template array
     * 
     * The array format must be:
     * <pre>
     * array(error code => 'message template',...)
     * </pre>
     * 
     * Error message parameters passed into {@link push()} will be used as input
     * for the error message.  If the template is 'message %foo% was %bar%', and the
     * parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will
     * be 'message one was six'
     * @return string
     */
    function setErrorMessageTemplate($template)
    {
        $this->_errorMsgs = $template;
    }
    
    
    /**
     * emulate PEAR::raiseError()
     * 
     * @return PEAR_Error
     */
    function raiseError()
    {
        require_once 'PEAR.php';
        $args = func_get_args();
        return call_user_func_array(array('PEAR', 'raiseError'), $args);
    }
}
$stack = &PEAR_ErrorStack::singleton('PEAR_ErrorStack');
$stack->pushCallback(array('PEAR_ErrorStack', '_handleError'));
?>
PEAR-1.7.2/PEAR/Exception.php100644   1750   1750       33567 11014312742  10721 <?php
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
/**
 * PEAR_Exception
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Tomas V. V. Cox <cox@idecnet.com>
 * @author     Hans Lellelid <hans@velum.net>
 * @author     Bertrand Mansion <bmansion@mamasam.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Exception.php,v 1.29 2008/01/03 20:26:35 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.3.3
 */


/**
 * Base PEAR_Exception Class
 *
 * 1) Features:
 *
 * - Nestable exceptions (throw new PEAR_Exception($msg, $prev_exception))
 * - Definable triggers, shot when exceptions occur
 * - Pretty and informative error messages
 * - Added more context info available (like class, method or cause)
 * - cause can be a PEAR_Exception or an array of mixed
 *   PEAR_Exceptions/PEAR_ErrorStack warnings
 * - callbacks for specific exception classes and their children
 *
 * 2) Ideas:
 *
 * - Maybe a way to define a 'template' for the output
 *
 * 3) Inherited properties from PHP Exception Class:
 *
 * protected $message
 * protected $code
 * protected $line
 * protected $file
 * private   $trace
 *
 * 4) Inherited methods from PHP Exception Class:
 *
 * __clone
 * __construct
 * getMessage
 * getCode
 * getFile
 * getLine
 * getTraceSafe
 * getTraceSafeAsString
 * __toString
 *
 * 5) Usage example
 *
 * <code>
 *  require_once 'PEAR/Exception.php';
 *
 *  class Test {
 *     function foo() {
 *         throw new PEAR_Exception('Error Message', ERROR_CODE);
 *     }
 *  }
 *
 *  function myLogger($pear_exception) {
 *     echo $pear_exception->getMessage();
 *  }
 *  // each time a exception is thrown the 'myLogger' will be called
 *  // (its use is completely optional)
 *  PEAR_Exception::addObserver('myLogger');
 *  $test = new Test;
 *  try {
 *     $test->foo();
 *  } catch (PEAR_Exception $e) {
 *     print $e;
 *  }
 * </code>
 *
 * @category   pear
 * @package    PEAR
 * @author     Tomas V.V.Cox <cox@idecnet.com>
 * @author     Hans Lellelid <hans@velum.net>
 * @author     Bertrand Mansion <bmansion@mamasam.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.3.3
 *
 */
class PEAR_Exception extends Exception
{
    const OBSERVER_PRINT = -2;
    const OBSERVER_TRIGGER = -4;
    const OBSERVER_DIE = -8;
    protected $cause;
    private static $_observers = array();
    private static $_uniqueid = 0;
    private $_trace;

    /**
     * Supported signatures:
     *  - PEAR_Exception(string $message);
     *  - PEAR_Exception(string $message, int $code);
     *  - PEAR_Exception(string $message, Exception $cause);
     *  - PEAR_Exception(string $message, Exception $cause, int $code);
     *  - PEAR_Exception(string $message, PEAR_Error $cause);
     *  - PEAR_Exception(string $message, PEAR_Error $cause, int $code);
     *  - PEAR_Exception(string $message, array $causes);
     *  - PEAR_Exception(string $message, array $causes, int $code);
     * @param string exception message
     * @param int|Exception|PEAR_Error|array|null exception cause
     * @param int|null exception code or null
     */
    public function __construct($message, $p2 = null, $p3 = null)
    {
        if (is_int($p2)) {
            $code = $p2;
            $this->cause = null;
        } elseif (is_object($p2) || is_array($p2)) {
            // using is_object allows both Exception and PEAR_Error
            if (is_object($p2) && !($p2 instanceof Exception)) {
                if (!class_exists('PEAR_Error') || !($p2 instanceof PEAR_Error)) {
                    throw new PEAR_Exception('exception cause must be Exception, ' .
                        'array, or PEAR_Error');
                }
            }
            $code = $p3;
            if (is_array($p2) && isset($p2['message'])) {
                // fix potential problem of passing in a single warning
                $p2 = array($p2);
            }
            $this->cause = $p2;
        } else {
            $code = null;
            $this->cause = null;
        }
        parent::__construct($message, $code);
        $this->signal();
    }

    /**
     * @param mixed $callback  - A valid php callback, see php func is_callable()
     *                         - A PEAR_Exception::OBSERVER_* constant
     *                         - An array(const PEAR_Exception::OBSERVER_*,
     *                           mixed $options)
     * @param string $label    The name of the observer. Use this if you want
     *                         to remove it later with removeObserver()
     */
    public static function addObserver($callback, $label = 'default')
    {
        self::$_observers[$label] = $callback;
    }

    public static function removeObserver($label = 'default')
    {
        unset(self::$_observers[$label]);
    }

    /**
     * @return int unique identifier for an observer
     */
    public static function getUniqueId()
    {
        return self::$_uniqueid++;
    }

    private function signal()
    {
        foreach (self::$_observers as $func) {
            if (is_callable($func)) {
                call_user_func($func, $this);
                continue;
            }
            settype($func, 'array');
            switch ($func[0]) {
                case self::OBSERVER_PRINT :
                    $f = (isset($func[1])) ? $func[1] : '%s';
                    printf($f, $this->getMessage());
                    break;
                case self::OBSERVER_TRIGGER :
                    $f = (isset($func[1])) ? $func[1] : E_USER_NOTICE;
                    trigger_error($this->getMessage(), $f);
                    break;
                case self::OBSERVER_DIE :
                    $f = (isset($func[1])) ? $func[1] : '%s';
                    die(printf($f, $this->getMessage()));
                    break;
                default:
                    trigger_error('invalid observer type', E_USER_WARNING);
            }
        }
    }

    /**
     * Return specific error information that can be used for more detailed
     * error messages or translation.
     *
     * This method may be overridden in child exception classes in order
     * to add functionality not present in PEAR_Exception and is a placeholder
     * to define API
     *
     * The returned array must be an associative array of parameter => value like so:
     * <pre>
     * array('name' => $name, 'context' => array(...))
     * </pre>
     * @return array
     */
    public function getErrorData()
    {
        return array();
    }

    /**
     * Returns the exception that caused this exception to be thrown
     * @access public
     * @return Exception|array The context of the exception
     */
    public function getCause()
    {
        return $this->cause;
    }

    /**
     * Function must be public to call on caused exceptions
     * @param array
     */
    public function getCauseMessage(&$causes)
    {
        $trace = $this->getTraceSafe();
        $cause = array('class'   => get_class($this),
                       'message' => $this->message,
                       'file' => 'unknown',
                       'line' => 'unknown');
        if (isset($trace[0])) {
            if (isset($trace[0]['file'])) {
                $cause['file'] = $trace[0]['file'];
                $cause['line'] = $trace[0]['line'];
            }
        }
        $causes[] = $cause;
        if ($this->cause instanceof PEAR_Exception) {
            $this->cause->getCauseMessage($causes);
        } elseif ($this->cause instanceof Exception) {
            $causes[] = array('class'   => get_class($this->cause),
                              'message' => $this->cause->getMessage(),
                              'file' => $this->cause->getFile(),
                              'line' => $this->cause->getLine());
        } elseif (class_exists('PEAR_Error') && $this->cause instanceof PEAR_Error) {
            $causes[] = array('class' => get_class($this->cause),
                              'message' => $this->cause->getMessage(),
                              'file' => 'unknown',
                              'line' => 'unknown');
        } elseif (is_array($this->cause)) {
            foreach ($this->cause as $cause) {
                if ($cause instanceof PEAR_Exception) {
                    $cause->getCauseMessage($causes);
                } elseif ($cause instanceof Exception) {
                    $causes[] = array('class'   => get_class($cause),
                                   'message' => $cause->getMessage(),
                                   'file' => $cause->getFile(),
                                   'line' => $cause->getLine());
                } elseif (class_exists('PEAR_Error') && $cause instanceof PEAR_Error) {
                    $causes[] = array('class' => get_class($cause),
                                      'message' => $cause->getMessage(),
                                      'file' => 'unknown',
                                      'line' => 'unknown');
                } elseif (is_array($cause) && isset($cause['message'])) {
                    // PEAR_ErrorStack warning
                    $causes[] = array(
                        'class' => $cause['package'],
                        'message' => $cause['message'],
                        'file' => isset($cause['context']['file']) ?
                                            $cause['context']['file'] :
                                            'unknown',
                        'line' => isset($cause['context']['line']) ?
                                            $cause['context']['line'] :
                                            'unknown',
                    );
                }
            }
        }
    }

    public function getTraceSafe()
    {   
        if (!isset($this->_trace)) {
            $this->_trace = $this->getTrace();
            if (empty($this->_trace)) {
                $backtrace = debug_backtrace();
                $this->_trace = array($backtrace[count($backtrace)-1]);
            }
        }
        return $this->_trace;
    }

    public function getErrorClass()
    {
        $trace = $this->getTraceSafe();
        return $trace[0]['class'];
    }

    public function getErrorMethod()
    {
        $trace = $this->getTraceSafe();
        return $trace[0]['function'];
    }

    public function __toString()
    {
        if (isset($_SERVER['REQUEST_URI'])) {
            return $this->toHtml();
        }
        return $this->toText();
    }

    public function toHtml()
    {
        $trace = $this->getTraceSafe();
        $causes = array();
        $this->getCauseMessage($causes);
        $html =  '<table border="1" cellspacing="0">' . "\n";
        foreach ($causes as $i => $cause) {
            $html .= '<tr><td colspan="3" bgcolor="#ff9999">'
               . str_repeat('-', $i) . ' <b>' . $cause['class'] . '</b>: '
               . htmlspecialchars($cause['message']) . ' in <b>' . $cause['file'] . '</b> '
               . 'on line <b>' . $cause['line'] . '</b>'
               . "</td></tr>\n";
        }
        $html .= '<tr><td colspan="3" bgcolor="#aaaaaa" align="center"><b>Exception trace</b></td></tr>' . "\n"
               . '<tr><td align="center" bgcolor="#cccccc" width="20"><b>#</b></td>'
               . '<td align="center" bgcolor="#cccccc"><b>Function</b></td>'
               . '<td align="center" bgcolor="#cccccc"><b>Location</b></td></tr>' . "\n";

        foreach ($trace as $k => $v) {
            $html .= '<tr><td align="center">' . $k . '</td>'
                   . '<td>';
            if (!empty($v['class'])) {
                $html .= $v['class'] . $v['type'];
            }
            $html .= $v['function'];
            $args = array();
            if (!empty($v['args'])) {
                foreach ($v['args'] as $arg) {
                    if (is_null($arg)) $args[] = 'null';
                    elseif (is_array($arg)) $args[] = 'Array';
                    elseif (is_object($arg)) $args[] = 'Object('.get_class($arg).')';
                    elseif (is_bool($arg)) $args[] = $arg ? 'true' : 'false';
                    elseif (is_int($arg) || is_double($arg)) $args[] = $arg;
                    else {
                        $arg = (string)$arg;
                        $str = htmlspecialchars(substr($arg, 0, 16));
                        if (strlen($arg) > 16) $str .= '&hellip;';
                        $args[] = "'" . $str . "'";
                    }
                }
            }
            $html .= '(' . implode(', ',$args) . ')'
                   . '</td>'
                   . '<td>' . (isset($v['file']) ? $v['file'] : 'unknown')
                   . ':' . (isset($v['line']) ? $v['line'] : 'unknown')
                   . '</td></tr>' . "\n";
        }
        $html .= '<tr><td align="center">' . ($k+1) . '</td>'
               . '<td>{main}</td>'
               . '<td>&nbsp;</td></tr>' . "\n"
               . '</table>';
        return $html;
    }

    public function toText()
    {
        $causes = array();
        $this->getCauseMessage($causes);
        $causeMsg = '';
        foreach ($causes as $i => $cause) {
            $causeMsg .= str_repeat(' ', $i) . $cause['class'] . ': '
                   . $cause['message'] . ' in ' . $cause['file']
                   . ' on line ' . $cause['line'] . "\n";
        }
        return $causeMsg . $this->getTraceAsString();
    }
}

?>PEAR-1.7.2/PEAR/FixPHP5PEARWarnings.php100644   1750   1750         231 11014312742  12245 <?php
if ($skipmsg) {
    $a = &new $ec($code, $mode, $options, $userinfo);
} else {
    $a = &new $ec($message, $code, $mode, $options, $userinfo);
}
?>PEAR-1.7.2/PEAR/Frontend.php100644   1750   1750       15617 11014312742  10536 <?php
/**
 * PEAR_Frontend, the singleton-based frontend for user input/output
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Frontend.php,v 1.13 2008/01/03 20:26:35 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * Which user interface class is being used.
 * @var string class name
 */
$GLOBALS['_PEAR_FRONTEND_CLASS'] = 'PEAR_Frontend_CLI';

/**
 * Instance of $_PEAR_Command_uiclass.
 * @var object
 */
$GLOBALS['_PEAR_FRONTEND_SINGLETON'] = null;

/**
 * Singleton-based frontend for PEAR user input/output
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Frontend extends PEAR
{
    /**
     * Retrieve the frontend object
     * @return PEAR_Frontend_CLI|PEAR_Frontend_Web|PEAR_Frontend_Gtk
     * @static
     */
    function &singleton($type = null)
    {
        if ($type === null) {
            if (!isset($GLOBALS['_PEAR_FRONTEND_SINGLETON'])) {
                $a = false;
                return $a;
            }
            return $GLOBALS['_PEAR_FRONTEND_SINGLETON'];
        } else {
            $a = PEAR_Frontend::setFrontendClass($type);
            return $a;
        }
    }

    /**
     * Set the frontend class that will be used by calls to {@link singleton()}
     *
     * Frontends are expected to conform to the PEAR naming standard of
     * _ => DIRECTORY_SEPARATOR (PEAR_Frontend_CLI is in PEAR/Frontend/CLI.php)
     * @param string $uiclass full class name
     * @return PEAR_Frontend
     * @static
     */
    function &setFrontendClass($uiclass)
    {
        if (is_object($GLOBALS['_PEAR_FRONTEND_SINGLETON']) &&
              is_a($GLOBALS['_PEAR_FRONTEND_SINGLETON'], $uiclass)) {
            return $GLOBALS['_PEAR_FRONTEND_SINGLETON'];
        }
        if (!class_exists($uiclass)) {
            $file = str_replace('_', '/', $uiclass) . '.php';
            if (PEAR_Frontend::isIncludeable($file)) {
                include_once $file;
            }
        }
        if (class_exists($uiclass)) {
            $obj = &new $uiclass;
            // quick test to see if this class implements a few of the most
            // important frontend methods
            if (is_a($obj, 'PEAR_Frontend')) {
                $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = &$obj;
                $GLOBALS['_PEAR_FRONTEND_CLASS'] = $uiclass;
                return $obj;
            } else {
                $err = PEAR::raiseError("not a frontend class: $uiclass");
                return $err;
            }
        }
        $err = PEAR::raiseError("no such class: $uiclass");
        return $err;
    }

    /**
     * Set the frontend class that will be used by calls to {@link singleton()}
     *
     * Frontends are expected to be a descendant of PEAR_Frontend
     * @param PEAR_Frontend
     * @return PEAR_Frontend
     * @static
     */
    function &setFrontendObject($uiobject)
    {
        if (is_object($GLOBALS['_PEAR_FRONTEND_SINGLETON']) &&
              is_a($GLOBALS['_PEAR_FRONTEND_SINGLETON'], get_class($uiobject))) {
            return $GLOBALS['_PEAR_FRONTEND_SINGLETON'];
        }
        if (!is_a($uiobject, 'PEAR_Frontend')) {
            $err = PEAR::raiseError('not a valid frontend class: (' .
                get_class($uiobject) . ')');
            return $err;
        }
        $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = &$uiobject;
        $GLOBALS['_PEAR_FRONTEND_CLASS'] = get_class($uiobject);
        return $uiobject;
    }

    /**
     * @param string $path relative or absolute include path
     * @return boolean
     * @static
     */
    function isIncludeable($path)
    {
        if (file_exists($path) && is_readable($path)) {
            return true;
        }
        $fp = @fopen($path, 'r', true);
        if ($fp) {
            fclose($fp);
            return true;
        }
        return false;
    }

    /**
     * @param PEAR_Config
     */
    function setConfig(&$config)
    {
    }

    /**
     * This can be overridden to allow session-based temporary file management
     *
     * By default, all files are deleted at the end of a session.  The web installer
     * needs to be able to sustain a list over many sessions in order to support
     * user interaction with install scripts
     */
    function addTempFile($file)
    {
        $GLOBALS['_PEAR_Common_tempfiles'][] = $file;
    }

    /**
     * Log an action
     *
     * @param string $msg the message to log
     * @param boolean $append_crlf
     * @return boolean true
     * @abstract
     */
    function log($msg, $append_crlf = true)
    {
    }

    /**
     * Run a post-installation script
     *
     * @param array $scripts array of post-install scripts
     * @abstract
     */
    function runPostinstallScripts(&$scripts)
    {
    }

    /**
     * Display human-friendly output formatted depending on the
     * $command parameter.
     *
     * This should be able to handle basic output data with no command
     * @param mixed  $data    data structure containing the information to display
     * @param string $command command from which this method was called
     * @abstract
     */
    function outputData($data, $command = '_default')
    {
    }

    /**
     * Display a modal form dialog and return the given input
     *
     * A frontend that requires multiple requests to retrieve and process
     * data must take these needs into account, and implement the request
     * handling code.
     * @param string $command  command from which this method was called
     * @param array  $prompts  associative array. keys are the input field names
     *                         and values are the description
     * @param array  $types    array of input field types (text, password,
     *                         etc.) keys have to be the same like in $prompts
     * @param array  $defaults array of default values. again keys have
     *                         to be the same like in $prompts.  Do not depend
     *                         on a default value being set.
     * @return array input sent by the user
     * @abstract
     */
    function userDialog($command, $prompts, $types = array(), $defaults = array())
    {
    }
}
?>PEAR-1.7.2/PEAR/Installer.php100644   1750   1750      210161 11014312742  10723 <?php
/**
 * PEAR_Installer
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V.V. Cox <cox@idecnet.com>
 * @author     Martin Jansen <mj@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Installer.php,v 1.253 2008/05/13 22:46:07 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * Used for installation groups in package.xml 2.0 and platform exceptions
 */
require_once 'OS/Guess.php';
require_once 'PEAR/Downloader.php';

define('PEAR_INSTALLER_NOBINARY', -240);
/**
 * Administration class used to install PEAR packages and maintain the
 * installed package database.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V.V. Cox <cox@idecnet.com>
 * @author     Martin Jansen <mj@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */
class PEAR_Installer extends PEAR_Downloader
{
    // {{{ 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 PEAR_Registry
     */
    var $registry;

    /**
     * array of PEAR_Downloader_Packages
     * @var array
     */
    var $_downloadedPackages;

    /** 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');
    }

    function setOptions($options)
    {
        $this->_options = $options;
    }

    function setConfig(&$config)
    {
        $this->config = &$config;
        $this->_registry = &$config->getRegistry();
    }

    // }}}

    function _removeBackups($files)
    {
        foreach ($files as $path) {
            $this->addFileOperation('removebackup', array($path));
        }
    }

    // {{{ _deletePackageFiles()

    /**
     * Delete a package's installed files, does not remove empty directories.
     *
     * @param string package name
     * @param string channel name
     * @param bool if true, then files are backed up first
     * @return bool TRUE on success, or a PEAR error on failure
     * @access protected
     */
    function _deletePackageFiles($package, $channel = false, $backup = false)
    {
        if (!$channel) {
            $channel = 'pear.php.net';
        }
        if (!strlen($package)) {
            return $this->raiseError("No package to uninstall given");
        }
        if (strtolower($package) == 'pear' && $channel == 'pear.php.net') {
            // to avoid race conditions, include all possible needed files
            require_once 'PEAR/Task/Common.php';
            require_once 'PEAR/Task/Replace.php';
            require_once 'PEAR/Task/Unixeol.php';
            require_once 'PEAR/Task/Windowseol.php';
            require_once 'PEAR/PackageFile/v1.php';
            require_once 'PEAR/PackageFile/v2.php';
            require_once 'PEAR/PackageFile/Generator/v1.php';
            require_once 'PEAR/PackageFile/Generator/v2.php';
        }
        $filelist = $this->_registry->packageInfo($package, 'filelist', $channel);
        if ($filelist == null) {
            return $this->raiseError("$channel/$package not installed");
        }
        $ret = array();
        foreach ($filelist as $file => $props) {
            if (empty($props['installed_as'])) {
                continue;
            }
            $path = $props['installed_as'];
            if ($backup) {
                $this->addFileOperation('backup', array($path));
                $ret[] = $path;
            }
            $this->addFileOperation('delete', array($path));
        }
        if ($backup) {
            return $ret;
        }
        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($this->_registry)) {
            $this->_registry = &$this->config->getRegistry();
        }
        if (isset($atts['platform'])) {
            if (empty($os)) {
                $os = new OS_Guess();
            }
            if (strlen($atts['platform']) && $atts['platform']{0} == '!') {
                $negate = true;
                $platform = substr($atts['platform'], 1);
            } else {
                $negate = false;
                $platform = $atts['platform'];
            }
            if ((bool) $os->matchSignature($platform) === $negate) {
                $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")");
                return PEAR_INSTALLER_SKIPPED;
            }
        }
        // }}}

        $channel = $this->pkginfo->getChannel();
        // {{{ assemble the destination paths
        switch ($atts['role']) {
            case 'src':
            case 'extsrc':
                $this->source_files++;
                return;
            case 'doc':
            case 'data':
            case 'test':
                $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel) .
                            DIRECTORY_SEPARATOR . $this->pkginfo->getPackage();
                unset($atts['baseinstalldir']);
                break;
            case 'ext':
            case 'php':
                $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel);
                break;
            case 'script':
                $dest_dir = $this->config->get('bin_dir', null, $channel);
                break;
            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+!"),
                                                    array(DIRECTORY_SEPARATOR,
                                                          DIRECTORY_SEPARATOR,
                                                          DIRECTORY_SEPARATOR),
                                                    array($dest_file, $orig_file));
        $final_dest_file = $installed_as = $dest_file;
        if (isset($this->_options['packagingroot'])) {
            $installedas_dest_dir = dirname($final_dest_file);
            $installedas_dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
            $final_dest_file = $this->_prependPath($final_dest_file,
                $this->_options['packagingroot']);
        } else {
            $installedas_dest_dir = dirname($final_dest_file);
            $installedas_dest_file = $installedas_dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
        }
        $dest_dir = dirname($final_dest_file);
        $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
        if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) {
            return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED);
        }
        // }}}

        if (empty($this->_options['register-only']) &&
              (!file_exists($dest_dir) || !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");
        }
        // pretty much nothing happens if we are only registering the install
        if (empty($this->_options['register-only'])) {
            if (empty($atts['replacements'])) {
                if (!file_exists($orig_file)) {
                    return $this->raiseError("file $orig_file does not exist",
                                             PEAR_INSTALLER_FAILED);
                }
                if (!@copy($orig_file, $dest_file)) {
                    return $this->raiseError("failed to write $dest_file: $php_errormsg",
                                             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);
                }
                $contents = file_get_contents($orig_file);
                if ($contents === false) {
                    $contents = '';
                }
                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_]+\\z/i', $a['to'])) {
                            eval("\$to = $a[to];");
                        } else {
                            if (!isset($options['soft'])) {
                                $this->log(0, "invalid php-const replacement: $a[to]");
                            }
                            continue;
                        }
                    } elseif ($a['type'] == 'pear-config') {
                        if ($a['to'] == 'master_server') {
                            $chan = $this->_registry->getChannel($channel);
                            if (!PEAR::isError($chan)) {
                                $to = $chan->getServer();
                            } else {
                                $to = $this->config->get($a['to'], null, $channel);
                            }
                        } else {
                            $to = $this->config->get($a['to'], null, $channel);
                        }
                        if (is_null($to)) {
                            if (!isset($options['soft'])) {
                                $this->log(0, "invalid pear-config replacement: $a[to]");
                            }
                            continue;
                        }
                    } elseif ($a['type'] == 'package-info') {
                        if ($t = $this->pkginfo->packageInfo($a['to'])) {
                            $to = $t;
                        } else {
                            if (!isset($options['soft'])) {
                                $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) === false) {
                    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
                        if (file_exists($dest_file)) {
                            unlink($dest_file);
                        }
                        if (!isset($options['ignore-errors'])) {
                            return $this->raiseError("bad md5sum for file $final_dest_file",
                                                 PEAR_INSTALLER_FAILED);
                        } else {
                            if (!isset($options['soft'])) {
                                $this->log(0, "warning : bad md5sum for file $final_dest_file");
                            }
                        }
                    } else {
                        if (!isset($options['soft'])) {
                            $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'));
                }
                if ($atts['role'] != 'src') {
                    $this->addFileOperation("chmod", array($mode, $dest_file));
                    if (!@chmod($dest_file, $mode)) {
                        if (!isset($options['soft'])) {
                            $this->log(0, "failed to change mode of $dest_file: $php_errormsg");
                        }
                    }
                }
            }
            // }}}
            if ($atts['role'] == 'src') {
                rename($dest_file, $final_dest_file);
                $this->log(2, "renamed source file $dest_file to $final_dest_file");
            } else {
                $this->addFileOperation("rename", array($dest_file, $final_dest_file,
                    $atts['role'] == 'ext'));
            }
        }
        // Store the full path where the file was installed for easy unistall
        if ($atts['role'] != 'script') {
            $loc = $this->config->get($atts['role'] . '_dir');
        } else {
            $loc = $this->config->get('bin_dir');
        }
        if ($atts['role'] != 'src') {
            $this->addFileOperation("installed_as", array($file, $installed_as,
                                    $loc,
                                    dirname(substr($installedas_dest_file, strlen($loc)))));
        }

        //$this->log(2, "installed: $dest_file");
        return PEAR_INSTALLER_OK;
    }

    // }}}
    // {{{ _installFile2()

    /**
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @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 _installFile2(&$pkg, $file, &$real_atts, $tmp_path, $options)
    {
        $atts = $real_atts;
        if (!isset($this->_registry)) {
            $this->_registry = &$this->config->getRegistry();
        }

        $channel = $pkg->getChannel();
        // {{{ assemble the destination paths
        if (!in_array($atts['attribs']['role'],
              PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) {
            return $this->raiseError('Invalid role `' . $atts['attribs']['role'] .
                    "' for file $file");
        }
        $role = &PEAR_Installer_Role::factory($pkg, $atts['attribs']['role'], $this->config);
        $err = $role->setup($this, $pkg, $atts['attribs'], $file);
        if (PEAR::isError($err)) {
            return $err;
        }
        if (!$role->isInstallable()) {
            return;
        }
        $info = $role->processInstallation($pkg, $atts['attribs'], $file, $tmp_path);
        if (PEAR::isError($info)) {
            return $info;
        } else {
            list($save_destdir, $dest_dir, $dest_file, $orig_file) = $info;
        }
        if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) {
            return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED);
        }
        $final_dest_file = $installed_as = $dest_file;
        if (isset($this->_options['packagingroot'])) {
            $final_dest_file = $this->_prependPath($final_dest_file,
                $this->_options['packagingroot']);
        }
        $dest_dir = dirname($final_dest_file);
        $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
        // }}}

        if (empty($this->_options['register-only'])) {
            if (!file_exists($dest_dir) || !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");
            }
        }
        $attribs = $atts['attribs'];
        unset($atts['attribs']);
        // pretty much nothing happens if we are only registering the install
        if (empty($this->_options['register-only'])) {
            if (!count($atts)) { // no tasks
                if (!file_exists($orig_file)) {
                    return $this->raiseError("file $orig_file does not exist",
                                             PEAR_INSTALLER_FAILED);
                }
                if (!@copy($orig_file, $dest_file)) {
                    return $this->raiseError("failed to write $dest_file: $php_errormsg",
                                             PEAR_INSTALLER_FAILED);
                }
                $this->log(3, "+ cp $orig_file $dest_file");
                if (isset($attribs['md5sum'])) {
                    $md5sum = md5_file($dest_file);
                }
            } else { // file with tasks
                if (!file_exists($orig_file)) {
                    return $this->raiseError("file $orig_file does not exist",
                                             PEAR_INSTALLER_FAILED);
                }
                $contents = file_get_contents($orig_file);
                if ($contents === false) {
                    $contents = '';
                }
                if (isset($attribs['md5sum'])) {
                    $md5sum = md5($contents);
                }
                foreach ($atts as $tag => $raw) {
                    $tag = str_replace(array($pkg->getTasksNs() . ':', '-'), 
                        array('', '_'), $tag);
                    $task = "PEAR_Task_$tag";
                    $task = &new $task($this->config, $this, PEAR_TASK_INSTALL);
                    if (!$task->isScript()) { // scripts are only handled after installation
                        $task->init($raw, $attribs, $pkg->getLastInstalledVersion());
                        $res = $task->startSession($pkg, $contents, $final_dest_file);
                        if ($res === false) {
                            continue; // skip this file
                        }
                        if (PEAR::isError($res)) {
                            return $res;
                        }
                        $contents = $res; // save changes
                    }
                    $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) === false) {
                        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($attribs['md5sum'])) {
                    $this->log(2, "md5sum ok: $final_dest_file");
                } else {
                    if (empty($options['force'])) {
                        // delete the file
                        if (file_exists($dest_file)) {
                            unlink($dest_file);
                        }
                        if (!isset($options['ignore-errors'])) {
                            return $this->raiseError("bad md5sum for file $final_dest_file",
                                                     PEAR_INSTALLER_FAILED);
                        } else {
                            if (!isset($options['soft'])) {
                                $this->log(0, "warning : bad md5sum for file $final_dest_file");
                            }
                        }
                    } else {
                        if (!isset($options['soft'])) {
                            $this->log(0, "warning : bad md5sum for file $final_dest_file");
                        }
                    }
                }
            } else {
                $real_atts['attribs']['md5sum'] = md5_file($dest_file);
            }
            // }}}
            // {{{ set file permissions
            if (!OS_WINDOWS) {
                if ($role->isExecutable()) {
                    $mode = 0777 & ~(int)octdec($this->config->get('umask'));
                    $this->log(3, "+ chmod +x $dest_file");
                } else {
                    $mode = 0666 & ~(int)octdec($this->config->get('umask'));
                }
                if ($attribs['role'] != 'src') {
                    $this->addFileOperation("chmod", array($mode, $dest_file));
                    if (!@chmod($dest_file, $mode)) {
                        if (!isset($options['soft'])) {
                            $this->log(0, "failed to change mode of $dest_file: $php_errormsg");
                        }
                    }
                }
            }
            // }}}
            if ($attribs['role'] == 'src') {
                rename($dest_file, $final_dest_file);
                $this->log(2, "renamed source file $dest_file to $final_dest_file");
            } else {
                $this->addFileOperation("rename", array($dest_file, $final_dest_file, $role->isExtension()));
            }
        }
        // Store the full path where the file was installed for easy uninstall
        if ($attribs['role'] != 'src') {
            $loc = $this->config->get($role->getLocationConfig(), null, $channel);
            $this->addFileOperation("installed_as", array($file, $installed_as,
                                $loc,
                                dirname(substr($installed_as, strlen($loc)))));
        }

        //$this->log(2, "installed: $dest_file");
        return PEAR_INSTALLER_OK;
    }

    // }}}
    // {{{ addFileOperation()

    /**
     * Add a file operation to the current file transaction.
     *
     * @see startFileTransaction()
     * @param string $type This can be one of:
     *    - rename:  rename a file ($data has 3 values)
     *    - backup:  backup an existing file ($data has 1 value)
     *    - removebackup:  clean up backups created during install ($data has 1 value)
     *    - 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).
     * @param 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 third whether this is a PHP extension.
     *
     *    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])) {
                        if (!is_writable(dirname($data[0]))) {
                            $errors[] = "permission denied ($type): $data[0]";
                        } else {
                            // make sure the file to be deleted can be opened for writing
                            $fp = false;
                            if (!is_dir($data[0]) &&
                                  (!is_writable($data[0]) || !($fp = @fopen($data[0], 'a')))) {
                                $errors[] = "permission denied ($type): $data[0]";
                            } elseif ($fp) {
                                fclose($fp);
                            }
                        }
                    }
                    break;
            }

        }
        // }}}
        $m = sizeof($errors);
        if ($m > 0) {
            foreach ($errors as $error) {
                if (!isset($this->_options['soft'])) {
                    $this->log(1, $error);
                }
            }
            if (!isset($this->_options['ignore-errors'])) {
                return false;
            }
        }
        $this->_dirtree = array();
        // {{{ really commit the transaction
        foreach ($this->file_operations as $i => $tr) {
            if (!$tr) {
                // support removal of non-existing backups
                continue;
            }
            list($type, $data) = $tr;
            switch ($type) {
                case 'backup':
                    if (!file_exists($data[0])) {
                        $this->file_operations[$i] = false;
                        break;
                    }
                    if (!@copy($data[0], $data[0] . '.bak')) {
                        $this->log(1, 'Could not copy ' . $data[0] . ' to ' . $data[0] .
                            '.bak ' . $php_errormsg);
                        return false;
                    }
                    $this->log(3, "+ backup $data[0] to $data[0].bak");
                    break;
                case 'removebackup':
                    if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) {
                        unlink($data[0] . '.bak');
                        $this->log(3, "+ rm backup of $data[0] ($data[0].bak)");
                    }
                    break;
                case 'rename':
                    if (file_exists($data[1])) {
                        $test = @unlink($data[1]);
                    } else {
                        $test = null;
                    }
                    if (!$test && file_exists($data[1])) {
                        if ($data[2]) {
                            $extra = ', this extension must be installed manually.  Rename to "' .
                                basename($data[1]) . '"';
                        } else {
                            $extra = '';
                        }
                        if (!isset($this->_options['soft'])) {
                            $this->log(1, 'Could not delete ' . $data[1] . ', cannot rename ' .
                                $data[0] . $extra);
                        }
                        if (!isset($this->_options['ignore-errors'])) {
                            return false;
                        }
                    }
                    // permissions issues with rename - copy() is far superior
                    $perms = @fileperms($data[0]);
                    if (!@copy($data[0], $data[1])) {
                        $this->log(1, 'Could not rename ' . $data[0] . ' to ' . $data[1] .
                            ' ' . $php_errormsg);
                        return false;
                    }
                    // copy over permissions, otherwise they are lost
                    @chmod($data[1], $perms);
                    @unlink($data[0]);
                    $this->log(3, "+ mv $data[0] $data[1]");
                    break;
                case 'chmod':
                    if (!@chmod($data[1], $data[0])) {
                        $this->log(1, 'Could not chmod ' . $data[1] . ' to ' .
                            decoct($data[0]) . ' ' . $php_errormsg);
                        return false;
                    }
                    $octmode = decoct($data[0]);
                    $this->log(3, "+ chmod $octmode $data[1]");
                    break;
                case 'delete':
                    if (file_exists($data[0])) {
                        if (!@unlink($data[0])) {
                            $this->log(1, 'Could not delete ' . $data[0] . ' ' .
                                $php_errormsg);
                            return false;
                        }
                        $this->log(3, "+ rm $data[0]");
                    }
                    break;
                case 'rmdir':
                    if (file_exists($data[0])) {
                        do {
                            $testme = opendir($data[0]);
                            while (false !== ($entry = readdir($testme))) {
                                if ($entry == '.' || $entry == '..') {
                                    continue;
                                }
                                closedir($testme);
                                break 2; // this directory is not empty and can't be
                                         // deleted
                            }
                            closedir($testme);
                            if (!@rmdir($data[0])) {
                                $this->log(1, 'Could not rmdir ' . $data[0] . ' ' .
                                    $php_errormsg);
                                return false;
                            }
                            $this->log(3, "+ rmdir $data[0]");
                        } while (false);
                    }
                    break;
                case 'installed_as':
                    $this->pkginfo->setInstalledAs($data[0], $data[1]);
                    if (!isset($this->_dirtree[dirname($data[1])])) {
                        $this->_dirtree[dirname($data[1])] = true;
                        $this->pkginfo->setDirtree(dirname($data[1]));

                        while(!empty($data[3]) && dirname($data[3]) != $data[3] &&
                                $data[3] != '/' && $data[3] != '\\') {
                            $this->pkginfo->setDirtree($pp =
                                $this->_prependPath($data[3], $data[2]));
                            $this->_dirtree[$pp] = 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 'backup':
                    if (file_exists($data[0] . '.bak')) {
                        if (file_exists($data[0] && is_writable($data[0]))) {
                            unlink($data[0]);
                        }
                        @copy($data[0] . '.bak', $data[0]);
                        $this->log(3, "+ restore $data[0] from $data[0].bak");
                    }
                    break;
                case 'removebackup':
                    if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) {
                        unlink($data[0] . '.bak');
                        $this->log(3, "+ rm backup of $data[0] ($data[0].bak)");
                    }
                    break;
                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':
                    $this->pkginfo->setInstalledAs($data[0], false);
                    break;
            }
        }
        $this->pkginfo->resetDirtree();
        $this->file_operations = array();
    }

    // }}}
    // {{{ mkDirHier($dir)

    function mkDirHier($dir)
    {
        $this->addFileOperation('mkdir', array($dir));
        return parent::mkDirHier($dir);
    }

    // }}}
    // {{{ 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
     * @deprecated in favor of PEAR_Downloader
     */
    function download($packages, $options, &$config, &$installpackages,
                      &$errors, $installed = false, $willinstall = false, $state = false)
    {
        // trickiness: initialize here
        parent::PEAR_Downloader($this->ui, $options, $config);
        $ret = parent::download($packages);
        $errors = $this->getErrorMsgs();
        $installpackages = $this->getDownloadedPackages();
        trigger_error("PEAR Warning: PEAR_Installer::download() is deprecated " .
                      "in favor of PEAR_Downloader class", E_USER_WARNING);
        return $ret;
    }

    // }}}
    // {{{ _parsePackageXml()

    function _parsePackageXml(&$descfile, &$tmpdir)
    {
        if (substr($descfile, -4) == '.xml') {
            $tmpdir = false;
        } else {
            // {{{ Decompress pack in tmp dir -------------------------------------

            // To allow relative package file names
            $descfile = realpath($descfile);

            if (PEAR::isError($tmpdir = System::mktemp('-d'))) {
                return $tmpdir;
            }
            $this->log(3, '+ tmp dir created at ' . $tmpdir);
            // }}}
        }
        // Parse xml file -----------------------------------------------
        $pkg = new PEAR_PackageFile($this->config, $this->debug, $tmpdir);
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        $p = &$pkg->fromAnyFile($descfile, PEAR_VALIDATE_INSTALLING);
        PEAR::staticPopErrorHandling();
        if (PEAR::isError($p)) {
            if (is_array($p->getUserInfo())) {
                foreach ($p->getUserInfo() as $err) {
                    $loglevel = $err['level'] == 'error' ? 0 : 1;
                    if (!isset($this->_options['soft'])) {
                        $this->log($loglevel, ucfirst($err['level']) . ': ' . $err['message']);
                    }
                }
            }
            return $this->raiseError('Installation failed: invalid package file');
        } else {
            $descfile = $p->getPackageFile();
        }
        return $p;
    }

    // }}}
    /**
     * Set the list of PEAR_Downloader_Package objects to allow more sane
     * dependency validation
     * @param array
     */
    function setDownloadedPackages(&$pkgs)
    {
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        $err = $this->analyzeDependencies($pkgs);
        PEAR::popErrorHandling();
        if (PEAR::isError($err)) {
            return $err;
        }
        $this->_downloadedPackages = &$pkgs;
    }

    /**
     * Set the list of PEAR_Downloader_Package objects to allow more sane
     * dependency validation
     * @param array
     */
    function setUninstallPackages(&$pkgs)
    {
        $this->_downloadedPackages = &$pkgs;
    }

    function getInstallPackages()
    {
        return $this->_downloadedPackages;
    }

    // {{{ install()

    /**
     * Installs the files within the package file specified.
     *
     * @param string|PEAR_Downloader_Package $pkgfile path to the package file,
     *        or a pre-initialized packagefile object
     * @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())
    {
        $this->_options = $options;
        $this->_registry = &$this->config->getRegistry();
        if (is_object($pkgfile)) {
            $dlpkg = &$pkgfile;
            $pkg = $pkgfile->getPackageFile();
            $pkgfile = $pkg->getArchiveFile();
            $descfile = $pkg->getPackageFile();
            $tmpdir = dirname($descfile);
        } else {
            $descfile = $pkgfile;
            $tmpdir = '';
            if (PEAR::isError($pkg = &$this->_parsePackageXml($descfile, $tmpdir))) {
                return $pkg;
            }
        }

        if (realpath($descfile) != realpath($pkgfile)) {
            $tar = new Archive_Tar($pkgfile);
            if (!$tar->extract($tmpdir)) {
                return $this->raiseError("unable to unpack $pkgfile");
            }
        }

        $pkgname = $pkg->getName();
        $channel = $pkg->getChannel();
        if (isset($this->_options['packagingroot'])) {
            $regdir = $this->_prependPath(
                $this->config->get('php_dir', null, 'pear.php.net'),
                $this->_options['packagingroot']);
            $packrootphp_dir = $this->_prependPath(
                $this->config->get('php_dir', null, $channel),
                $this->_options['packagingroot']);
        }

        if (isset($options['installroot'])) {
            $this->config->setInstallRoot($options['installroot']);
            $this->_registry = &$this->config->getRegistry();
            $installregistry = &$this->_registry;
            $this->installroot = ''; // all done automagically now
            $php_dir = $this->config->get('php_dir', null, $channel);
        } else {
            $this->config->setInstallRoot(false);
            $this->_registry = &$this->config->getRegistry();
            if (isset($this->_options['packagingroot'])) {
                $installregistry = &new PEAR_Registry($regdir);
                if (!$installregistry->channelExists($channel, true)) {
                    // we need to fake a channel-discover of this channel
                    $chanobj = $this->_registry->getChannel($channel, true);
                    $installregistry->addChannel($chanobj);
                }
                $php_dir = $packrootphp_dir;
            } else {
                $installregistry = &$this->_registry;
                $php_dir = $this->config->get('php_dir', null, $channel);
            }
            $this->installroot = '';
        }

        // {{{ checks to do when not in "force" mode
        if (empty($options['force']) &&
              (file_exists($this->config->get('php_dir')) &&
               is_dir($this->config->get('php_dir')))) {
            $testp = $channel == 'pear.php.net' ? $pkgname : array($channel, $pkgname);
            $instfilelist = $pkg->getInstallationFileList(true);
            if (PEAR::isError($instfilelist)) {
                return $instfilelist;
            }
            // ensure we have the most accurate registry
            $installregistry->flushFileMap();
            $test = $installregistry->checkFileMap($instfilelist, $testp, '1.1');
            if (PEAR::isError($test)) {
                return $test;
            }
            if (sizeof($test)) {
                $pkgs = $this->getInstallPackages();
                $found = false;
                foreach ($pkgs as $param) {
                    if ($pkg->isSubpackageOf($param)) {
                        $found = true;
                        break;
                    }
                }
                if ($found) {
                    // subpackages can conflict with earlier versions of parent packages
                    $parentreg = $installregistry->packageInfo($param->getPackage(), null, $param->getChannel());
                    $tmp = $test;
                    foreach ($tmp as $file => $info) {
                        if (is_array($info)) {
                            if (strtolower($info[1]) == strtolower($param->getPackage()) &&
                                  strtolower($info[0]) == strtolower($param->getChannel())) {
                                unset($test[$file]);
                                unset($parentreg['filelist'][$file]);
                            }
                        } else {
                            if (strtolower($param->getChannel()) != 'pear.php.net') {
                                continue;
                            }
                            if (strtolower($info) == strtolower($param->getPackage())) {
                                unset($test[$file]);
                                unset($parentreg['filelist'][$file]);
                            }
                        }
                    }
                    $pfk = &new PEAR_PackageFile($this->config);
                    $parentpkg = &$pfk->fromArray($parentreg);
                    $installregistry->updatePackage2($parentpkg);
                }
                if ($param->getChannel() == 'pecl.php.net' && isset($options['upgrade'])) {
                    $tmp = $test;
                    foreach ($tmp as $file => $info) {
                        if (is_string($info)) {
                            // pear.php.net packages are always stored as strings
                            if (strtolower($info) == strtolower($param->getPackage())) {
                                // upgrading existing package
                                unset($test[$file]);
                            }
                        }
                    }
                }
                if (sizeof($test)) {
                    $msg = "$channel/$pkgname: conflicting files found:\n";
                    $longest = max(array_map("strlen", array_keys($test)));
                    $fmt = "%${longest}s (%s)\n";
                    foreach ($test as $file => $info) {
                        if (!is_array($info)) {
                            $info = array('pear.php.net', $info);
                        }
                        $info = $info[0] . '/' . $info[1];
                        $msg .= sprintf($fmt, $file, $info);
                    }
                    if (!isset($options['ignore-errors'])) {
                        return $this->raiseError($msg);
                    } else {
                        if (!isset($options['soft'])) {
                            $this->log(0, "WARNING: $msg");
                        }
                    }
                }
            }
        }
        // }}}

        $this->startFileTransaction();

        if (empty($options['upgrade']) && empty($options['soft'])) {
            // checks to do only when installing new packages
            if ($channel == 'pecl.php.net') {
                $test = $installregistry->packageExists($pkgname, $channel);
                if (!$test) {
                    $test = $installregistry->packageExists($pkgname, 'pear.php.net');
                }
            } else {
                $test = $installregistry->packageExists($pkgname, $channel);
            }
            if (empty($options['force']) && $test) {
                return $this->raiseError("$channel/$pkgname is already installed");
            }
        } else {
            $usechannel = $channel;
            if ($channel == 'pecl.php.net') {
                $test = $installregistry->packageExists($pkgname, $channel);
                if (!$test) {
                    $test = $installregistry->packageExists($pkgname, 'pear.php.net');
                    $usechannel = 'pear.php.net';
                }
            } else {
                $test = $installregistry->packageExists($pkgname, $channel);
            }
            if ($test) {
                $v1 = $installregistry->packageInfo($pkgname, 'version', $usechannel);
                $v2 = $pkg->getVersion();
                $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, $usechannel,
                          true))) {
                        if (!isset($options['ignore-errors'])) {
                            return $this->raiseError($err);
                        } else {
                            if (!isset($options['soft'])) {
                                $this->log(0, 'WARNING: ' . $err->getMessage());
                            }
                        }
                    } else {
                        $backedup = $err;
                    }
                }
            }
        }

        // {{{ Copy files to dest dir ---------------------------------------

        // info from the package it self we want to access from _installFile
        $this->pkginfo = &$pkg;
        // used to determine whether we should build any C code
        $this->source_files = 0;

        $savechannel = $this->config->get('default_channel');
        if (empty($options['register-only']) && !is_dir($php_dir)) {
            if (PEAR::isError(System::mkdir(array('-p'), $php_dir))) {
                return $this->raiseError("no installation destination directory '$php_dir'\n");
            }
        }

        $tmp_path = dirname($descfile);
        if (substr($pkgfile, -4) != '.xml') {
            $tmp_path .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkg->getVersion();
        }

        $this->configSet('default_channel', $channel);
        // {{{ install files

        $ver = $pkg->getPackagexmlVersion();
        if (version_compare($ver, '2.0', '>=')) {
            $filelist = $pkg->getInstallationFilelist();
        } else {
            $filelist = $pkg->getFileList();
        }
        if (PEAR::isError($filelist)) {
            return $filelist;
        }
        $p = &$installregistry->getPackage($pkgname, $channel);
        if (empty($options['register-only']) && $p) {
            $dirtree = $p->getDirTree();
        } else {
            $dirtree = false;
        }
        $pkg->resetFilelist();
        $pkg->setLastInstalledVersion($installregistry->packageInfo($pkg->getPackage(),
            'version', $pkg->getChannel()));
        foreach ($filelist as $file => $atts) {
            if ($pkg->getPackagexmlVersion() == '1.0') {
                $this->expectError(PEAR_INSTALLER_FAILED);
                $res = $this->_installFile($file, $atts, $tmp_path, $options);
                $this->popExpect();
            } else {
                $this->expectError(PEAR_INSTALLER_FAILED);
                $res = $this->_installFile2($pkg, $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 {
                    if (!isset($options['soft'])) {
                        $this->log(0, "Warning: " . $res->getMessage());
                    }
                }
            }
            $real = isset($atts['attribs']) ? $atts['attribs'] : $atts;
            if ($res == PEAR_INSTALLER_OK && $real['role'] != 'src') {
                // Register files that were installed
                $pkg->installedFile($file, $atts);
            }
        }
        // }}}

        // {{{ compile and install source files
        if ($this->source_files > 0 && empty($options['nobuild'])) {
            if (PEAR::isError($err =
                  $this->_compileSourceFiles($savechannel, $pkg))) {
                return $err;
            }
        }
        // }}}

        if (isset($backedup)) {
            $this->_removeBackups($backedup);
        }
        if (!$this->commitFileTransaction()) {
            $this->rollbackFileTransaction();
            $this->configSet('default_channel', $savechannel);
            return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED);
        }
        // }}}

        $ret = false;
        $installphase = 'install';
        $oldversion = false;
        // {{{ Register that the package is installed -----------------------
        if (empty($options['upgrade'])) {
            // if 'force' is used, replace the info in registry
            $usechannel = $channel;
            if ($channel == 'pecl.php.net') {
                $test = $installregistry->packageExists($pkgname, $channel);
                if (!$test) {
                    $test = $installregistry->packageExists($pkgname, 'pear.php.net');
                    $usechannel = 'pear.php.net';
                }
            } else {
                $test = $installregistry->packageExists($pkgname, $channel);
            }
            if (!empty($options['force']) && $test) {
                $oldversion = $installregistry->packageInfo($pkgname, 'version', $usechannel);
                $installregistry->deletePackage($pkgname, $usechannel);
            }
            $ret = $installregistry->addPackage2($pkg);
        } else {
            if ($dirtree) {
                $this->startFileTransaction();
                // attempt to delete empty directories
                uksort($dirtree, array($this, '_sortDirs'));
                foreach($dirtree as $dir => $notused) {
                    $this->addFileOperation('rmdir', array($dir));
                }
                $this->commitFileTransaction();
            }
            $usechannel = $channel;
            if ($channel == 'pecl.php.net') {
                $test = $installregistry->packageExists($pkgname, $channel);
                if (!$test) {
                    $test = $installregistry->packageExists($pkgname, 'pear.php.net');
                    $usechannel = 'pear.php.net';
                }
            } else {
                $test = $installregistry->packageExists($pkgname, $channel);
            }
            // new: upgrade installs a package if it isn't installed
            if (!$test) {
                $ret = $installregistry->addPackage2($pkg);
            } else {
                if ($usechannel != $channel) {
                    $installregistry->deletePackage($pkgname, $usechannel);
                    $ret = $installregistry->addPackage2($pkg);
                } else {
                    $ret = $installregistry->updatePackage2($pkg);
                }
                $installphase = 'upgrade';
            }
        }
        if (!$ret) {
            $this->configSet('default_channel', $savechannel);
            return $this->raiseError("Adding package $channel/$pkgname to registry failed");
        }
        // }}}
        $this->configSet('default_channel', $savechannel);
        if (class_exists('PEAR_Task_Common')) { // this is auto-included if any tasks exist
            if (PEAR_Task_Common::hasPostinstallTasks()) {
                PEAR_Task_Common::runPostinstallTasks($installphase);
            }
        }
        return $pkg->toArray(true);
    }

    // }}}

    // {{{ _compileSourceFiles()
    /**
     * @param string
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     */
    function _compileSourceFiles($savechannel, &$filelist)
    {
        require_once 'PEAR/Builder.php';
        $this->log(1, "$this->source_files source files, building");
        $bob = &new PEAR_Builder($this->ui);
        $bob->debug = $this->debug;
        $built = $bob->build($filelist, array(&$this, '_buildCallback'));
        if (PEAR::isError($built)) {
            $this->rollbackFileTransaction();
            $this->configSet('default_channel', $savechannel);
            return $built;
        }
        $this->log(1, "\nBuild process completed successfully");
        foreach ($built as $ext) {
            $bn = basename($ext['file']);
            list($_ext_name, $_ext_suff) = explode('.', $bn);
            if ($_ext_suff == '.so' || $_ext_suff == '.dll') {
                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');
                }
                $role = 'ext';
            } else {
                $role = 'src';
            }
            $dest = $ext['dest'];
            $packagingroot = '';
            if (isset($this->_options['packagingroot'])) {
                $packagingroot = $this->_options['packagingroot'];
            }
            $copyto = $this->_prependPath($dest, $packagingroot);
            if ($copyto != $dest) {
	            $this->log(1, "Installing '$dest' as '$copyto'");
            } else {
	            $this->log(1, "Installing '$dest'");
            }
            $copydir = dirname($copyto);
            // pretty much nothing happens if we are only registering the install
            if (empty($this->_options['register-only'])) {
                if (!file_exists($copydir) || !is_dir($copydir)) {
                    if (!$this->mkDirHier($copydir)) {
                        return $this->raiseError("failed to mkdir $copydir",
                            PEAR_INSTALLER_FAILED);
                    }
                    $this->log(3, "+ mkdir $copydir");
                }
                if (!@copy($ext['file'], $copyto)) {
                    return $this->raiseError("failed to write $copyto ($php_errormsg)", PEAR_INSTALLER_FAILED);
                }
                $this->log(3, "+ cp $ext[file] $copyto");
                $this->addFileOperation('rename', array($ext['file'], $copyto));
                if (!OS_WINDOWS) {
                    $mode = 0666 & ~(int)octdec($this->config->get('umask'));
                    $this->addFileOperation('chmod', array($mode, $copyto));
                    if (!@chmod($copyto, $mode)) {
                        $this->log(0, "failed to change mode of $copyto ($php_errormsg)");
                    }
                }
            }

            if ($filelist->getPackageXmlVersion() == '1.0') {
                $filelist->installedFile($bn, array(
                    'role' => $role,
                    'name' => $bn,
                    'installed_as' => $dest,
                    'php_api' => $ext['php_api'],
                    'zend_mod_api' => $ext['zend_mod_api'],
                    'zend_ext_api' => $ext['zend_ext_api'],
                    ));
            } else {
                $filelist->installedFile($bn, array('attribs' => array(
                    'role' => $role,
                    'name' => $bn,
                    'installed_as' => $dest,
                    'php_api' => $ext['php_api'],
                    'zend_mod_api' => $ext['zend_mod_api'],
                    'zend_ext_api' => $ext['zend_ext_api'],
                    )));
            }
        }
    }

    // }}}
    function &getUninstallPackages()
    {
        return $this->_downloadedPackages;
    }
    // {{{ 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
     *              - register-only : update registry but don't remove files
     *              - nodeps: do not process dependencies of other packages to ensure
     *                        uninstallation does not break things
     */
    function uninstall($package, $options = array())
    {
        if (isset($options['installroot'])) {
            $this->config->setInstallRoot($options['installroot']);
            $this->installroot = '';
        } else {
            $this->config->setInstallRoot('');
            $this->installroot = '';
        }
        $this->_registry = &$this->config->getRegistry();
        if (is_object($package)) {
            $channel = $package->getChannel();
            $pkg = $package;
            $package = $pkg->getPackage();
        } else {
            $pkg = false;
            $info = $this->_registry->parsePackageName($package,
                $this->config->get('default_channel'));
            $channel = $info['channel'];
            $package = $info['package'];
        }
        $savechannel = $this->config->get('default_channel');
        $this->configSet('default_channel', $channel);
        if (!is_object($pkg)) {
            $pkg = $this->_registry->getPackage($package, $channel);
        }
        if (!$pkg) {
            $this->configSet('default_channel', $savechannel);
            return $this->raiseError($this->_registry->parsedPackageNameToString(
                array(
                    'channel' => $channel,
                    'package' => $package
                ), true) . ' not installed');
        }
        if ($pkg->getInstalledBinary()) {
            // this is just an alias for a binary package
            return $this->_registry->deletePackage($package, $channel);
        }
        $filelist = $pkg->getFilelist();
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        if (!class_exists('PEAR_Dependency2')) {
            require_once 'PEAR/Dependency2.php';
        }
        $depchecker = &new PEAR_Dependency2($this->config, $options, 
            array('channel' => $channel, 'package' => $package),
            PEAR_VALIDATE_UNINSTALLING);
        $e = $depchecker->validatePackageUninstall($this);
        PEAR::staticPopErrorHandling();
        if (PEAR::isError($e)) {
            if (!isset($options['ignore-errors'])) {
                return $this->raiseError($e);
            } else {
                if (!isset($options['soft'])) {
                    $this->log(0, 'WARNING: ' . $e->getMessage());
                }
            }
        } elseif (is_array($e)) {
            if (!isset($options['soft'])) {
                $this->log(0, $e[0]);
            }
        }
        $this->pkginfo = &$pkg;
        // pretty much nothing happens if we are only registering the uninstall
        if (empty($options['register-only'])) {
            // {{{ Delete the files
            $this->startFileTransaction();
            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
            if (PEAR::isError($err = $this->_deletePackageFiles($package, $channel))) {
                PEAR::popErrorHandling();
                $this->rollbackFileTransaction();
                $this->configSet('default_channel', $savechannel);
                if (!isset($options['ignore-errors'])) {
                    return $this->raiseError($err);
                } else {
                    if (!isset($options['soft'])) {
                        $this->log(0, 'WARNING: ' . $err->getMessage());
                    }
                }
            } else {
                PEAR::popErrorHandling();
            }
            if (!$this->commitFileTransaction()) {
                $this->rollbackFileTransaction();
                if (!isset($options['ignore-errors'])) {
                    return $this->raiseError("uninstall failed");
                } elseif (!isset($options['soft'])) {
                    $this->log(0, 'WARNING: uninstall failed');
                }
            } else {
                $this->startFileTransaction();
                if ($dirtree = $pkg->getDirTree()) {
                    // attempt to delete empty directories
                    uksort($dirtree, array($this, '_sortDirs'));
                    foreach($dirtree as $dir => $notused) {
                        $this->addFileOperation('rmdir', array($dir));
                    }
                } else {
                    $this->configSet('default_channel', $savechannel);
                    return $this->_registry->deletePackage($package, $channel);
                }
                if (!$this->commitFileTransaction()) {
                    $this->rollbackFileTransaction();
                    if (!isset($options['ignore-errors'])) {
                        return $this->raiseError("uninstall failed");
                    } elseif (!isset($options['soft'])) {
                        $this->log(0, 'WARNING: uninstall failed');
                    }
                }
            }
            // }}}
        }

        $this->configSet('default_channel', $savechannel);
        // Register that the package is no longer installed
        return $this->_registry->deletePackage($package, $channel);
    }

    /**
     * Sort a list of arrays of array(downloaded packagefilename) by dependency.
     *
     * It also removes duplicate dependencies
     * @param array an array of PEAR_PackageFile_v[1/2] objects
     * @return array|PEAR_Error array of array(packagefilename, package.xml contents)
     */
    function sortPackagesForUninstall(&$packages)
    {
        $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->config);
        if (PEAR::isError($this->_dependencyDB)) {
            return $this->_dependencyDB;
        }
        usort($packages, array(&$this, '_sortUninstall'));
    }

    function _sortUninstall($a, $b)
    {
        if (!$a->getDeps() && !$b->getDeps()) {
            return 0; // neither package has dependencies, order is insignificant
        }
        if ($a->getDeps() && !$b->getDeps()) {
            return -1; // $a must be installed after $b because $a has dependencies
        }
        if (!$a->getDeps() && $b->getDeps()) {
            return 1; // $b must be installed after $a because $b has dependencies
        }
        // both packages have dependencies
        if ($this->_dependencyDB->dependsOn($a, $b)) {
            return -1;
        }
        if ($this->_dependencyDB->dependsOn($b, $a)) {
            return 1;
        }
        return 0;
    }

    // }}}
    // {{{ _sortDirs()
    function _sortDirs($a, $b)
    {
        if (strnatcmp($a, $b) == -1) return 1;
        if (strnatcmp($a, $b) == 1) return -1;
        return 0;
    }

    // }}}

    // {{{ _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) {
        if (!$fd = @fopen($file, 'r')) {
            return false;
        }
        fclose($fd);
        return md5(file_get_contents($filename));
    }
}
// }}}

?>PEAR-1.7.2/PEAR/PackageFile.php100644   1750   1750       40353 11014312742  11105 <?php
/**
 * PEAR_PackageFile, package.xml parsing utility class
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: PackageFile.php,v 1.41 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * needed for PEAR_VALIDATE_* constants
 */
require_once 'PEAR/Validate.php';
/**
 * Error code if the package.xml <package> tag does not contain a valid version
 */
define('PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION', 1);
/**
 * Error code if the package.xml <package> tag version is not supported (version 1.0 and 1.1 are the only supported versions,
 * currently
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_PACKAGEVERSION', 2);
/**
 * Abstraction for the package.xml package description file
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_PackageFile
{
    /**
     * @var PEAR_Config
     */
    var $_config;
    var $_debug;
    /**
     * Temp directory for uncompressing tgz files.
     * @var string|false
     */
    var $_tmpdir;
    var $_logger = false;
    /**
     * @var boolean
     */
    var $_rawReturn = false;

    /**
     *
     * @param   PEAR_Config $config
     * @param   ?   $debug
     * @param   string @tmpdir Optional temporary directory for uncompressing
     *          files
     */
    function PEAR_PackageFile(&$config, $debug = false, $tmpdir = false)
    {
        $this->_config = $config;
        $this->_debug = $debug;
        $this->_tmpdir = $tmpdir;
    }

    /**
     * Turn off validation - return a parsed package.xml without checking it
     *
     * This is used by the package-validate command
     */
    function rawReturn()
    {
        $this->_rawReturn = true;
    }

    function setLogger(&$l)
    {
        $this->_logger = &$l;
    }

    /**
     * Create a PEAR_PackageFile_Parser_v* of a given version.
     * @param   int $version
     * @return  PEAR_PackageFile_Parser_v1|PEAR_PackageFile_Parser_v1
     */
    function &parserFactory($version)
    {
        if (!in_array($version{0}, array('1', '2'))) {
            $a = false;
            return $a;
        }
        include_once 'PEAR/PackageFile/Parser/v' . $version{0} . '.php';
        $version = $version{0};
        $class = "PEAR_PackageFile_Parser_v$version";
        $a = new $class;
        return $a;
    }

    /**
     * For simpler unit-testing
     * @return string
     */
    function getClassPrefix()
    {
        return 'PEAR_PackageFile_v';
    }

    /**
     * Create a PEAR_PackageFile_v* of a given version.
     * @param   int $version
     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v1
     */
    function &factory($version)
    {
        if (!in_array($version{0}, array('1', '2'))) {
            $a = false;
            return $a;
        }
        include_once 'PEAR/PackageFile/v' . $version{0} . '.php';
        $version = $version{0};
        $class = $this->getClassPrefix() . $version;
        $a = new $class;
        return $a;
    }

    /**
     * Create a PEAR_PackageFile_v* from its toArray() method
     *
     * WARNING: no validation is performed, the array is assumed to be valid,
     * always parse from xml if you want validation.
     * @param   array $arr
     * @return PEAR_PackageFileManager_v1|PEAR_PackageFileManager_v2
     * @uses    factory() to construct the returned object.
     */
    function &fromArray($arr)
    {
        if (isset($arr['xsdversion'])) {
            $obj = &$this->factory($arr['xsdversion']);
            if ($this->_logger) {
                $obj->setLogger($this->_logger);
            }
            $obj->setConfig($this->_config);
            $obj->fromArray($arr);
            return $obj;
        } else {
            if (isset($arr['package']['attribs']['version'])) {
                $obj = &$this->factory($arr['package']['attribs']['version']);
            } else {
                $obj = &$this->factory('1.0');
            }
            if ($this->_logger) {
                $obj->setLogger($this->_logger);
            }
            $obj->setConfig($this->_config);
            $obj->fromArray($arr);
            return $obj;
        }
    }

    /**
     * Create a PEAR_PackageFile_v* from an XML string.
     * @access  public
     * @param   string $data contents of package.xml file
     * @param   int $state package state (one of PEAR_VALIDATE_* constants)
     * @param   string $file full path to the package.xml file (and the files
     *          it references)
     * @param   string $archive optional name of the archive that the XML was
     *          extracted from, if any
     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @uses    parserFactory() to construct a parser to load the package.
     */
    function &fromXmlString($data, $state, $file, $archive = false)
    {
        if (preg_match('/<package[^>]+version="([0-9]+\.[0-9]+)"/', $data, $packageversion)) {
            if (!in_array($packageversion[1], array('1.0', '2.0', '2.1'))) {
                return PEAR::raiseError('package.xml version "' . $packageversion[1] .
                    '" is not supported, only 1.0, 2.0, and 2.1 are supported.');
            }
            $object = &$this->parserFactory($packageversion[1]);
            if ($this->_logger) {
                $object->setLogger($this->_logger);
            }
            $object->setConfig($this->_config);
            $pf = $object->parse($data, $file, $archive);
            if (PEAR::isError($pf)) {
                return $pf;
            }
            if ($this->_rawReturn) {
                return $pf;
            }
            if ($pf->validate($state)) {
                if ($this->_logger) {
                    if ($pf->getValidationWarnings(false)) {
                        foreach ($pf->getValidationWarnings() as $warning) {
                            $this->_logger->log(0, 'WARNING: ' . $warning['message']);
                        }
                    }
                }
                if (method_exists($pf, 'flattenFilelist')) {
                    $pf->flattenFilelist(); // for v2
                }
                return $pf;
            } else {
                if ($this->_config->get('verbose') > 0) {
                    if ($this->_logger) {
                        if ($pf->getValidationWarnings(false)) {
                            foreach ($pf->getValidationWarnings(false) as $warning) {
                                $this->_logger->log(0, 'ERROR: ' . $warning['message']);
                            }
                        }
                    }
                }
                $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed',
                    2, null, null, $pf->getValidationWarnings());
                return $a;
            }
        } elseif (preg_match('/<package[^>]+version="([^"]+)"/', $data, $packageversion)) {
            $a = PEAR::raiseError('package.xml file "' . $file .
                '" has unsupported package.xml <package> version "' . $packageversion[1] . '"');
            return $a;
        } else {
            if (!class_exists('PEAR_ErrorStack')) {
                require_once 'PEAR/ErrorStack.php';
            }
            PEAR_ErrorStack::staticPush('PEAR_PackageFile',
                PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION,
                'warning', array('xml' => $data), 'package.xml "' . $file .
                    '" has no package.xml <package> version');
            $object = &$this->parserFactory('1.0');
            $object->setConfig($this->_config);
            $pf = $object->parse($data, $file, $archive);
            if (PEAR::isError($pf)) {
                return $pf;
            }
            if ($this->_rawReturn) {
                return $pf;
            }
            if ($pf->validate($state)) {
                if ($this->_logger) {
                    if ($pf->getValidationWarnings(false)) {
                        foreach ($pf->getValidationWarnings() as $warning) {
                            $this->_logger->log(0, 'WARNING: ' . $warning['message']);
                        }
                    }
                }
                if (method_exists($pf, 'flattenFilelist')) {
                    $pf->flattenFilelist(); // for v2
                }
                return $pf;
            } else {
                $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed',
                    2, null, null, $pf->getValidationWarnings());
                return $a;
            }
        }
    }

    /**
     * Register a temporary file or directory.  When the destructor is
     * executed, all registered temporary files and directories are
     * removed.
     *
     * @param string  $file  name of file or directory
     * @return  void
     */
    function addTempFile($file)
    {
        $GLOBALS['_PEAR_Common_tempfiles'][] = $file;
    }

    /**
     * Create a PEAR_PackageFile_v* from a compresed Tar or Tgz file.
     * @access  public
     * @param string contents of package.xml file
     * @param int package state (one of PEAR_VALIDATE_* constants)
     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @using   Archive_Tar to extract the files
     * @using   fromPackageFile() to load the package after the package.xml
     *          file is extracted.
     */
    function &fromTgzFile($file, $state)
    {
        if (!class_exists('Archive_Tar')) {
            require_once 'Archive/Tar.php';
        }
        $tar = new Archive_Tar($file);
        if ($this->_debug <= 1) {
            $tar->pushErrorHandling(PEAR_ERROR_RETURN);
        }
        $content = $tar->listContent();
        if ($this->_debug <= 1) {
            $tar->popErrorHandling();
        }
        if (!is_array($content)) {
            if (is_string($file) && strlen($file < 255) &&
                  (!file_exists($file) || !@is_file($file))) {
                $ret = PEAR::raiseError("could not open file \"$file\"");
                return $ret;
            }
            $file = realpath($file);
            $ret = PEAR::raiseError("Could not get contents of package \"$file\"".
                                     '. Invalid tgz file.');
            return $ret;
        } else {
            if (!count($content) && !@is_file($file)) {
                $ret = PEAR::raiseError("could not open file \"$file\"");
                return $ret;
            }
        }
        $xml = null;
        $origfile = $file;
        foreach ($content as $file) {
            $name = $file['filename'];
            if ($name == 'package2.xml') { // allow a .tgz to distribute both versions
                $xml = $name;
                break;
            }
            if ($name == 'package.xml') {
                $xml = $name;
                break;
            } elseif (ereg('package.xml$', $name, $match)) {
                $xml = $name;
                break;
            }
        }
        if ($this->_tmpdir) {
            $tmpdir = $this->_tmpdir;
        } else {
            $tmpdir = System::mkTemp(array('-d', 'pear'));
            PEAR_PackageFile::addTempFile($tmpdir);
        }
        $this->_extractErrors();
        PEAR::staticPushErrorHandling(PEAR_ERROR_CALLBACK, array($this, '_extractErrors'));
        if (!$xml || !$tar->extractList(array($xml), $tmpdir)) {
            $extra = implode("\n", $this->_extractErrors());
            if ($extra) {
                $extra = ' ' . $extra;
            }
            PEAR::staticPopErrorHandling();
            $ret = PEAR::raiseError('could not extract the package.xml file from "' .
                $origfile . '"' . $extra);
            return $ret;
        }
        PEAR::staticPopErrorHandling();
        $ret = &PEAR_PackageFile::fromPackageFile("$tmpdir/$xml", $state, $origfile);
        return $ret;
    }

    /**
     * helper for extracting Archive_Tar errors
     * @var array
     * @access private
     */
    var $_extractErrors = array();

    /**
     * helper callback for extracting Archive_Tar errors
     *
     * @param PEAR_Error|null $err
     * @return array
     * @access private
     */
    function _extractErrors($err = null)
    {
        static $errors = array();
        if ($err === null) {
            $e = $errors;
            $errors = array();
            return $e;
        }
        $errors[] = $err->getMessage();
    }

    /**
     * Create a PEAR_PackageFile_v* from a package.xml file.
     *
     * @access public
     * @param   string  $descfile  name of package xml file
     * @param   int     $state package state (one of PEAR_VALIDATE_* constants)
     * @param   string|false $archive name of the archive this package.xml came
     *          from, if any
     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @uses    PEAR_PackageFile::fromXmlString to create the oject after the
     *          XML is loaded from the package.xml file.
     */
    function &fromPackageFile($descfile, $state, $archive = false)
    {
        if (is_string($descfile) && strlen($descfile) < 255 &&
             (!file_exists($descfile) || !is_file($descfile) || !is_readable($descfile) ||
             (!$fp = @fopen($descfile, 'r')))) {
            $a = PEAR::raiseError("Unable to open $descfile");
            return $a;
        }

        // read the whole thing so we only get one cdata callback
        // for each block of cdata
        fclose($fp);
        $data = file_get_contents($descfile);
        $ret = &PEAR_PackageFile::fromXmlString($data, $state, $descfile, $archive);
        return $ret;
    }


    /**
     * Create a PEAR_PackageFile_v* from a .tgz archive or package.xml file.
     *
     * This method is able to extract information about a package from a .tgz
     * archive or from a XML package definition file.
     *
     * @access public
     * @param   string  $info file name
     * @param   int     $state package state (one of PEAR_VALIDATE_* constants)
     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @uses    fromPackageFile() if the file appears to be XML
     * @uses    fromTgzFile() to load all non-XML files
     */
    function &fromAnyFile($info, $state)
    {
        if (is_dir($info)) {
            $dir_name = realpath($info);
            if (file_exists($dir_name . '/package.xml')) {
                $info = PEAR_PackageFile::fromPackageFile($dir_name .  '/package.xml', $state);
            } elseif (file_exists($dir_name .  '/package2.xml')) {
                $info = PEAR_PackageFile::fromPackageFile($dir_name .  '/package2.xml', $state);
            } else {
                $info = PEAR::raiseError("No package definition found in '$info' directory");
            }
            return $info;
        }

        $fp = false;
        if (is_string($info) && strlen($info) < 255 &&
             (file_exists($info) || ($fp = @fopen($info, 'r')))) {
            if ($fp) {
                fclose($fp);
            }
            $tmp = substr($info, -4);
            if ($tmp == '.xml') {
                $info = &PEAR_PackageFile::fromPackageFile($info, $state);
            } elseif ($tmp == '.tar' || $tmp == '.tgz') {
                $info = &PEAR_PackageFile::fromTgzFile($info, $state);
            } else {
                $fp = fopen($info, "r");
                $test = fread($fp, 5);
                fclose($fp);
                if ($test == "<?xml") {
                    $info = &PEAR_PackageFile::fromPackageFile($info, $state);
                } else {
                    $info = &PEAR_PackageFile::fromTgzFile($info, $state);
                }
            }
        } else {
            $info = PEAR::raiseError("Cannot open '$info' for parsing");
            return $info;
        }
        return $info;
    }
}

?>
PEAR-1.7.2/PEAR/Packager.php100644   1750   1750       17163 11014312742  10472 <?php
/**
 * PEAR_Packager for generating releases
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V. V. Cox <cox@idecnet.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Packager.php,v 1.71 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * base class
 */
require_once 'PEAR/Common.php';
require_once 'PEAR/PackageFile.php';
require_once 'System.php';

/**
 * Administration class used to make a PEAR release tarball.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */
class PEAR_Packager extends PEAR_Common
{
    /**
     * @var PEAR_Registry
     */
    var $_registry;
    // {{{ package()

    function package($pkgfile = null, $compress = true, $pkg2 = null)
    {
        // {{{ validate supplied package.xml file
        if (empty($pkgfile)) {
            $pkgfile = 'package.xml';
        }
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        $pkg = &new PEAR_PackageFile($this->config, $this->debug);
        $pf = &$pkg->fromPackageFile($pkgfile, PEAR_VALIDATE_NORMAL);
        $main = &$pf;
        PEAR::staticPopErrorHandling();
        if (PEAR::isError($pf)) {
            if (is_array($pf->getUserInfo())) {
                foreach ($pf->getUserInfo() as $error) {
                    $this->log(0, 'Error: ' . $error['message']);
                }
            }
            $this->log(0, $pf->getMessage());
            return $this->raiseError("Cannot package, errors in package file");
        } else {
            foreach ($pf->getValidationWarnings() as $warning) {
                $this->log(1, 'Warning: ' . $warning['message']);
            }
        }

        // }}}
        if ($pkg2) {
            $this->log(0, 'Attempting to process the second package file');
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            $pf2 = &$pkg->fromPackageFile($pkg2, PEAR_VALIDATE_NORMAL);
            PEAR::staticPopErrorHandling();
            if (PEAR::isError($pf2)) {
                if (is_array($pf2->getUserInfo())) {
                    foreach ($pf2->getUserInfo() as $error) {
                        $this->log(0, 'Error: ' . $error['message']);
                    }
                }
                $this->log(0, $pf2->getMessage());
                return $this->raiseError("Cannot package, errors in second package file");
            } else {
                foreach ($pf2->getValidationWarnings() as $warning) {
                    $this->log(1, 'Warning: ' . $warning['message']);
                }
            }
            if ($pf2->getPackagexmlVersion() == '2.0' ||
                  $pf2->getPackagexmlVersion() == '2.1') {
                $main = &$pf2;
                $other = &$pf;
            } else {
                $main = &$pf;
                $other = &$pf2;
            }
            if ($main->getPackagexmlVersion() != '2.0' &&
                  $main->getPackagexmlVersion() != '2.1') {
                return PEAR::raiseError('Error: cannot package two package.xml version 1.0, can ' .
                    'only package together a package.xml 1.0 and package.xml 2.0');
            }
            if ($other->getPackagexmlVersion() != '1.0') {
                return PEAR::raiseError('Error: cannot package two package.xml version 2.0, can ' .
                    'only package together a package.xml 1.0 and package.xml 2.0');
            }
        }
        $main->setLogger($this);
        if (!$main->validate(PEAR_VALIDATE_PACKAGING)) {
            foreach ($main->getValidationWarnings() as $warning) {
                $this->log(0, 'Error: ' . $warning['message']);
            }
            return $this->raiseError("Cannot package, errors in package");
        } else {
            foreach ($main->getValidationWarnings() as $warning) {
                $this->log(1, 'Warning: ' . $warning['message']);
            }
        }
        if ($pkg2) {
            $other->setLogger($this);
            $a = false;
            if (!$other->validate(PEAR_VALIDATE_NORMAL) || $a = !$main->isEquivalent($other)) {
                foreach ($other->getValidationWarnings() as $warning) {
                    $this->log(0, 'Error: ' . $warning['message']);
                }
                foreach ($main->getValidationWarnings() as $warning) {
                    $this->log(0, 'Error: ' . $warning['message']);
                }
                if ($a) {
                    return $this->raiseError('The two package.xml files are not equivalent!');
                }
                return $this->raiseError("Cannot package, errors in package");
            } else {
                foreach ($other->getValidationWarnings() as $warning) {
                    $this->log(1, 'Warning: ' . $warning['message']);
                }
            }
            $gen = &$main->getDefaultGenerator();
            $tgzfile = $gen->toTgz2($this, $other, $compress);
            if (PEAR::isError($tgzfile)) {
                return $tgzfile;
            }
            $dest_package = basename($tgzfile);
            $pkgdir = dirname($pkgfile);
    
            // TAR the Package -------------------------------------------
            $this->log(1, "Package $dest_package done");
            if (file_exists("$pkgdir/CVS/Root")) {
                $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $pf->getVersion());
                $cvstag = "RELEASE_$cvsversion";
                $this->log(1, 'Tag the released code with "pear cvstag ' .
                    $main->getPackageFile() . '"');
                $this->log(1, "(or set the CVS tag $cvstag by hand)");
            }
        } else { // this branch is executed for single packagefile packaging
            $gen = &$pf->getDefaultGenerator();
            $tgzfile = $gen->toTgz($this, $compress);
            if (PEAR::isError($tgzfile)) {
                $this->log(0, $tgzfile->getMessage());
                return $this->raiseError("Cannot package, errors in package");
            }
            $dest_package = basename($tgzfile);
            $pkgdir = dirname($pkgfile);
    
            // TAR the Package -------------------------------------------
            $this->log(1, "Package $dest_package done");
            if (file_exists("$pkgdir/CVS/Root")) {
                $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $pf->getVersion());
                $cvstag = "RELEASE_$cvsversion";
                $this->log(1, "Tag the released code with `pear cvstag $pkgfile'");
                $this->log(1, "(or set the CVS tag $cvstag by hand)");
            }
        }
        return $dest_package;
    }

    // }}}
}

// {{{ md5_file() utility function
if (!function_exists('md5_file')) {
    function md5_file($file) {
        if (!$fd = @fopen($file, 'r')) {
            return false;
        }
        fclose($fd);
        $md5 = md5(file_get_contents($file));
        return $md5;
    }
}
// }}}

?>
PEAR-1.7.2/PEAR/Registry.php100644   1750   1750      220133 11014312742  10576 <?php
/**
 * PEAR_Registry
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V. V. Cox <cox@idecnet.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Registry.php,v 1.171 2008/05/14 04:16:08 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * for PEAR_Error
 */
require_once 'PEAR.php';
require_once 'PEAR/DependencyDB.php';

define('PEAR_REGISTRY_ERROR_LOCK',   -2);
define('PEAR_REGISTRY_ERROR_FORMAT', -3);
define('PEAR_REGISTRY_ERROR_FILE',   -4);
define('PEAR_REGISTRY_ERROR_CONFLICT', -5);
define('PEAR_REGISTRY_ERROR_CHANNEL_FILE', -6);

/**
 * Administration class used to maintain the installed package database.
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V. V. Cox <cox@idecnet.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Registry extends PEAR
{
    // {{{ properties

    /**
     * File containing all channel information.
     * @var string
     */
    var $channels = '';

    /** Directory where registry files are stored.
     * @var string
     */
    var $statedir = '';

    /** File where the file map is stored
     * @var string
     */
    var $filemap = '';

    /** Directory where registry files for channels are stored.
     * @var string
     */
    var $channelsdir = '';

    /** Name of file used for locking the registry
     * @var string
     */
    var $lockfile = '';

    /** File descriptor used during locking
     * @var resource
     */
    var $lock_fp = null;

    /** Mode used during locking
     * @var int
     */
    var $lock_mode = 0; // XXX UNUSED

    /** Cache of package information.  Structure:
     * array(
     *   'package' => array('id' => ... ),
     *   ... )
     * @var array
     */
    var $pkginfo_cache = array();

    /** Cache of file map.  Structure:
     * array( '/path/to/file' => 'package', ... )
     * @var array
     */
    var $filemap_cache = array();

    /**
     * @var false|PEAR_ChannelFile
     */
    var $_pearChannel;

    /**
     * @var false|PEAR_ChannelFile
     */
    var $_peclChannel;

    /**
     * @var PEAR_DependencyDB
     */
    var $_dependencyDB;

    /**
     * @var PEAR_Config
     */
    var $_config;
    // }}}

    // {{{ constructor

    /**
     * PEAR_Registry constructor.
     *
     * @param string (optional) PEAR install directory (for .php files)
     * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PEAR channel, if
     *        default values are not desired.  Only used the very first time a PEAR
     *        repository is initialized
     * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PECL channel, if
     *        default values are not desired.  Only used the very first time a PEAR
     *        repository is initialized
     *
     * @access public
     */
    function PEAR_Registry($pear_install_dir = PEAR_INSTALL_DIR, $pear_channel = false,
                           $pecl_channel = false)
    {
        parent::PEAR();
        $this->setInstallDir($pear_install_dir);
        $this->_pearChannel = $pear_channel;
        $this->_peclChannel = $pecl_channel;
        $this->_config = false;
    }

    function setInstallDir($pear_install_dir = PEAR_INSTALL_DIR)
    {
        $ds = DIRECTORY_SEPARATOR;
        $this->install_dir = $pear_install_dir;
        $this->channelsdir = $pear_install_dir.$ds.'.channels';
        $this->statedir = $pear_install_dir.$ds.'.registry';
        $this->filemap  = $pear_install_dir.$ds.'.filemap';
        $this->lockfile = $pear_install_dir.$ds.'.lock';
    }

    function hasWriteAccess()
    {
        if (!file_exists($this->install_dir)) {
            $dir = $this->install_dir;
            while ($dir && $dir != '.') {
                $olddir = $dir;
                $dir = dirname($dir); // cd ..
                if ($dir != '.' && file_exists($dir)) {
                    if (is_writeable($dir)) {
                        return true;
                    } else {
                        return false;
                    }
                }
                if ($dir == $olddir) { // this can happen in safe mode
                    return @is_writable($dir);
                }
            }
            return false;
        }
        return is_writeable($this->install_dir);
    }

    function setConfig(&$config, $resetInstallDir = true)
    {
        $this->_config = &$config;
        if ($resetInstallDir) {
            $this->setInstallDir($config->get('php_dir'));
        }
    }

    function _initializeChannelDirs()
    {
        static $running = false;
        if (!$running) {
            $running = true;
            $ds = DIRECTORY_SEPARATOR;
            if (!is_dir($this->channelsdir) ||
                  !file_exists($this->channelsdir . $ds . 'pear.php.net.reg') ||
                  !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') ||
                  !file_exists($this->channelsdir . $ds . '__uri.reg')) {
                if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
                    $pear_channel = $this->_pearChannel;
                    if (!is_a($pear_channel, 'PEAR_ChannelFile') || !$pear_channel->validate()) {
                        if (!class_exists('PEAR_ChannelFile')) {
                            require_once 'PEAR/ChannelFile.php';
                        }
                        $pear_channel = new PEAR_ChannelFile;
                        $pear_channel->setName('pear.php.net');
                        $pear_channel->setAlias('pear');
                        $pear_channel->setServer('pear.php.net');
                        $pear_channel->setSummary('PHP Extension and Application Repository');
                        $pear_channel->setDefaultPEARProtocols();
                        $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/');
                        $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/');
                    } else {
                        $pear_channel->setName('pear.php.net');
                        $pear_channel->setAlias('pear');
                    }
                    $pear_channel->validate();
                    $this->_addChannel($pear_channel);
                }
                if (!file_exists($this->channelsdir . $ds . 'pecl.php.net.reg')) {
                    $pecl_channel = $this->_peclChannel;
                    if (!is_a($pecl_channel, 'PEAR_ChannelFile') || !$pecl_channel->validate()) {
                        if (!class_exists('PEAR_ChannelFile')) {
                            require_once 'PEAR/ChannelFile.php';
                        }
                        $pecl_channel = new PEAR_ChannelFile;
                        $pecl_channel->setName('pecl.php.net');
                        $pecl_channel->setAlias('pecl');
                        $pecl_channel->setServer('pecl.php.net');
                        $pecl_channel->setSummary('PHP Extension Community Library');
                        $pecl_channel->setDefaultPEARProtocols();
                        $pecl_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/');
                        $pecl_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/');
                        $pecl_channel->setValidationPackage('PEAR_Validator_PECL', '1.0');
                    } else {
                        $pecl_channel->setName('pecl.php.net');
                        $pecl_channel->setAlias('pecl');
                    }
                    $pecl_channel->validate();
                    $this->_addChannel($pecl_channel);
                }
                if (!file_exists($this->channelsdir . $ds . '__uri.reg')) {
                    if (!class_exists('PEAR_ChannelFile')) {
                        require_once 'PEAR/ChannelFile.php';
                    }
                    $private = new PEAR_ChannelFile;
                    $private->setName('__uri');
                    $private->addFunction('xmlrpc', '1.0', '****');
                    $private->setSummary('Pseudo-channel for static packages');
                    $this->_addChannel($private);
                }
                $this->_rebuildFileMap();
            }
            $running = false;
        }
    }

    function _initializeDirs()
    {
        $ds = DIRECTORY_SEPARATOR;
        // XXX Compatibility code should be removed in the future
        // rename all registry files if any to lowercase
        if (!OS_WINDOWS && file_exists($this->statedir) && is_dir($this->statedir) &&
              $handle = opendir($this->statedir)) {
            $dest = $this->statedir . $ds;
            while (false !== ($file = readdir($handle))) {
                if (preg_match('/^.*[A-Z].*\.reg\\z/', $file)) {
                    rename($dest . $file, $dest . strtolower($file));
                }
            }
            closedir($handle);
        }
        $this->_initializeChannelDirs();
        if (!file_exists($this->filemap)) {
            $this->_rebuildFileMap();
        }
        $this->_initializeDepDB();
    }

    function _initializeDepDB()
    {
        if (!isset($this->_dependencyDB)) {
            static $initializing = false;
            if (!$initializing) {
                $initializing = true;
                if (!$this->_config) { // never used?
                    if (OS_WINDOWS) {
                        $file = 'pear.ini';
                    } else {
                        $file = '.pearrc';
                    }
                    $this->_config = &new PEAR_Config($this->statedir . DIRECTORY_SEPARATOR .
                        $file);
                    $this->_config->setRegistry($this);
                    $this->_config->set('php_dir', $this->install_dir);
                }
                $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config);
                if (PEAR::isError($this->_dependencyDB)) {
                    // attempt to recover by removing the dep db
                    if (file_exists($this->_config->get('php_dir', null, 'pear.php.net') .
                        DIRECTORY_SEPARATOR . '.depdb')) {
                        @unlink($this->_config->get('php_dir', null, 'pear.php.net') .
                            DIRECTORY_SEPARATOR . '.depdb');
                    }
                    $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config);
                    if (PEAR::isError($this->_dependencyDB)) {
                        echo $this->_dependencyDB->getMessage();
                        echo 'Unrecoverable error';
                        exit(1);
                    }
                }
                $initializing = false;
            }
        }
    }
    // }}}
    // {{{ destructor

    /**
     * PEAR_Registry destructor.  Makes sure no locks are forgotten.
     *
     * @access private
     */
    function _PEAR_Registry()
    {
        parent::_PEAR();
        if (is_resource($this->lock_fp)) {
            $this->_unlock();
        }
    }

    // }}}

    // {{{ _assertStateDir()

    /**
     * Make sure the directory where we keep registry files exists.
     *
     * @return bool TRUE if directory exists, FALSE if it could not be
     * created
     *
     * @access private
     */
    function _assertStateDir($channel = false)
    {
        if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
            return $this->_assertChannelStateDir($channel);
        }
        static $init = false;
        if (!file_exists($this->statedir)) {
            if (!$this->hasWriteAccess()) {
                return false;
            }
            require_once 'System.php';
            if (!System::mkdir(array('-p', $this->statedir))) {
                return $this->raiseError("could not create directory '{$this->statedir}'");
            }
            $init = true;
        } elseif (!is_dir($this->statedir)) {
            return $this->raiseError('Cannot create directory ' . $this->statedir . ', ' .
                'it already exists and is not a directory');
        }
        $ds = DIRECTORY_SEPARATOR;
        if (!file_exists($this->channelsdir)) {
            if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg') ||
                  !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') ||
                  !file_exists($this->channelsdir . $ds . '__uri.reg')) {
                $init = true;
            }
        } elseif (!is_dir($this->channelsdir)) {
            return $this->raiseError('Cannot create directory ' . $this->channelsdir . ', ' .
                'it already exists and is not a directory');
        }
        if ($init) {
            static $running = false;
            if (!$running) {
                $running = true;
                $this->_initializeDirs();
                $running = false;
                $init = false;
            }
        } else {
            $this->_initializeDepDB();
        }
        return true;
    }

    // }}}
    // {{{ _assertChannelStateDir()

    /**
     * Make sure the directory where we keep registry files exists for a non-standard channel.
     *
     * @param string channel name
     * @return bool TRUE if directory exists, FALSE if it could not be
     * created
     *
     * @access private
     */
    function _assertChannelStateDir($channel)
    {
        $ds = DIRECTORY_SEPARATOR;
        if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
            if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
                $this->_initializeChannelDirs();
            }
            return $this->_assertStateDir($channel);
        }
        $channelDir = $this->_channelDirectoryName($channel);
        if (!is_dir($this->channelsdir) ||
              !file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
            $this->_initializeChannelDirs();
        }
        if (!file_exists($channelDir)) {
            if (!$this->hasWriteAccess()) {
                return false;
            }
            require_once 'System.php';
            if (!System::mkdir(array('-p', $channelDir))) {
                return $this->raiseError("could not create directory '" . $channelDir .
                    "'");
            }
        } elseif (!is_dir($channelDir)) {
            return $this->raiseError("could not create directory '" . $channelDir .
                "', already exists and is not a directory");
        }
        return true;
    }

    // }}}
    // {{{ _assertChannelDir()

    /**
     * Make sure the directory where we keep registry files for channels exists
     *
     * @return bool TRUE if directory exists, FALSE if it could not be
     * created
     *
     * @access private
     */
    function _assertChannelDir()
    {
        if (!file_exists($this->channelsdir)) {
            if (!$this->hasWriteAccess()) {
                return false;
            }
            require_once 'System.php';
            if (!System::mkdir(array('-p', $this->channelsdir))) {
                return $this->raiseError("could not create directory '{$this->channelsdir}'");
            }
        } elseif (!is_dir($this->channelsdir)) {
            return $this->raiseError("could not create directory '{$this->channelsdir}" .
                "', it already exists and is not a directory");
            
        }
        if (!file_exists($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) {
            if (!$this->hasWriteAccess()) {
                return false;
            }
            require_once 'System.php';
            if (!System::mkdir(array('-p', $this->channelsdir . DIRECTORY_SEPARATOR . '.alias'))) {
                return $this->raiseError("could not create directory '{$this->channelsdir}/.alias'");
            }
        } elseif (!is_dir($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) {
            return $this->raiseError("could not create directory '{$this->channelsdir}" .
                "/.alias', it already exists and is not a directory");
            
        }
        return true;
    }

    // }}}
    // {{{ _packageFileName()

    /**
     * Get the name of the file where data for a given package is stored.
     *
     * @param string channel name, or false if this is a PEAR package
     * @param string package name
     *
     * @return string registry file name
     *
     * @access public
     */
    function _packageFileName($package, $channel = false)
    {
        if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
            return $this->_channelDirectoryName($channel) . DIRECTORY_SEPARATOR .
                strtolower($package) . '.reg';
        }
        return $this->statedir . DIRECTORY_SEPARATOR . strtolower($package) . '.reg';
    }

    // }}}
    // {{{ _channelFileName()

    /**
     * Get the name of the file where data for a given channel is stored.
     * @param string channel name
     * @return string registry file name
     */
    function _channelFileName($channel, $noaliases = false)
    {
        if (!$noaliases) {
            if (file_exists($this->_getChannelAliasFileName($channel))) {
                $channel = implode('', file($this->_getChannelAliasFileName($channel)));
            }
        }
        return $this->channelsdir . DIRECTORY_SEPARATOR . str_replace('/', '_',
            strtolower($channel)) . '.reg';
    }

    // }}}
    // {{{ getChannelAliasFileName()

    /**
     * @param string
     * @return string
     */
    function _getChannelAliasFileName($alias)
    {
        return $this->channelsdir . DIRECTORY_SEPARATOR . '.alias' .
              DIRECTORY_SEPARATOR . str_replace('/', '_', strtolower($alias)) . '.txt';
    }

    // }}}
    // {{{ _getChannelFromAlias()

    /**
     * Get the name of a channel from its alias
     */
    function _getChannelFromAlias($channel)
    {
        if (!$this->_channelExists($channel)) {
            if ($channel == 'pear.php.net') {
                return 'pear.php.net';
            }
            if ($channel == 'pecl.php.net') {
                return 'pecl.php.net';
            }
            if ($channel == '__uri') {
                return '__uri';
            }
            return false;
        }
        $channel = strtolower($channel);
        if (file_exists($this->_getChannelAliasFileName($channel))) {
            // translate an alias to an actual channel
            return implode('', file($this->_getChannelAliasFileName($channel)));
        } else {
            return $channel;
        }
    }    
    // }}}
    // {{{ _getChannelFromAlias()

    /**
     * Get the alias of a channel from its alias or its name
     */
    function _getAlias($channel)
    {
        if (!$this->_channelExists($channel)) {
            if ($channel == 'pear.php.net') {
                return 'pear';
            }
            if ($channel == 'pecl.php.net') {
                return 'pecl';
            }
            return false;
        }
        $channel = $this->_getChannel($channel);
        if (PEAR::isError($channel)) {
            return $channel;
        }
        return $channel->getAlias();
    }    
    // }}}
    // {{{ _channelDirectoryName()

    /**
     * Get the name of the file where data for a given package is stored.
     *
     * @param string channel name, or false if this is a PEAR package
     * @param string package name
     *
     * @return string registry file name
     *
     * @access public
     */
    function _channelDirectoryName($channel)
    {
        if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
            return $this->statedir;
        } else {
            $ch = $this->_getChannelFromAlias($channel);
            if (!$ch) {
                $ch = $channel;
            }
            return $this->statedir . DIRECTORY_SEPARATOR . strtolower('.channel.' .
                str_replace('/', '_', $ch));
        }
    }

    // }}}
    // {{{ _openPackageFile()

    function _openPackageFile($package, $mode, $channel = false)
    {
        if (!$this->_assertStateDir($channel)) {
            return null;
        }
        if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) {
            return null;
        }
        $file = $this->_packageFileName($package, $channel);
        if (!file_exists($file) && $mode == 'r' || $mode == 'rb') {
            return null;
        }
        $fp = @fopen($file, $mode);
        if (!$fp) {
            return null;
        }
        return $fp;
    }

    // }}}
    // {{{ _closePackageFile()

    function _closePackageFile($fp)
    {
        fclose($fp);
    }

    // }}}
    // {{{ _openChannelFile()

    function _openChannelFile($channel, $mode)
    {
        if (!$this->_assertChannelDir()) {
            return null;
        }
        if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) {
            return null;
        }
        $file = $this->_channelFileName($channel);
        if (!file_exists($file) && $mode == 'r' || $mode == 'rb') {
            return null;
        }
        $fp = @fopen($file, $mode);
        if (!$fp) {
            return null;
        }
        return $fp;
    }

    // }}}
    // {{{ _closePackageFile()

    function _closeChannelFile($fp)
    {
        fclose($fp);
    }

    // }}}
    // {{{ _rebuildFileMap()

    function _rebuildFileMap()
    {
        if (!class_exists('PEAR_Installer_Role')) {
            require_once 'PEAR/Installer/Role.php';
        }
        $channels = $this->_listAllPackages();
        $files = array();
        foreach ($channels as $channel => $packages) {
            foreach ($packages as $package) {
                $version = $this->_packageInfo($package, 'version', $channel);
                $filelist = $this->_packageInfo($package, 'filelist', $channel);
                if (!is_array($filelist)) {
                    continue;
                }
                foreach ($filelist as $name => $attrs) {
                    if (isset($attrs['attribs'])) {
                        $attrs = $attrs['attribs'];
                    }
                    // it is possible for conflicting packages in different channels to
                    // conflict with data files/doc files
                    if ($name == 'dirtree') {
                        continue;
                    }
                    if (isset($attrs['role']) && !in_array($attrs['role'],
                          PEAR_Installer_Role::getInstallableRoles())) {
                        // these are not installed
                        continue;
                    }
                    if (isset($attrs['role']) && !in_array($attrs['role'],
                          PEAR_Installer_Role::getBaseinstallRoles())) {
                        $attrs['baseinstalldir'] = $package;
                    }
                    if (isset($attrs['baseinstalldir'])) {
                        $file = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name;
                    } else {
                        $file = $name;
                    }
                    $file = preg_replace(',^/+,', '', $file);
                    if ($channel != 'pear.php.net') {
                        if (!isset($files[$attrs['role']])) {
                            $files[$attrs['role']] = array();
                        }
                        $files[$attrs['role']][$file] = array(strtolower($channel),
                            strtolower($package));
                    } else {
                        if (!isset($files[$attrs['role']])) {
                            $files[$attrs['role']] = array();
                        }
                        $files[$attrs['role']][$file] = strtolower($package);
                    }
                }
            }
        }
        $this->_assertStateDir();
        if (!$this->hasWriteAccess()) {
            return false;
        }
        $fp = @fopen($this->filemap, 'wb');
        if (!$fp) {
            return false;
        }
        $this->filemap_cache = $files;
        fwrite($fp, serialize($files));
        fclose($fp);
        return true;
    }

    // }}}
    // {{{ _readFileMap()

    function _readFileMap()
    {
        if (!file_exists($this->filemap)) {
            return array();
        }
        $fp = @fopen($this->filemap, 'r');
        if (!$fp) {
            return $this->raiseError('PEAR_Registry: could not open filemap "' . $this->filemap . '"', PEAR_REGISTRY_ERROR_FILE, null, null, $php_errormsg);
        }
        clearstatcache();
        $rt = get_magic_quotes_runtime();
        set_magic_quotes_runtime(0);
        $fsize = filesize($this->filemap);
        fclose($fp);
        $data = file_get_contents($this->filemap);
        set_magic_quotes_runtime($rt);
        $tmp = unserialize($data);
        if (!$tmp && $fsize > 7) {
            return $this->raiseError('PEAR_Registry: invalid filemap data', PEAR_REGISTRY_ERROR_FORMAT, null, null, $data);
        }
        $this->filemap_cache = $tmp;
        return true;
    }

    // }}}
    // {{{ _lock()

    /**
     * Lock the registry.
     *
     * @param integer lock mode, one of LOCK_EX, LOCK_SH or LOCK_UN.
     *                See flock manual for more information.
     *
     * @return bool TRUE on success, FALSE if locking failed, or a
     *              PEAR error if some other error occurs (such as the
     *              lock file not being writable).
     *
     * @access private
     */
    function _lock($mode = LOCK_EX)
    {
        if (!eregi('Windows 9', php_uname())) {
            if ($mode != LOCK_UN && is_resource($this->lock_fp)) {
                // XXX does not check type of lock (LOCK_SH/LOCK_EX)
                return true;
            }
            if (!$this->_assertStateDir()) {
                if ($mode == LOCK_EX) {
                    return $this->raiseError('Registry directory is not writeable by the current user');
                } else {
                    return true;
                }
            }
            $open_mode = 'w';
            // XXX People reported problems with LOCK_SH and 'w'
            if ($mode === LOCK_SH || $mode === LOCK_UN) {
                if (!file_exists($this->lockfile)) {
                    touch($this->lockfile);
                }
                $open_mode = 'r';
            }

            if (!is_resource($this->lock_fp)) {
                $this->lock_fp = @fopen($this->lockfile, $open_mode);
            }

            if (!is_resource($this->lock_fp)) {
                $this->lock_fp = null;
                return $this->raiseError("could not create lock file" .
                                         (isset($php_errormsg) ? ": " . $php_errormsg : ""));
            }
            if (!(int)flock($this->lock_fp, $mode)) {
                switch ($mode) {
                    case LOCK_SH: $str = 'shared';    break;
                    case LOCK_EX: $str = 'exclusive'; break;
                    case LOCK_UN: $str = 'unlock';    break;
                    default:      $str = 'unknown';   break;
                }
                //is resource at this point, close it on error.
                fclose($this->lock_fp);
                $this->lock_fp = null;
                return $this->raiseError("could not acquire $str lock ($this->lockfile)",
                                         PEAR_REGISTRY_ERROR_LOCK);
            }
        }
        return true;
    }

    // }}}
    // {{{ _unlock()

    function _unlock()
    {
        $ret = $this->_lock(LOCK_UN);
        if (is_resource($this->lock_fp)) {
            fclose($this->lock_fp);
        }
        $this->lock_fp = null;
        return $ret;
    }

    // }}}
    // {{{ _packageExists()

    function _packageExists($package, $channel = false)
    {
        return file_exists($this->_packageFileName($package, $channel));
    }

    // }}}
    // {{{ _channelExists()

    /**
     * Determine whether a channel exists in the registry
     * @param string Channel name
     * @param bool if true, then aliases will be ignored
     * @return boolean
     */
    function _channelExists($channel, $noaliases = false)
    {
        $a = file_exists($this->_channelFileName($channel, $noaliases));
        if (!$a && $channel == 'pear.php.net') {
            return true;
        }
        if (!$a && $channel == 'pecl.php.net') {
            return true;
        }
        return $a;
    }

    // }}}
    // {{{ _addChannel()

    /**
     * @param PEAR_ChannelFile Channel object
     * @param donotuse
     * @param string Last-Modified HTTP tag from remote request
     * @return boolean|PEAR_Error True on creation, false if it already exists
     */
    function _addChannel($channel, $update = false, $lastmodified = false)
    {
        if (!is_a($channel, 'PEAR_ChannelFile')) {
            return false;
        }
        if (!$channel->validate()) {
            return false;
        }
        if (file_exists($this->_channelFileName($channel->getName()))) {
            if (!$update) {
                return false;
            }
            $checker = $this->_getChannel($channel->getName());
            if (PEAR::isError($checker)) {
                return $checker;
            }
            if ($channel->getAlias() != $checker->getAlias()) {
                if (file_exists($this->_getChannelAliasFileName($checker->getAlias()))) {
                    @unlink($this->_getChannelAliasFileName($checker->getAlias()));
                }
            }
        } else {
            if ($update && !in_array($channel->getName(), array('pear.php.net', 'pecl.php.net'))) {
                return false;
            }
        }
        $ret = $this->_assertChannelDir();
        if (PEAR::isError($ret)) {
            return $ret;
        }
        $ret = $this->_assertChannelStateDir($channel->getName());
        if (PEAR::isError($ret)) {
            return $ret;
        }
        if ($channel->getAlias() != $channel->getName()) {
            if (file_exists($this->_getChannelAliasFileName($channel->getAlias())) &&
                  $this->_getChannelFromAlias($channel->getAlias()) != $channel->getName()) {
                $channel->setAlias($channel->getName());
            }
            if (!$this->hasWriteAccess()) {
                return false;
            }
            $fp = @fopen($this->_getChannelAliasFileName($channel->getAlias()), 'w');
            if (!$fp) {
                return false;
            }
            fwrite($fp, $channel->getName());
            fclose($fp);
        }
        if (!$this->hasWriteAccess()) {
            return false;
        }
        $fp = @fopen($this->_channelFileName($channel->getName()), 'wb');
        if (!$fp) {
            return false;
        }
        $info = $channel->toArray();
        if ($lastmodified) {
            $info['_lastmodified'] = $lastmodified;
        } else {
            $info['_lastmodified'] = date('r');
        }
        fwrite($fp, serialize($info));
        fclose($fp);
        return true;
    }

    // }}}
    // {{{ _deleteChannel()

    /**
     * Deletion fails if there are any packages installed from the channel
     * @param string|PEAR_ChannelFile channel name
     * @return boolean|PEAR_Error True on deletion, false if it doesn't exist
     */
    function _deleteChannel($channel)
    {
        if (!is_string($channel)) {
            if (is_a($channel, 'PEAR_ChannelFile')) {
                if (!$channel->validate()) {
                    return false;
                }
                $channel = $channel->getName();
            } else {
                return false;
            }
        }
        if ($this->_getChannelFromAlias($channel) == '__uri') {
            return false;
        }
        if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') {
            return false;
        }
        if (!$this->_channelExists($channel)) {
            return false;
        }
        if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
            return false;
        }
        $channel = $this->_getChannelFromAlias($channel);
        if ($channel == 'pear.php.net') {
            return false;
        }
        $test = $this->_listChannelPackages($channel);
        if (count($test)) {
            return false;
        }
        $test = @rmdir($this->_channelDirectoryName($channel));
        if (!$test) {
            return false;
        }
        $file = $this->_getChannelAliasFileName($this->_getAlias($channel));
        if (file_exists($file)) {
            $test = @unlink($file);
            if (!$test) {
                return false;
            }
        }
        $file = $this->_channelFileName($channel);
        $ret = true;
        if (file_exists($file)) {
            $ret = @unlink($file);
        }
        return $ret;
    }

    // }}}
    // {{{ _isChannelAlias()

    /**
     * Determine whether a channel exists in the registry
     * @param string Channel Alias
     * @return boolean
     */
    function _isChannelAlias($alias)
    {
        return file_exists($this->_getChannelAliasFileName($alias));
    }

    // }}}
    // {{{ _packageInfo()

    /**
     * @param string|null
     * @param string|null
     * @param string|null
     * @return array|null
     * @access private
     */
    function _packageInfo($package = null, $key = null, $channel = 'pear.php.net')
    {
        if ($package === null) {
            if ($channel === null) {
                $channels = $this->_listChannels();
                $ret = array();
                foreach ($channels as $channel) {
                    $channel = strtolower($channel);
                    $ret[$channel] = array();
                    $packages = $this->_listPackages($channel);
                    foreach ($packages as $package) {
                        $ret[$channel][] = $this->_packageInfo($package, null, $channel);
                    }
                }
                return $ret;
            }
            $ps = $this->_listPackages($channel);
            if (!count($ps)) {
                return array();
            }
            return array_map(array(&$this, '_packageInfo'),
                             $ps, array_fill(0, count($ps), null),
                             array_fill(0, count($ps), $channel));
        }
        $fp = $this->_openPackageFile($package, 'r', $channel);
        if ($fp === null) {
            return null;
        }
        $rt = get_magic_quotes_runtime();
        set_magic_quotes_runtime(0);
        clearstatcache();
        $this->_closePackageFile($fp);
        $data = file_get_contents($this->_packageFileName($package, $channel));
        set_magic_quotes_runtime($rt);
        $data = unserialize($data);
        if ($key === null) {
            return $data;
        }
        // compatibility for package.xml version 2.0
        if (isset($data['old'][$key])) {
            return $data['old'][$key];
        }
        if (isset($data[$key])) {
            return $data[$key];
        }
        return null;
    }

    // }}}
    // {{{ _channelInfo()

    /**
     * @param string Channel name
     * @param bool whether to strictly retrieve info of channels, not just aliases
     * @return array|null
     */
    function _channelInfo($channel, $noaliases = false)
    {
        if (!$this->_channelExists($channel, $noaliases)) {
            return null;
        }
        $fp = $this->_openChannelFile($channel, 'r');
        if ($fp === null) {
            return null;
        }
        $rt = get_magic_quotes_runtime();
        set_magic_quotes_runtime(0);
        clearstatcache();
        $this->_closeChannelFile($fp);
        $data = file_get_contents($this->_channelFileName($channel));
        set_magic_quotes_runtime($rt);
        $data = unserialize($data);
        return $data;
    }

    // }}}
    // {{{ _listChannels()

    function _listChannels()
    {
        $channellist = array();
        if (!file_exists($this->channelsdir) || !is_dir($this->channelsdir)) {
            return array('pear.php.net', 'pecl.php.net', '__uri');
        }
        $dp = opendir($this->channelsdir);
        while ($ent = readdir($dp)) {
            if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
                continue;
            }
            if ($ent == '__uri.reg') {
                $channellist[] = '__uri';
                continue;
            }
            $channellist[] = str_replace('_', '/', substr($ent, 0, -4));
        }
        closedir($dp);
        if (!in_array('pear.php.net', $channellist)) {
            $channellist[] = 'pear.php.net';
        }
        if (!in_array('pecl.php.net', $channellist)) {
            $channellist[] = 'pecl.php.net';
        }
        if (!in_array('__uri', $channellist)) {
            $channellist[] = '__uri';
        } 
   
        natsort($channellist);
        return $channellist;
    }

    // }}}
    // {{{ _listPackages()

    function _listPackages($channel = false)
    {
        if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
            return $this->_listChannelPackages($channel);
        }
        if (!file_exists($this->statedir) || !is_dir($this->statedir)) {
            return array();
        }
        $pkglist = array();
        $dp = opendir($this->statedir);
        if (!$dp) {
            return $pkglist;
        }
        while ($ent = readdir($dp)) {
            if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
                continue;
            }
            $pkglist[] = substr($ent, 0, -4);
        }
        closedir($dp);
        return $pkglist;
    }

    // }}}
    // {{{ _listChannelPackages()

    function _listChannelPackages($channel)
    {
        $pkglist = array();
        if (!file_exists($this->_channelDirectoryName($channel)) ||
              !is_dir($this->_channelDirectoryName($channel))) {
            return array();
        }
        $dp = opendir($this->_channelDirectoryName($channel));
        if (!$dp) {
            return $pkglist;
        }
        while ($ent = readdir($dp)) {
            if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
                continue;
            }
            $pkglist[] = substr($ent, 0, -4);
        }
        closedir($dp);
        return $pkglist;
    }

    // }}}
    
    function _listAllPackages()
    {
        $ret = array();
        foreach ($this->_listChannels() as $channel) {
            $ret[$channel] = $this->_listPackages($channel);
        }
        return $ret;
    }

    /**
     * Add an installed package to the registry
     * @param string package name
     * @param array package info (parsed by PEAR_Common::infoFrom*() methods)
     * @return bool success of saving
     * @access private
     */
    function _addPackage($package, $info)
    {
        if ($this->_packageExists($package)) {
            return false;
        }
        $fp = $this->_openPackageFile($package, 'wb');
        if ($fp === null) {
            return false;
        }
        $info['_lastmodified'] = time();
        fwrite($fp, serialize($info));
        $this->_closePackageFile($fp);
        if (isset($info['filelist'])) {
            $this->_rebuildFileMap();
        }
        return true;
    }

    /**
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @return bool
     * @access private
     */
    function _addPackage2($info)
    {
        if (!is_a($info, 'PEAR_PackageFile_v1') && !is_a($info, 'PEAR_PackageFile_v2')) {
            return false;
        }

        if (!$info->validate()) {
            if (class_exists('PEAR_Common')) {
                $ui = PEAR_Frontend::singleton();
                if ($ui) {
                    foreach ($info->getValidationWarnings() as $err) {
                        $ui->log($err['message'], true);
                    }
                }
            }
            return false;
        }
        $channel = $info->getChannel();
        $package = $info->getPackage();
        $save = $info;
        if ($this->_packageExists($package, $channel)) {
            return false;
        }
        if (!$this->_channelExists($channel, true)) {
            return false;
        }
        $info = $info->toArray(true);
        if (!$info) {
            return false;
        }
        $fp = $this->_openPackageFile($package, 'wb', $channel);
        if ($fp === null) {
            return false;
        }
        $info['_lastmodified'] = time();
        fwrite($fp, serialize($info));
        $this->_closePackageFile($fp);
        $this->_rebuildFileMap();
        return true;
    }

    /**
     * @param string Package name
     * @param array parsed package.xml 1.0
     * @param bool this parameter is only here for BC.  Don't use it.
     * @access private
     */
    function _updatePackage($package, $info, $merge = true)
    {
        $oldinfo = $this->_packageInfo($package);
        if (empty($oldinfo)) {
            return false;
        }
        $fp = $this->_openPackageFile($package, 'w');
        if ($fp === null) {
            return false;
        }
        if (is_object($info)) {
            $info = $info->toArray();
        }
        $info['_lastmodified'] = time();
        $newinfo = $info;
        if ($merge) {
            $info = array_merge($oldinfo, $info);
        } else {
            $diff = $info;
        }
        fwrite($fp, serialize($info));
        $this->_closePackageFile($fp);
        if (isset($newinfo['filelist'])) {
            $this->_rebuildFileMap();
        }
        return true;
    }

    /**
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @return bool
     * @access private
     */
    function _updatePackage2($info)
    {
        if (!$this->_packageExists($info->getPackage(), $info->getChannel())) {
            return false;
        }
        $fp = $this->_openPackageFile($info->getPackage(), 'w', $info->getChannel());
        if ($fp === null) {
            return false;
        }
        $save = $info;
        $info = $save->getArray(true);
        $info['_lastmodified'] = time();
        fwrite($fp, serialize($info));
        $this->_closePackageFile($fp);
        $this->_rebuildFileMap();
        return true;
    }

    /**
     * @param string Package name
     * @param string Channel name
     * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null
     * @access private
     */
    function &_getPackage($package, $channel = 'pear.php.net')
    {
        $info = $this->_packageInfo($package, null, $channel);
        if ($info === null) {
            return $info;
        }
        $a = $this->_config;
        if (!$a) {
            $this->_config = &new PEAR_Config;
            $this->_config->set('php_dir', $this->statedir);
        }
        if (!class_exists('PEAR_PackageFile')) {
            require_once 'PEAR/PackageFile.php';
        }
        $pkg = &new PEAR_PackageFile($this->_config);
        $pf = &$pkg->fromArray($info);
        return $pf;
    }

    /**
     * @param string channel name
     * @param bool whether to strictly retrieve channel names
     * @return PEAR_ChannelFile|PEAR_Error
     * @access private
     */
    function &_getChannel($channel, $noaliases = false)
    {
        $ch = false;
        if ($this->_channelExists($channel, $noaliases)) {
            $chinfo = $this->_channelInfo($channel, $noaliases);
            if ($chinfo) {
                if (!class_exists('PEAR_ChannelFile')) {
                    require_once 'PEAR/ChannelFile.php';
                }
                $ch = &PEAR_ChannelFile::fromArrayWithErrors($chinfo);
            }
        }
        if ($ch) {
            if ($ch->validate()) {
                return $ch;
            }
            foreach ($ch->getErrors(true) as $err) {
                $message = $err['message'] . "\n";
            }
            $ch = PEAR::raiseError($message);
            return $ch;
        }
        if ($this->_getChannelFromAlias($channel) == 'pear.php.net') {
            // the registry is not properly set up, so use defaults
            if (!class_exists('PEAR_ChannelFile')) {
                require_once 'PEAR/ChannelFile.php';
            }
            $pear_channel = new PEAR_ChannelFile;
            $pear_channel->setName('pear.php.net');
            $pear_channel->setAlias('pear');
            $pear_channel->setSummary('PHP Extension and Application Repository');
            $pear_channel->setDefaultPEARProtocols();
            $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/');
            $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/');
            return $pear_channel;
        }
        if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') {
            // the registry is not properly set up, so use defaults
            if (!class_exists('PEAR_ChannelFile')) {
                require_once 'PEAR/ChannelFile.php';
            }
            $pear_channel = new PEAR_ChannelFile;
            $pear_channel->setName('pecl.php.net');
            $pear_channel->setAlias('pecl');
            $pear_channel->setSummary('PHP Extension Community Library');
            $pear_channel->setDefaultPEARProtocols();
            $pear_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/');
            $pear_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/');
            $pear_channel->setValidationPackage('PEAR_Validator_PECL', '1.0');
            return $pear_channel;
        }
        if ($this->_getChannelFromAlias($channel) == '__uri') {
            // the registry is not properly set up, so use defaults
            if (!class_exists('PEAR_ChannelFile')) {
                require_once 'PEAR/ChannelFile.php';
            }
            $private = new PEAR_ChannelFile;
            $private->setName('__uri');
            $private->addFunction('xmlrpc', '1.0', '****');
            $private->setSummary('Pseudo-channel for static packages');
            return $private;
        }
        return $ch;
    }

    // {{{ packageExists()

    /**
     * @param string Package name
     * @param string Channel name
     * @return bool
     */
    function packageExists($package, $channel = 'pear.php.net')
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $ret = $this->_packageExists($package, $channel);
        $this->_unlock();
        return $ret;
    }

    // }}}

    // {{{ channelExists()

    /**
     * @param string channel name
     * @param bool if true, then aliases will be ignored
     * @return bool
     */
    function channelExists($channel, $noaliases = false)
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $ret = $this->_channelExists($channel, $noaliases);
        $this->_unlock();
        return $ret;
    }

    // }}}

    // {{{ isAlias()

    /**
     * Determines whether the parameter is an alias of a channel
     * @param string
     * @return bool
     */
    function isAlias($alias)
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $ret = $this->_isChannelAlias($alias);
        $this->_unlock();
        return $ret;
    }

    // }}}
    // {{{ packageInfo()

    /**
     * @param string|null
     * @param string|null
     * @param string
     * @return array|null
     */
    function packageInfo($package = null, $key = null, $channel = 'pear.php.net')
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $ret = $this->_packageInfo($package, $key, $channel);
        $this->_unlock();
        return $ret;
    }

    // }}}
    // {{{ channelInfo()

    /**
     * Retrieve a raw array of channel data.
     *
     * Do not use this, instead use {@link getChannel()} for normal
     * operations.  Array structure is undefined in this method
     * @param string channel name
     * @param bool whether to strictly retrieve information only on non-aliases
     * @return array|null|PEAR_Error
     */
    function channelInfo($channel = null, $noaliases = false)
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $ret = $this->_channelInfo($channel, $noaliases);
        $this->_unlock();
        return $ret;
    }

    // }}}

    /**
     * @param string
     */
    function channelName($channel)
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $ret = $this->_getChannelFromAlias($channel);
        $this->_unlock();
        return $ret;
    }

    /**
     * @param string
     */
    function channelAlias($channel)
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $ret = $this->_getAlias($channel);
        $this->_unlock();
        return $ret;
    }
    // {{{ listPackages()

    function listPackages($channel = false)
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $ret = $this->_listPackages($channel);
        $this->_unlock();
        return $ret;
    }

    // }}}
    // {{{ listAllPackages()

    function listAllPackages()
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $ret = $this->_listAllPackages();
        $this->_unlock();
        return $ret;
    }

    // }}}
    // {{{ listChannel()

    function listChannels()
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $ret = $this->_listChannels();
        $this->_unlock();
        return $ret;
    }

    // }}}
    // {{{ addPackage()

    /**
     * Add an installed package to the registry
     * @param string|PEAR_PackageFile_v1|PEAR_PackageFile_v2 package name or object
     *               that will be passed to {@link addPackage2()}
     * @param array package info (parsed by PEAR_Common::infoFrom*() methods)
     * @return bool success of saving
     */
    function addPackage($package, $info)
    {
        if (is_object($info)) {
            return $this->addPackage2($info);
        }
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
            return $e;
        }
        $ret = $this->_addPackage($package, $info);
        $this->_unlock();
        if ($ret) {
            if (!class_exists('PEAR_PackageFile_v1')) {
                require_once 'PEAR/PackageFile/v1.php';
            }
            $pf = new PEAR_PackageFile_v1;
            $pf->setConfig($this->_config);
            $pf->fromArray($info);
            $this->_dependencyDB->uninstallPackage($pf);
            $this->_dependencyDB->installPackage($pf);
        }
        return $ret;
    }

    // }}}
    // {{{ addPackage2()

    function addPackage2($info)
    {
        if (!is_object($info)) {
            return $this->addPackage($info['package'], $info);
        }
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
            return $e;
        }
        $ret = $this->_addPackage2($info);
        $this->_unlock();
        if ($ret) {
            $this->_dependencyDB->uninstallPackage($info);
            $this->_dependencyDB->installPackage($info);
        }
        return $ret;
    }

    // }}}
    // {{{ updateChannel()

    /**
     * For future expandibility purposes, separate this
     * @param PEAR_ChannelFile
     */
    function updateChannel($channel, $lastmodified = null)
    {
        if ($channel->getName() == '__uri') {
            return false;
        }
        return $this->addChannel($channel, $lastmodified, true);
    }

    // }}}
    // {{{ deleteChannel()

    /**
     * Deletion fails if there are any packages installed from the channel
     * @param string|PEAR_ChannelFile channel name
     * @return boolean|PEAR_Error True on deletion, false if it doesn't exist
     */
    function deleteChannel($channel)
    {
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
            return $e;
        }
        $ret = $this->_deleteChannel($channel);
        $this->_unlock();
        if ($ret && is_a($this->_config, 'PEAR_Config')) {
            $this->_config->setChannels($this->listChannels());
        }
        return $ret;
    }

    // }}}
    // {{{ addChannel()

    /**
     * @param PEAR_ChannelFile Channel object
     * @param string Last-Modified header from HTTP for caching
     * @return boolean|PEAR_Error True on creation, false if it already exists
     */
    function addChannel($channel, $lastmodified = false, $update = false)
    {
        if (!is_a($channel, 'PEAR_ChannelFile')) {
            return false;
        }
        if (!$channel->validate()) {
            return false;
        }
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
            return $e;
        }
        $ret = $this->_addChannel($channel, $update, $lastmodified);
        $this->_unlock();
        if (!$update && $ret && is_a($this->_config, 'PEAR_Config')) {
            $this->_config->setChannels($this->listChannels());
        }
        return $ret;
    }

    // }}}
    // {{{ deletePackage()

    function deletePackage($package, $channel = 'pear.php.net')
    {
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
            return $e;
        }
        $file = $this->_packageFileName($package, $channel);
        if (file_exists($file)) {
            $ret = @unlink($file);
        } else {
            $ret = false;
        }
        $this->_rebuildFileMap();
        $this->_unlock();
        $p = array('channel' => $channel, 'package' => $package);
        $this->_dependencyDB->uninstallPackage($p);
        return $ret;
    }

    // }}}
    // {{{ updatePackage()

    function updatePackage($package, $info, $merge = true)
    {
        if (is_object($info)) {
            return $this->updatePackage2($info, $merge);
        }
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
            return $e;
        }
        $ret = $this->_updatePackage($package, $info, $merge);
        $this->_unlock();
        if ($ret) {
            if (!class_exists('PEAR_PackageFile_v1')) {
                require_once 'PEAR/PackageFile/v1.php';
            }
            $pf = new PEAR_PackageFile_v1;
            $pf->setConfig($this->_config);
            $pf->fromArray($this->packageInfo($package));
            $this->_dependencyDB->uninstallPackage($pf);
            $this->_dependencyDB->installPackage($pf);
        }
        return $ret;
    }

    // }}}
    // {{{ updatePackage2()

    function updatePackage2($info)
    {
        if (!is_object($info)) {
            return $this->updatePackage($info['package'], $info, $merge);
        }
        if (!$info->validate(PEAR_VALIDATE_DOWNLOADING)) {
            return false;
        }
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
            return $e;
        }
        $ret = $this->_updatePackage2($info);
        $this->_unlock();
        if ($ret) {
            $this->_dependencyDB->uninstallPackage($info);
            $this->_dependencyDB->installPackage($info);
        }
        return $ret;
    }

    // }}}
    // {{{ getChannel()
    /**
     * @param string channel name
     * @param bool whether to strictly return raw channels (no aliases)
     * @return PEAR_ChannelFile|PEAR_Error
     */
    function &getChannel($channel, $noaliases = false)
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $ret = &$this->_getChannel($channel, $noaliases);
        $this->_unlock();
        if (!$ret) {
            return PEAR::raiseError('Unknown channel: ' . $channel);
        }
        return $ret;
    }

    // }}}
    // {{{ getPackage()
    /**
     * @param string package name
     * @param string channel name
     * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null
     */
    function &getPackage($package, $channel = 'pear.php.net')
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $pf = &$this->_getPackage($package, $channel);
        $this->_unlock();
        return $pf;
    }

    // }}}

    /**
     * Get PEAR_PackageFile_v[1/2] objects representing the contents of
     * a dependency group that are installed.
     *
     * This is used at uninstall-time
     * @param array
     * @return array|false
     */
    function getInstalledGroup($group)
    {
        $ret = array();
        if (isset($group['package'])) {
            if (!isset($group['package'][0])) {
                $group['package'] = array($group['package']);
            }
            foreach ($group['package'] as $package) {
                $depchannel = isset($package['channel']) ? $package['channel'] : '__uri';
                $p = &$this->getPackage($package['name'], $depchannel);
                if ($p) {
                    $save = &$p;
                    $ret[] = &$save;
                }
            }
        }
        if (isset($group['subpackage'])) {
            if (!isset($group['subpackage'][0])) {
                $group['subpackage'] = array($group['subpackage']);
            }
            foreach ($group['subpackage'] as $package) {
                $depchannel = isset($package['channel']) ? $package['channel'] : '__uri';
                $p = &$this->getPackage($package['name'], $depchannel);
                if ($p) {
                    $save = &$p;
                    $ret[] = &$save;
                }
            }
        }
        if (!count($ret)) {
            return false;
        }
        return $ret;
    }

    // {{{ getChannelValidator()
    /**
     * @param string channel name
     * @return PEAR_Validate|false
     */
    function &getChannelValidator($channel)
    {
        $chan = $this->getChannel($channel);
        if (PEAR::isError($chan)) {
            return $chan;
        }
        $val = $chan->getValidationObject();
        return $val;
    }
    // }}}
    // {{{ getChannels()
    /**
     * @param string channel name
     * @return array an array of PEAR_ChannelFile objects representing every installed channel
     */
    function &getChannels()
    {
        $ret = array();
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        foreach ($this->_listChannels() as $channel) {
            $e = &$this->_getChannel($channel);
            if (!$e || PEAR::isError($e)) {
                continue;
            }
            $ret[] = $e;
        }
        $this->_unlock();
        return $ret;
    }

    // }}}
    // {{{ checkFileMap()

    /**
     * Test whether a file or set of files belongs to a package.
     *
     * If an array is passed in
     * @param string|array file path, absolute or relative to the pear
     *                     install dir
     * @param string|array name of PEAR package or array('package' => name, 'channel' =>
     *                     channel) of a package that will be ignored
     * @param string API version - 1.1 will exclude any files belonging to a package
     * @param array private recursion variable
     * @return array|false which package and channel the file belongs to, or an empty
     *                     string if the file does not belong to an installed package,
     *                     or belongs to the second parameter's package
     */
    function checkFileMap($path, $package = false, $api = '1.0', $attrs = false)
    {
        if (is_array($path)) {
            static $notempty;
            if (empty($notempty)) {
                if (!class_exists('PEAR_Installer_Role')) {
                    require_once 'PEAR/Installer/Role.php';
                }
                $notempty = create_function('$a','return !empty($a);');
            }
            $package = is_array($package) ? array(strtolower($package[0]), strtolower($package[1]))
                : strtolower($package);
            $pkgs = array();
            foreach ($path as $name => $attrs) {
                if (is_array($attrs)) {
                    if (isset($attrs['install-as'])) {
                        $name = $attrs['install-as'];
                    }
                    if (!in_array($attrs['role'], PEAR_Installer_Role::getInstallableRoles())) {
                        // these are not installed
                        continue;
                    }
                    if (!in_array($attrs['role'], PEAR_Installer_Role::getBaseinstallRoles())) {
                        $attrs['baseinstalldir'] = is_array($package) ? $package[1] : $package;
                    }
                    if (isset($attrs['baseinstalldir'])) {
                        $name = $attrs['baseinstalldir'] . DIRECTORY_SEPARATOR . $name;
                    }
                }
                $pkgs[$name] = $this->checkFileMap($name, $package, $api, $attrs);
                if (PEAR::isError($pkgs[$name])) {
                    return $pkgs[$name];
                }
            }
            return array_filter($pkgs, $notempty);
        }
        if (empty($this->filemap_cache)) {
            if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
                return $e;
            }
            $err = $this->_readFileMap();
            $this->_unlock();
            if (PEAR::isError($err)) {
                return $err;
            }
        }
        if (!$attrs) {
            $attrs = array('role' => 'php'); // any old call would be for PHP role only
        }
        if (isset($this->filemap_cache[$attrs['role']][$path])) {
            if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) {
                return false;
            }
            return $this->filemap_cache[$attrs['role']][$path];
        }
        $l = strlen($this->install_dir);
        if (substr($path, 0, $l) == $this->install_dir) {
            $path = preg_replace('!^'.DIRECTORY_SEPARATOR.'+!', '', substr($path, $l));
        }
        if (isset($this->filemap_cache[$attrs['role']][$path])) {
            if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) {
                return false;
            }
            return $this->filemap_cache[$attrs['role']][$path];
        }
        return false;
    }

    // }}}
    // {{{ flush()
    /**
     * Force a reload of the filemap
     * @since 1.5.0RC3
     */
    function flushFileMap()
    {
        $this->filemap_cache = null;
        clearstatcache(); // ensure that the next read gets the full, current filemap
    }

    // }}}
    // {{{ apiVersion()
    /**
     * Get the expected API version.  Channels API is version 1.1, as it is backwards
     * compatible with 1.0
     * @return string
     */
    function apiVersion()
    {
        return '1.1';
    }
    // }}}


    /**
     * Parse a package name, or validate a parsed package name array
     * @param string|array pass in an array of format
     *                     array(
     *                      'package' => 'pname',
     *                     ['channel' => 'channame',]
     *                     ['version' => 'version',]
     *                     ['state' => 'state',]
     *                     ['group' => 'groupname'])
     *                     or a string of format
     *                     [channel://][channame/]pname[-version|-state][/group=groupname]
     * @return array|PEAR_Error
     */
    function parsePackageName($param, $defaultchannel = 'pear.php.net')
    {
        $saveparam = $param;
        if (is_array($param)) {
            // convert to string for error messages
            $saveparam = $this->parsedPackageNameToString($param);
            // process the array
            if (!isset($param['package'])) {
                return PEAR::raiseError('parsePackageName(): array $param ' .
                    'must contain a valid package name in index "param"',
                    'package', null, null, $param);
            }
            if (!isset($param['uri'])) {
                if (!isset($param['channel'])) {
                    $param['channel'] = $defaultchannel;
                }
            } else {
                $param['channel'] = '__uri';
            }
        } else {
            $components = @parse_url((string) $param);
            if (isset($components['scheme'])) {
                if ($components['scheme'] == 'http') {
                    // uri package
                    $param = array('uri' => $param, 'channel' => '__uri');
                } elseif($components['scheme'] != 'channel') {
                    return PEAR::raiseError('parsePackageName(): only channel:// uris may ' .
                        'be downloaded, not "' . $param . '"', 'invalid', null, null, $param);
                }
            }
            if (!isset($components['path'])) {
                return PEAR::raiseError('parsePackageName(): array $param ' .
                    'must contain a valid package name in "' . $param . '"',
                    'package', null, null, $param);
            }
            if (isset($components['host'])) {
                // remove the leading "/"
                $components['path'] = substr($components['path'], 1);
            }
            if (!isset($components['scheme'])) {
                if (strpos($components['path'], '/') !== false) {
                    if ($components['path']{0} == '/') {
                        return PEAR::raiseError('parsePackageName(): this is not ' .
                            'a package name, it begins with "/" in "' . $param . '"',
                            'invalid', null, null, $param);
                    }
                    $parts = explode('/', $components['path']);
                    $components['host'] = array_shift($parts);
                    if (count($parts) > 1) {
                        $components['path'] = array_pop($parts);
                        $components['host'] .= '/' . implode('/', $parts);
                    } else {
                        $components['path'] = implode('/', $parts);
                    }
                } else {
                    $components['host'] = $defaultchannel;
                }
            } else {
                if (strpos($components['path'], '/')) {
                    $parts = explode('/', $components['path']);
                    $components['path'] = array_pop($parts);
                    $components['host'] .= '/' . implode('/', $parts);
                }
            }

            if (is_array($param)) {
                $param['package'] = $components['path'];
            } else {
                $param = array(
                    'package' => $components['path']
                    );
                if (isset($components['host'])) {
                    $param['channel'] = $components['host'];
                }
            }
            if (isset($components['fragment'])) {
                $param['group'] = $components['fragment'];
            }
            if (isset($components['user'])) {
                $param['user'] = $components['user'];
            }
            if (isset($components['pass'])) {
                $param['pass'] = $components['pass'];
            }
            if (isset($components['query'])) {
                parse_str($components['query'], $param['opts']);
            }
            // check for extension
            $pathinfo = pathinfo($param['package']);
            if (isset($pathinfo['extension']) &&
                  in_array(strtolower($pathinfo['extension']), array('tgz', 'tar'))) {
                $param['extension'] = $pathinfo['extension'];
                $param['package'] = substr($pathinfo['basename'], 0,
                    strlen($pathinfo['basename']) - 4);
            }
            // check for version
            if (strpos($param['package'], '-')) {
                $test = explode('-', $param['package']);
                if (count($test) != 2) {
                    return PEAR::raiseError('parsePackageName(): only one version/state ' .
                        'delimiter "-" is allowed in "' . $saveparam . '"',
                        'version', null, null, $param);
                }
                list($param['package'], $param['version']) = $test;
            }
        }
        // validation
        $info = $this->channelExists($param['channel']);
        if (PEAR::isError($info)) {
            return $info;
        }
        if (!$info) {
            return PEAR::raiseError('unknown channel "' . $param['channel'] .
                '" in "' . $saveparam . '"', 'channel', null, null, $param);
        }
        $chan = $this->getChannel($param['channel']);
        if (PEAR::isError($chan)) {
            return $chan;
        }
        if (!$chan) {
            return PEAR::raiseError("Exception: corrupt registry, could not " .
                "retrieve channel " . $param['channel'] . " information",
                'registry', null, null, $param);
        }
        $param['channel'] = $chan->getName();
        $validate = $chan->getValidationObject();
        $vpackage = $chan->getValidationPackage();
        // validate package name
        if (!$validate->validPackageName($param['package'], $vpackage['_content'])) {
            return PEAR::raiseError('parsePackageName(): invalid package name "' .
                $param['package'] . '" in "' . $saveparam . '"',
                'package', null, null, $param);
        }
        if (isset($param['group'])) {
            if (!PEAR_Validate::validGroupName($param['group'])) {
                return PEAR::raiseError('parsePackageName(): dependency group "' . $param['group'] .
                    '" is not a valid group name in "' . $saveparam . '"', 'group', null, null,
                    $param);
            }
        }
        if (isset($param['state'])) {
            if (!in_array(strtolower($param['state']), $validate->getValidStates())) {
                return PEAR::raiseError('parsePackageName(): state "' . $param['state']
                    . '" is not a valid state in "' . $saveparam . '"',
                    'state', null, null, $param);
            }
        }
        if (isset($param['version'])) {
            if (isset($param['state'])) {
                return PEAR::raiseError('parsePackageName(): cannot contain both ' .
                    'a version and a stability (state) in "' . $saveparam . '"',
                    'version/state', null, null, $param);
            }
            // check whether version is actually a state
            if (in_array(strtolower($param['version']), $validate->getValidStates())) {
                $param['state'] = strtolower($param['version']);
                unset($param['version']);
            } else {
                if (!$validate->validVersion($param['version'])) {
                    return PEAR::raiseError('parsePackageName(): "' . $param['version'] .
                        '" is neither a valid version nor a valid state in "' .
                        $saveparam . '"', 'version/state', null, null, $param);
                }                    
            }
        }
        return $param;
    }

    /**
     * @param array
     * @return string
     */
    function parsedPackageNameToString($parsed, $brief = false)
    {
        if (is_string($parsed)) {
            return $parsed;
        }
        if (is_object($parsed)) {
            $p = $parsed;
            $parsed = array(
                'package' => $p->getPackage(),
                'channel' => $p->getChannel(),
                'version' => $p->getVersion(),
            );
        }
        if (isset($parsed['uri'])) {
            return $parsed['uri'];
        }
        if ($brief) {
            if ($channel = $this->channelAlias($parsed['channel'])) {
                return $channel . '/' . $parsed['package'];
            }
        }
        $upass = '';
        if (isset($parsed['user'])) {
            $upass = $parsed['user'];
            if (isset($parsed['pass'])) {
                $upass .= ':' . $parsed['pass'];
            }
            $upass = "$upass@";
        }
        $ret = 'channel://' . $upass . $parsed['channel'] . '/' . $parsed['package'];
        if (isset($parsed['version']) || isset($parsed['state'])) {
            $ver = isset($parsed['version']) ? $parsed['version'] : '';
            $ver .= isset($parsed['state']) ? $parsed['state'] : '';
            $ret .= '-' . $ver;
        }
        if (isset($parsed['extension'])) {
            $ret .= '.' . $parsed['extension'];
        }
        if (isset($parsed['opts'])) {
            $ret .= '?';
            foreach ($parsed['opts'] as $name => $value) {
                $parsed['opts'][$name] = "$name=$value";
            }
            $ret .= implode('&', $parsed['opts']);
        }
        if (isset($parsed['group'])) {
            $ret .= '#' . $parsed['group'];
        }
        return $ret;
    }
}

?>
PEAR-1.7.2/PEAR/Remote.php100644   1750   1750       43506 11014312742  10210 <?php
/**
 * PEAR_Remote
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Remote.php,v 1.80 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * needed for PEAR_Error
 */
require_once 'PEAR.php';
require_once 'PEAR/Config.php';

/**
 * This is a class for doing remote operations against the central
 * PEAR database.
 *
 * @nodep XML_RPC_Value
 * @nodep XML_RPC_Message
 * @nodep XML_RPC_Client
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */
class PEAR_Remote extends PEAR
{
    // {{{ properties

    var $config = null;
    var $cache  = null;
    /**
     * @var PEAR_Registry
     * @access private
     */
    var $_registry;

    // }}}

    // {{{ PEAR_Remote(config_object)

    function PEAR_Remote(&$config)
    {
        $this->PEAR();
        $this->config = &$config;
        $this->_registry = &$this->config->getRegistry();
    }

    // }}}
    // {{{ setRegistry()
    
    function setRegistry(&$reg)
    {
        $this->_registry = &$reg;
    }
    // }}}
    // {{{ getCache()


    function getCache($args)
    {
        $id       = md5(serialize($args));
        $cachedir = $this->config->get('cache_dir');
        $filename = $cachedir . DIRECTORY_SEPARATOR . 'xmlrpc_cache_' . $id;
        if (!file_exists($filename)) {
            return null;
        }

        $fp = fopen($filename, 'rb');
        if (!$fp) {
            return null;
        }
        fclose($fp);
        $content = file_get_contents($filename);
        $result   = array(
            'age'        => time() - filemtime($filename),
            'lastChange' => filemtime($filename),
            'content'    => unserialize($content),
            );
        return $result;
    }

    // }}}

    // {{{ saveCache()

    function saveCache($args, $data)
    {
        $id       = md5(serialize($args));
        $cachedir = $this->config->get('cache_dir');
        if (!file_exists($cachedir)) {
            System::mkdir(array('-p', $cachedir));
        }
        $filename = $cachedir.'/xmlrpc_cache_'.$id;

        $fp = @fopen($filename, "wb");
        if ($fp) {
            fwrite($fp, serialize($data));
            fclose($fp);
        }
    }

    // }}}

    // {{{ clearCache()

    function clearCache($method, $args)
    {
        array_unshift($args, $method);
        array_unshift($args, $this->config->get('default_channel')); // cache by channel
        $id       = md5(serialize($args));
        $cachedir = $this->config->get('cache_dir');
        $filename = $cachedir.'/xmlrpc_cache_'.$id;
        if (file_exists($filename)) {
            @unlink($filename);
        }
    }

    // }}}
    // {{{ call(method, [args...])

    function call($method)
    {
        $_args = $args = func_get_args();

        $server_channel = $this->config->get('default_channel');
        $channel = $this->_registry->getChannel($server_channel);
        if (!PEAR::isError($channel)) {
            $mirror = $this->config->get('preferred_mirror');
            if ($channel->getMirror($mirror)) {
                if ($channel->supports('xmlrpc', $method, $mirror)) {
                    $server_channel = $server_host = $mirror; // use the preferred mirror
                    $server_port = $channel->getPort($mirror);
                } elseif (!$channel->supports('xmlrpc', $method)) {
                    return $this->raiseError("Channel $server_channel does not " .
                        "support xml-rpc method $method");
                }
            }
            if (!isset($server_host)) {
                if (!$channel->supports('xmlrpc', $method)) {
                    return $this->raiseError("Channel $server_channel does not support " .
                        "xml-rpc method $method");
                } else {
                    $server_host = $server_channel;
                    $server_port = $channel->getPort();
                }
            }
        } else {
            return $this->raiseError("Unknown channel '$server_channel'");
        }

        array_unshift($_args, $server_channel); // cache by channel
        $this->cache = $this->getCache($_args);
        $cachettl = $this->config->get('cache_ttl');
        // If cache is newer than $cachettl seconds, we use the cache!
        if ($this->cache !== null && $this->cache['age'] < $cachettl) {
            return $this->cache['content'];
        }
        $fp = false;
        if (extension_loaded("xmlrpc")) {
            $result = call_user_func_array(array(&$this, 'call_epi'), $args);
            if (!PEAR::isError($result)) {
                $this->saveCache($_args, $result);
            }
            return $result;
        } elseif (!($fp = fopen('XML/RPC.php', 'r', true))) {
            return $this->raiseError("For this remote PEAR operation you need to load the xmlrpc extension or install XML_RPC");
        }
        include_once 'XML/RPC.php';
        if ($fp) {
            fclose($fp);
        }

        array_shift($args);
        $username = $this->config->get('username');
        $password = $this->config->get('password');
        $eargs = array();
        foreach($args as $arg) {
            $eargs[] = $this->_encode($arg);
        }
        $f = new XML_RPC_Message($method, $eargs);
        if ($this->cache !== null) {
            $maxAge = '?maxAge='.$this->cache['lastChange'];
        } else {
            $maxAge = '';
        }
        $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
        if ($proxy = parse_url($this->config->get('http_proxy'))) {
            $proxy_host = isset($proxy['host']) ? $proxy['host'] : null;
            if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') {
                $proxy_host = 'https://' . $proxy_host;
            }
            $proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080;
            $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null;
            $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null;
        }
        $shost = $server_host;
        if ($channel->getSSL()) {
            $shost = "https://$shost";
        }
        $c = new XML_RPC_Client('/' . $channel->getPath('xmlrpc')
            . $maxAge, $shost, $server_port, $proxy_host, $proxy_port,
            $proxy_user, $proxy_pass);
        if ($username && $password) {
            $c->setCredentials($username, $password);
        }
        if ($this->config->get('verbose') >= 3) {
            $c->setDebug(1);
        }
        $r = $c->send($f);
        if (!$r) {
            return $this->raiseError("XML_RPC send failed");
        }
        $v = $r->value();
        if ($e = $r->faultCode()) {
            if ($e == $GLOBALS['XML_RPC_err']['http_error'] && strstr($r->faultString(), '304 Not Modified') !== false) {
                return $this->cache['content'];
            }
            return $this->raiseError($r->faultString(), $e);
        }

        $result = XML_RPC_decode($v);
        $this->saveCache($_args, $result);
        return $result;
    }

    // }}}

    // {{{ call_epi(method, [args...])

    function call_epi($method)
    {
        if (!extension_loaded("xmlrpc")) {
            return $this->raiseError("xmlrpc extension is not loaded");
        }
        $server_channel = $this->config->get('default_channel');
        $channel = $this->_registry->getChannel($server_channel);
        if (!PEAR::isError($channel)) {
            $mirror = $this->config->get('preferred_mirror');
            if ($channel->getMirror($mirror)) {
                if ($channel->supports('xmlrpc', $method, $mirror)) {
                    $server_channel = $server_host = $mirror; // use the preferred mirror
                    $server_port = $channel->getPort($mirror);
                } elseif (!$channel->supports('xmlrpc', $method)) {
                    return $this->raiseError("Channel $server_channel does not " .
                        "support xml-rpc method $method");
                }
            }
            if (!isset($server_host)) {
                if (!$channel->supports('xmlrpc', $method)) {
                    return $this->raiseError("Channel $server_channel does not support " .
                        "xml-rpc method $method");
                } else {
                    $server_host = $server_channel;
                    $server_port = $channel->getPort();
                }
            }
        } else {
            return $this->raiseError("Unknown channel '$server_channel'");
        }
        $params = func_get_args();
        array_shift($params);
        $method = str_replace("_", ".", $method);
        $request = xmlrpc_encode_request($method, $params);
        if ($http_proxy = $this->config->get('http_proxy')) {
            $proxy = parse_url($http_proxy);
            $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
            $proxy_host = isset($proxy['host']) ? $proxy['host'] : null;
            if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') {
                $proxy_host = 'https://' . $proxy_host;
            }
            $proxy_port = isset($proxy['port']) ? $proxy['port'] : null;
            $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null;
            $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null;
            $fp = @fsockopen($proxy_host, $proxy_port);
            $use_proxy = true;
            if ($channel->getSSL()) {
                $server_host = "https://$server_host";
            }
        } else {
            $use_proxy = false;
            $ssl = $channel->getSSL();
            $fp = @fsockopen(($ssl ? 'ssl://' : '') . $server_host, $server_port);
            if (!$fp) {
                $server_host = "$ssl$server_host"; // for error-reporting
            }
        }
        if (!$fp && $http_proxy) {
            return $this->raiseError("PEAR_Remote::call: fsockopen(`$proxy_host', $proxy_port) failed");
        } elseif (!$fp) {
            return $this->raiseError("PEAR_Remote::call: fsockopen(`$server_host', $server_port) failed");
        }
        $len = strlen($request);
        $req_headers = "Host: $server_host:$server_port\r\n" .
             "Content-type: text/xml\r\n" .
             "Content-length: $len\r\n";
        $username = $this->config->get('username');
        $password = $this->config->get('password');
        if ($username && $password) {
            $req_headers .= "Cookie: PEAR_USER=$username; PEAR_PW=$password\r\n";
            $tmp = base64_encode("$username:$password");
            $req_headers .= "Authorization: Basic $tmp\r\n";
        }
        if ($this->cache !== null) {
            $maxAge = '?maxAge='.$this->cache['lastChange'];
        } else {
            $maxAge = '';
        }

        if ($use_proxy && $proxy_host != '' && $proxy_user != '') {
            $req_headers .= 'Proxy-Authorization: Basic '
                .base64_encode($proxy_user.':'.$proxy_pass)
                ."\r\n";
        }

        if ($this->config->get('verbose') > 3) {
            print "XMLRPC REQUEST HEADERS:\n";
            var_dump($req_headers);
            print "XMLRPC REQUEST BODY:\n";
            var_dump($request);
        }

        if ($use_proxy && $proxy_host != '') {
            $post_string = "POST http://".$server_host;
            if ($proxy_port > '') {
                $post_string .= ':'.$server_port;
            }
        } else {
            $post_string = "POST ";
        }

        $path = '/' . $channel->getPath('xmlrpc');
        fwrite($fp, ($post_string . $path . "$maxAge HTTP/1.0\r\n$req_headers\r\n$request"));
        $response = '';
        $line1 = fgets($fp, 2048);
        if (!preg_match('!^HTTP/[0-9\.]+ (\d+) (.*)!', $line1, $matches)) {
            return $this->raiseError("PEAR_Remote: invalid HTTP response from XML-RPC server");
        }
        switch ($matches[1]) {
            case "200": // OK
                break;
            case "304": // Not Modified
                return $this->cache['content'];
            case "401": // Unauthorized
                if ($username && $password) {
                    return $this->raiseError("PEAR_Remote ($server_host:$server_port) " .
                        ": authorization failed", 401);
                } else {
                    return $this->raiseError("PEAR_Remote ($server_host:$server_port) " .
                        ": authorization required, please log in first", 401);
                }
            default:
                return $this->raiseError("PEAR_Remote ($server_host:$server_port) : " .
                    "unexpected HTTP response", (int)$matches[1], null, null,
                    "$matches[1] $matches[2]");
        }
        while (trim(fgets($fp, 2048)) != ''); // skip rest of headers
        while ($chunk = fread($fp, 10240)) {
            $response .= $chunk;
        }
        fclose($fp);
        if ($this->config->get('verbose') > 3) {
            print "XMLRPC RESPONSE:\n";
            var_dump($response);
        }
        $ret = xmlrpc_decode($response);
        if (is_array($ret) && isset($ret['__PEAR_TYPE__'])) {
            if ($ret['__PEAR_TYPE__'] == 'error') {
                if (isset($ret['__PEAR_CLASS__'])) {
                    $class = $ret['__PEAR_CLASS__'];
                } else {
                    $class = "PEAR_Error";
                }
                if ($ret['code']     === '') $ret['code']     = null;
                if ($ret['message']  === '') $ret['message']  = null;
                if ($ret['userinfo'] === '') $ret['userinfo'] = null;
                if (strtolower($class) == 'db_error') {
                    $ret = $this->raiseError(PEAR::errorMessage($ret['code']),
                                             $ret['code'], null, null,
                                             $ret['userinfo']);
                } else {
                    $ret = $this->raiseError($ret['message'], $ret['code'],
                                             null, null, $ret['userinfo']);
                }
            }
        } elseif (is_array($ret) && sizeof($ret) == 1 && isset($ret[0])
                  && is_array($ret[0]) &&
                  !empty($ret[0]['faultString']) &&
                  !empty($ret[0]['faultCode'])) {
            extract($ret[0]);
            $faultString = "XML-RPC Server Fault: " .
                 str_replace("\n", " ", $faultString);
            return $this->raiseError($faultString, $faultCode);
        } elseif (is_array($ret) && sizeof($ret) == 2 && !empty($ret['faultString']) &&
              !empty($ret['faultCode'])) {
            extract($ret);
            $faultString = "XML-RPC Server Fault: " .
                 str_replace("\n", " ", $faultString);
            return $this->raiseError($faultString, $faultCode);
        }
        return $ret;
    }

    // }}}

    // {{{ _encode

    // a slightly extended version of XML_RPC_encode
    function _encode($php_val)
    {
        global $XML_RPC_Boolean, $XML_RPC_Int, $XML_RPC_Double;
        global $XML_RPC_String, $XML_RPC_Array, $XML_RPC_Struct;

        $type = gettype($php_val);
        $xmlrpcval = new XML_RPC_Value;

        switch($type) {
            case "array":
                reset($php_val);
                $firstkey = key($php_val);
                end($php_val);
                $lastkey = key($php_val);
                reset($php_val);
                if ($firstkey === 0 && is_int($lastkey) &&
                    ($lastkey + 1) == count($php_val)) {
                    $is_continuous = true;
                    reset($php_val);
                    $size = count($php_val);
                    for ($expect = 0; $expect < $size; $expect++, next($php_val)) {
                        if (key($php_val) !== $expect) {
                            $is_continuous = false;
                            break;
                        }
                    }
                    if ($is_continuous) {
                        reset($php_val);
                        $arr = array();
                        while (list($k, $v) = each($php_val)) {
                            $arr[$k] = $this->_encode($v);
                        }
                        $xmlrpcval->addArray($arr);
                        break;
                    }
                }
                // fall though if not numerical and continuous
            case "object":
                $arr = array();
                while (list($k, $v) = each($php_val)) {
                    $arr[$k] = $this->_encode($v);
                }
                $xmlrpcval->addStruct($arr);
                break;
            case "integer":
                $xmlrpcval->addScalar($php_val, $XML_RPC_Int);
                break;
            case "double":
                $xmlrpcval->addScalar($php_val, $XML_RPC_Double);
                break;
            case "string":
            case "NULL":
                $xmlrpcval->addScalar($php_val, $XML_RPC_String);
                break;
            case "boolean":
                $xmlrpcval->addScalar($php_val, $XML_RPC_Boolean);
                break;
            case "unknown type":
            default:
                return null;
        }
        return $xmlrpcval;
    }

    // }}}

}

?>
PEAR-1.7.2/PEAR/REST.php100644   1750   1750       36114 11014312742   7527 <?php
/**
 * PEAR_REST
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: REST.php,v 1.31 2008/05/13 18:03:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * For downloading xml files
 */
require_once 'PEAR.php';
require_once 'PEAR/XMLParser.php';

/**
 * Intelligently retrieve data, following hyperlinks if necessary, and re-directing
 * as well
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_REST
{
    var $config;
    var $_options;
    function PEAR_REST(&$config, $options = array())
    {
        $this->config = &$config;
        $this->_options = $options;
    }

    /**
     * Retrieve REST data, but always retrieve the local cache if it is available.
     *
     * This is useful for elements that should never change, such as information on a particular
     * release
     * @param string full URL to this resource
     * @param array|false contents of the accept-encoding header
     * @param boolean     if true, xml will be returned as a string, otherwise, xml will be
     *                    parsed using PEAR_XMLParser
     * @return string|array
     */
    function retrieveCacheFirst($url, $accept = false, $forcestring = false, $channel = false)
    {
        $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
            md5($url) . 'rest.cachefile';
        if (file_exists($cachefile)) {
            return unserialize(implode('', file($cachefile)));
        }
        return $this->retrieveData($url, $accept, $forcestring, $channel);
    }

    /**
     * Retrieve a remote REST resource
     * @param string full URL to this resource
     * @param array|false contents of the accept-encoding header
     * @param boolean     if true, xml will be returned as a string, otherwise, xml will be
     *                    parsed using PEAR_XMLParser
     * @return string|array
     */
    function retrieveData($url, $accept = false, $forcestring = false, $channel = false)
    {
        $cacheId = $this->getCacheId($url);
        if ($ret = $this->useLocalCache($url, $cacheId)) {
            return $ret;
        }
        if (!isset($this->_options['offline'])) {
            $trieddownload = true;
            $file = $this->downloadHttp($url, $cacheId ? $cacheId['lastChange'] : false, $accept, $channel);
        } else {
            $trieddownload = false;
            $file = false;
        }
        if (PEAR::isError($file)) {
            if ($file->getCode() == -9276) {
                $trieddownload = false;
                $file = false; // use local copy if available on socket connect error
            } else {
                return $file;
            }
        }
        if (!$file) {
            $ret = $this->getCache($url);
            if (!PEAR::isError($ret) && $trieddownload) {
                // reset the age of the cache if the server says it was unmodified
                $this->saveCache($url, $ret, null, true, $cacheId);
            }
            return $ret;
        }
        if (is_array($file)) {
            $headers = $file[2];
            $lastmodified = $file[1];
            $content = $file[0];
        } else {
            $content = $file;
            $lastmodified = false;
            $headers = array();
        }
        if ($forcestring) {
            $this->saveCache($url, $content, $lastmodified, false, $cacheId);
            return $content;
        }
        if (isset($headers['content-type'])) {
            switch ($headers['content-type']) {
                case 'text/xml' :
                case 'application/xml' :
                    $parser = new PEAR_XMLParser;
                    PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                    $err = $parser->parse($content);
                    PEAR::popErrorHandling();
                    if (PEAR::isError($err)) {
                        return PEAR::raiseError('Invalid xml downloaded from "' . $url . '": ' .
                            $err->getMessage());
                    }
                    $content = $parser->getData();
                case 'text/html' :
                default :
                    // use it as a string
            }
        } else {
            // assume XML
            $parser = new PEAR_XMLParser;
            $parser->parse($content);
            $content = $parser->getData();
        }
        $this->saveCache($url, $content, $lastmodified, false, $cacheId);
        return $content;
    }

    function useLocalCache($url, $cacheid = null)
    {
        if ($cacheid === null) {
            $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
                md5($url) . 'rest.cacheid';
            if (file_exists($cacheidfile)) {
                $cacheid = unserialize(implode('', file($cacheidfile)));
            } else {
                return false;
            }
        }
        $cachettl = $this->config->get('cache_ttl');
        // If cache is newer than $cachettl seconds, we use the cache!
        if (time() - $cacheid['age'] < $cachettl) {
            return $this->getCache($url);
        }
        return false;
    }

    function getCacheId($url)
    {
        $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
            md5($url) . 'rest.cacheid';
        if (file_exists($cacheidfile)) {
            $ret = unserialize(implode('', file($cacheidfile)));
            return $ret;
        } else {
            return false;
        }
    }

    function getCache($url)
    {
        $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
            md5($url) . 'rest.cachefile';
        if (file_exists($cachefile)) {
            return unserialize(implode('', file($cachefile)));
        } else {
            return PEAR::raiseError('No cached content available for "' . $url . '"');
        }
    }

    /**
     * @param string full URL to REST resource
     * @param string original contents of the REST resource
     * @param array  HTTP Last-Modified and ETag headers
     * @param bool   if true, then the cache id file should be regenerated to
     *               trigger a new time-to-live value
     */
    function saveCache($url, $contents, $lastmodified, $nochange = false, $cacheid = null)
    {
        $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
            md5($url) . 'rest.cacheid';
        $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
            md5($url) . 'rest.cachefile';
        if ($cacheid === null && $nochange) {
            $cacheid = unserialize(implode('', file($cacheidfile)));
        }

        $fp = @fopen($cacheidfile, 'wb');
        if (!$fp) {
            $cache_dir = $this->config->get('cache_dir');
            if (!is_dir($cache_dir)) {
                System::mkdir(array('-p', $cache_dir));
                $fp = @fopen($cacheidfile, 'wb');
                if (!$fp) {
                    return false;
                }
            } else {
                return false;
            }
        }

        if ($nochange) {
            fwrite($fp, serialize(array(
                'age'        => time(),
                'lastChange' => $cacheid['lastChange'],
                )));
            fclose($fp);
            return true;
        } else {
            fwrite($fp, serialize(array(
                'age'        => time(),
                'lastChange' => $lastmodified,
                )));
        }
        fclose($fp);
        $fp = @fopen($cachefile, 'wb');
        if (!$fp) {
            if (file_exists($cacheidfile)) {
                @unlink($cacheidfile);
            }
            return false;
        }
        fwrite($fp, serialize($contents));
        fclose($fp);
        return true;
    }

    /**
     * Efficiently Download a file through HTTP.  Returns downloaded file as a string in-memory
     * This is best used for small files
     *
     * If an HTTP proxy has been configured (http_proxy PEAR_Config
     * setting), the proxy will be used.
     *
     * @param string  $url       the URL to download
     * @param string  $save_dir  directory to save file in
     * @param false|string|array $lastmodified header values to check against for caching
     *                           use false to return the header values from this download
     * @param false|array $accept Accept headers to send
     * @return string|array  Returns the contents of the downloaded file or a PEAR
     *                       error on failure.  If the error is caused by
     *                       socket-related errors, the error object will
     *                       have the fsockopen error code available through
     *                       getCode().  If caching is requested, then return the header
     *                       values.
     *
     * @access public
     */
    function downloadHttp($url, $lastmodified = null, $accept = false, $channel = false)
    {
        $info = parse_url($url);
        if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) {
            return PEAR::raiseError('Cannot download non-http URL "' . $url . '"');
        }
        if (!isset($info['host'])) {
            return PEAR::raiseError('Cannot download from non-URL "' . $url . '"');
        } else {
            $host = $info['host'];
            if (!array_key_exists('port', $info)) {
                $info['port'] = null;
            }
            if (!array_key_exists('path', $info)) {
                $info['path'] = null;
            }
            $port = $info['port'];
            $path = $info['path'];
        }
        $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
        if ($this->config->get('http_proxy')&&
              $proxy = parse_url($this->config->get('http_proxy'))) {
            $proxy_host = isset($proxy['host']) ? $proxy['host'] : null;
            if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') {
                $proxy_host = 'ssl://' . $proxy_host;
            }
            $proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080;
            $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null;
            $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null;
        }
        if (empty($port)) {
            if (isset($info['scheme']) && $info['scheme'] == 'https') {
                $port = 443;
            } else {
                $port = 80;
            }
        }
        If (isset($proxy['host'])) {
            $request = "GET $url HTTP/1.1\r\n";
        } else {
            $request = "GET $path HTTP/1.1\r\n";
        }
        $request .= "Host: $host:$port\r\n";

        $ifmodifiedsince = '';
        if (is_array($lastmodified)) {
            if (isset($lastmodified['Last-Modified'])) {
                $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n";
            }
            if (isset($lastmodified['ETag'])) {
                $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n";
            }
        } else {
            $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : '');
        }
        $request .= $ifmodifiedsince .
            "User-Agent: PEAR/1.7.2/PHP/" . PHP_VERSION . "\r\n";
        $username = $this->config->get('username', null, $channel);
        $password = $this->config->get('password', null, $channel);
        if ($username && $password) {
            $tmp = base64_encode("$username:$password");
            $request .= "Authorization: Basic $tmp\r\n";
        }
        if ($proxy_host != '' && $proxy_user != '') {
            $request .= 'Proxy-Authorization: Basic ' .
                base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n";
        }
        if ($accept) {
            $request .= 'Accept: ' . implode(', ', $accept) . "\r\n";
        }
        $request .= "Accept-Encoding:\r\n";
        $request .= "Connection: close\r\n";
        $request .= "\r\n";
        if ($proxy_host != '') {
            $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr, 15);
            if (!$fp) {
                return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr",
                    -9276);
            }
        } else {
            if (isset($info['scheme']) && $info['scheme'] == 'https') {
                $host = 'ssl://' . $host;
            }
            $fp = @fsockopen($host, $port, $errno, $errstr);
            if (!$fp) {
                return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno);
            }
        }
        fwrite($fp, $request);
        $headers = array();
        while (trim($line = fgets($fp, 1024))) {
            if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) {
                $headers[strtolower($matches[1])] = trim($matches[2]);
            } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
                if ($matches[1] == 304 && ($lastmodified || ($lastmodified === false))) {
                    return false;
                }
                if ($matches[1] != 200) {
                    return PEAR::raiseError("File http://$host:$port$path not valid (received: $line)", (int) $matches[1]);
                }
            }
        }
        if (isset($headers['content-length'])) {
            $length = $headers['content-length'];
        } else {
            $length = -1;
        }
        $data = '';
        while ($chunk = @fread($fp, 8192)) {
            $data .= $chunk;
        }
        fclose($fp);
        if ($lastmodified === false || $lastmodified) {
            if (isset($headers['etag'])) {
                $lastmodified = array('ETag' => $headers['etag']);
            }
            if (isset($headers['last-modified'])) {
                if (is_array($lastmodified)) {
                    $lastmodified['Last-Modified'] = $headers['last-modified'];
                } else {
                    $lastmodified = $headers['last-modified'];
                }
            }
            return array($data, $lastmodified, $headers);
        }
        return $data;
    }
}
?>
PEAR-1.7.2/PEAR/RunTest.php100644   1750   1750      103774 11014312742  10405 <?php
/**
 * PEAR_RunTest
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Tomas V.V.Cox <cox@idecnet.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: RunTest.php,v 1.67 2008/05/14 02:30:16 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.3.3
 */

/**
 * for error handling
 */
require_once 'PEAR.php';
require_once 'PEAR/Config.php';

define('DETAILED', 1);
putenv("PHP_PEAR_RUNTESTS=1");

/**
 * Simplified version of PHP's test suite
 *
 * Try it with:
 *
 * $ php -r 'include "../PEAR/RunTest.php"; $t=new PEAR_RunTest; $o=$t->run("./pear_system.phpt");print_r($o);'
 *
 *
 * @category   pear
 * @package    PEAR
 * @author     Tomas V.V.Cox <cox@idecnet.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.3.3
 */
class PEAR_RunTest
{
    var $_headers = array();
    var $_logger;
    var $_options;
    var $_php;
    var $tests_count;
    var $xdebug_loaded;
    /**
     * Saved value of php executable, used to reset $_php when we
     * have a test that uses cgi
     *
     * @var unknown_type
     */
    var $_savephp;
    var $ini_overwrites = array(
        'output_handler=',
        'open_basedir=',
        'safe_mode=0',
        'disable_functions=',
        'output_buffering=Off',
        'display_errors=1',
        'log_errors=0',
        'html_errors=0',
        'track_errors=1',
        'report_memleaks=0',
        'report_zend_debug=0',
        'docref_root=',
        'docref_ext=.html',
        'error_prepend_string=',
        'error_append_string=',
        'auto_prepend_file=',
        'auto_append_file=',
        'magic_quotes_runtime=0',
        'xdebug.default_enable=0',
        'allow_url_fopen=1',
    );

    /**
     * An object that supports the PEAR_Common->log() signature, or null
     * @param PEAR_Common|null
     */
    function PEAR_RunTest($logger = null, $options = array())
    {
        if (!defined('E_DEPRECATED')) {
            define('E_DEPRECATED', 0);
        }
        if (!defined('E_STRICT')) {
            define('E_STRICT', 0);
        }
        $this->ini_overwrites[] = 'error_reporting=' . (E_ALL & ~(E_DEPRECATED | E_STRICT));
        if (is_null($logger)) {
            require_once 'PEAR/Common.php';
            $logger = new PEAR_Common;
        }
        $this->_logger  = $logger;
        $this->_options = $options;

        $conf = &PEAR_Config::singleton();
        $this->_php = $conf->get('php_bin');
    }

    /**
     * Taken from php-src/run-tests.php
     *
     * @param string $commandline command name
     * @param array $env
     * @param string $stdin standard input to pass to the command
     * @return unknown
     */
    function system_with_timeout($commandline, $env = null, $stdin = null)
    {
        $data = '';
        if (version_compare(phpversion(), '5.0.0', '<')) {
            $proc = proc_open($commandline, array(
                0 => array('pipe', 'r'),
                1 => array('pipe', 'w'),
                2 => array('pipe', 'w')
                ), $pipes);
        } else {
            $proc = proc_open($commandline, array(
                0 => array('pipe', 'r'),
                1 => array('pipe', 'w'),
                2 => array('pipe', 'w')
                ), $pipes, null, $env, array('suppress_errors' => true));
        }

        if (!$proc) {
            return false;
        }

        if (is_string($stdin)) {
            fwrite($pipes[0], $stdin);
        }
        fclose($pipes[0]);

        while (true) {
            /* hide errors from interrupted syscalls */
            $r = $pipes;
            $e = $w = null;
            $n = @stream_select($r, $w, $e, 60);

            if ($n === 0) {
                /* timed out */
                $data .= "\n ** ERROR: process timed out **\n";
                proc_terminate($proc);
                return array(1234567890, $data);
            } else if ($n > 0) {
                $line = fread($pipes[1], 8192);
                if (strlen($line) == 0) {
                    /* EOF */
                    break;
                }
                $data .= $line;
            }
        }
        if (function_exists('proc_get_status')) {
            $stat = proc_get_status($proc);
            if ($stat['signaled']) {
                $data .= "\nTermsig=".$stat['stopsig'];
            }
        }
        $code = proc_close($proc);
        if (function_exists('proc_get_status')) {
            $code = $stat['exitcode'];
        }
        return array($code, $data);
    }

    /**
     * Turns a PHP INI string into an array
     *
     * Turns -d "include_path=/foo/bar" into this:
     * array(
     *   'include_path' => array(
     *          'operator' => '-d',
     *          'value'    => '/foo/bar',
     *   )
     * )
     * Works both with quotes and without
     *
     * @param string an PHP INI string, -d "include_path=/foo/bar"
     * @return array
     */
    function iniString2array($ini_string)
    {
        if (!$ini_string) {
            return array();
        }
        $split = preg_split('/[\s]|=/', $ini_string, -1, PREG_SPLIT_NO_EMPTY);
        $key   = $split[1][0] == '"'                     ? substr($split[1], 1)     : $split[1];
        $value = $split[2][strlen($split[2]) - 1] == '"' ? substr($split[2], 0, -1) : $split[2];
        // FIXME review if this is really the struct to go with
        $array = array($key => array('operator' => $split[0], 'value' => $value));
        return $array;
    }

    function settings2array($settings, $ini_settings)
    {
        foreach ($settings as $setting) {
            if (strpos($setting, '=') !== false) {
                $setting = explode('=', $setting, 2);
                $name  = trim(strtolower($setting[0]));
                $value = trim($setting[1]);
                $ini_settings[$name] = $value;
            }
        }
        return $ini_settings;
    }

    function settings2params($ini_settings)
    {
        $settings = '';
        foreach ($ini_settings as $name => $value) {
            if (is_array($value)) {
                $operator = $value['operator'];
                $value    = $value['value'];
            } else {
                $operator = '-d';
            }
            $value = addslashes($value);
            $settings .= " $operator \"$name=$value\"";
        }
        return $settings;
    }

    function runPHPUnit($file, $ini_settings = '')
    {
        if (!file_exists($file) && file_exists(getcwd() . DIRECTORY_SEPARATOR . $file)) {
            $file = realpath(getcwd() . DIRECTORY_SEPARATOR . $file);
            break;
        } elseif (file_exists($file)) {
            $file = realpath($file);
        }
        $cmd = "$this->_php$ini_settings -f $file";
        if (isset($this->_logger)) {
            $this->_logger->log(2, 'Running command "' . $cmd . '"');
        }

        $savedir = getcwd(); // in case the test moves us around
        chdir(dirname($file));
        echo `$cmd`;
        chdir($savedir);
        return 'PASSED'; // we have no way of knowing this information so assume passing
    }

    /**
     * Runs an individual test case.
     *
     * @param string       The filename of the test
     * @param array|string INI settings to be applied to the test run
     * @param integer      Number what the current running test is of the
     *                     whole test suite being runned.
     *
     * @return string|object Returns PASSED, WARNED, FAILED depending on how the
     *                       test came out.
     *                       PEAR Error when the tester it self fails
     */
    function run($file, $ini_settings = array(), $test_number = 1)
    {
        if (isset($this->_savephp)) {
            $this->_php = $this->_savephp;
            unset($this->_savephp);
        }
        if (empty($this->_options['cgi'])) {
            // try to see if php-cgi is in the path
            $res = $this->system_with_timeout('php-cgi -v');
            if (false !== $res && !(is_array($res) && $res === array(127, ''))) {
                $this->_options['cgi'] = 'php-cgi';
            }
        }
        if (1 < $len = strlen($this->tests_count)) {
            $test_number = str_pad($test_number, $len, ' ', STR_PAD_LEFT);
            $test_nr = "[$test_number/$this->tests_count] ";
        } else {
            $test_nr = '';
        }

        $file = realpath($file);
        $section_text = $this->_readFile($file);
        if (PEAR::isError($section_text)) {
            return $section_text;
        }

        if (isset($section_text['POST_RAW']) && isset($section_text['UPLOAD'])) {
            return PEAR::raiseError("Cannot contain both POST_RAW and UPLOAD in test file: $file");
        }

        $cwd = getcwd();

        $pass_options = '';
        if (!empty($this->_options['ini'])) {
            $pass_options = $this->_options['ini'];
        }

        if (is_string($ini_settings)) {
            $ini_settings = $this->iniString2array($ini_settings);
        }

        $ini_settings = $this->settings2array($this->ini_overwrites, $ini_settings);
        if ($section_text['INI']) {
            if (strpos($section_text['INI'], '{PWD}') !== false) {
                $section_text['INI'] = str_replace('{PWD}', dirname($file), $section_text['INI']);
            }
            $ini = preg_split( "/[\n\r]+/", $section_text['INI']);
            $ini_settings = $this->settings2array($ini, $ini_settings);
        }
        $ini_settings = $this->settings2params($ini_settings);
        $shortname = str_replace($cwd . DIRECTORY_SEPARATOR, '', $file);

        $tested = trim($section_text['TEST']);
        $tested.= !isset($this->_options['simple']) ? "[$shortname]" : ' ';

        if (!empty($section_text['POST']) || !empty($section_text['POST_RAW']) ||
              !empty($section_text['UPLOAD']) || !empty($section_text['GET']) ||
              !empty($section_text['COOKIE']) || !empty($section_text['EXPECTHEADERS'])) {
            if (empty($this->_options['cgi'])) {
                if (!isset($this->_options['quiet'])) {
                    $this->_logger->log(0, "SKIP $test_nr$tested (reason: --cgi option needed for this test, type 'pear help run-tests')");
                }
                if (isset($this->_options['tapoutput'])) {
                    return array('ok', ' # skip --cgi option needed for this test, "pear help run-tests" for info');
                }
                return 'SKIPPED';
            }
            $this->_savephp = $this->_php;
            $this->_php = $this->_options['cgi'];
        }

        $temp_dir = realpath(dirname($file));
        $main_file_name = basename($file, 'phpt');
        $diff_filename     = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'diff';
        $log_filename      = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'log';
        $exp_filename      = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'exp';
        $output_filename   = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'out';
        $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'mem';
        $temp_file         = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'php';
        $temp_skipif       = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'skip.php';
        $temp_clean        = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'clean.php';
        $tmp_post          = $temp_dir . DIRECTORY_SEPARATOR . uniqid('phpt.');

        // unlink old test results
        $this->_cleanupOldFiles($file);

        // Check if test should be skipped.
        $res  = $this->_runSkipIf($section_text, $temp_skipif, $tested, $ini_settings);
        if (count($res) != 2) {
            return $res;
        }
        $info = $res['info'];
        $warn = $res['warn'];

        // We've satisfied the preconditions - run the test!
        if (isset($this->_options['coverage']) && $this->xdebug_loaded) {
            $len_f = 5;
            if (substr($section_text['FILE'], 0, 5) != '<?php'
                && substr($section_text['FILE'], 0, 2) == '<?') {
                $len_f = 2;
            }

            $text = '<?php' . "\n" . 'xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);' . "\n";
            $new  = substr($section_text['FILE'], $len_f, strlen($section_text['FILE']));
            $text .= substr($new, 0, strrpos($new, '?>'));
            $xdebug_file = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'xdebug';
            $text .= "\n" .
                   "\n" . '$xdebug = var_export(xdebug_get_code_coverage(), true);';
            if (!function_exists('file_put_contents')) {
                $text .= "\n" . '$fh = fopen(\'' . $xdebug_file . '\', "wb");' .
                        "\n" . 'if ($fh !== false) {' .
                        "\n" . '    fwrite($fh, $xdebug);' .
                        "\n" . '    fclose($fh);' .
                        "\n" . '}';
            } else {
                $text .= "\n" . 'file_put_contents(\'' . $xdebug_file . '\', $xdebug);';
            }
            $text .= "\n" . 'xdebug_stop_code_coverage();' . "\n" . '?>';

            $this->save_text($temp_file, $text);
        } else {
            $this->save_text($temp_file, $section_text['FILE']);
        }

        $args = $section_text['ARGS'] ? ' -- '.$section_text['ARGS'] : '';
        $cmd = "$this->_php$ini_settings -f \"$temp_file\" $args 2>&1";
        if (isset($this->_logger)) {
            $this->_logger->log(2, 'Running command "' . $cmd . '"');
        }

        // Reset environment from any previous test.
        $env = $this->_resetEnv($section_text, $temp_file);

        $section_text = $this->_processUpload($section_text, $file);
        if (PEAR::isError($section_text)) {
            return $section_text;
        }

        if (array_key_exists('POST_RAW', $section_text) && !empty($section_text['POST_RAW'])) {
            $post = trim($section_text['POST_RAW']);
            $raw_lines = explode("\n", $post);

            $request = '';
            $started = false;
            foreach ($raw_lines as $i => $line) {
                if (empty($env['CONTENT_TYPE']) &&
                    preg_match('/^Content-Type:(.*)/i', $line, $res)) {
                    $env['CONTENT_TYPE'] = trim(str_replace("\r", '', $res[1]));
                    continue;
                }
                if ($started) {
                    $request .= "\n";
                }
                $started = true;
                $request .= $line;
            }

            $env['CONTENT_LENGTH'] = strlen($request);
            $env['REQUEST_METHOD'] = 'POST';

            $this->save_text($tmp_post, $request);
            $cmd = "$this->_php$pass_options$ini_settings \"$temp_file\" 2>&1 < $tmp_post";
        } elseif (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) {
            $post = trim($section_text['POST']);
            $this->save_text($tmp_post, $post);
            $content_length = strlen($post);

            $env['REQUEST_METHOD'] = 'POST';
            $env['CONTENT_TYPE']   = 'application/x-www-form-urlencoded';
            $env['CONTENT_LENGTH'] = $content_length;

            $cmd = "$this->_php$pass_options$ini_settings \"$temp_file\" 2>&1 < $tmp_post";
        } else {
            $env['REQUEST_METHOD'] = 'GET';
            $env['CONTENT_TYPE']   = '';
            $env['CONTENT_LENGTH'] = '';
        }

        if (OS_WINDOWS && isset($section_text['RETURNS'])) {
            ob_start();
            system($cmd, $return_value);
            $out = ob_get_contents();
            ob_end_clean();
            $section_text['RETURNS'] = (int) trim($section_text['RETURNS']);
            $returnfail = ($return_value != $section_text['RETURNS']);
        } else {
            $returnfail = false;
            $stdin = isset($section_text['STDIN']) ? $section_text['STDIN'] : null;
            $out = $this->system_with_timeout($cmd, $env, $stdin);
            $return_value = $out[0];
            $out = $out[1];
        }

        $output = preg_replace('/\r\n/', "\n", trim($out));

        if (isset($tmp_post) && realpath($tmp_post) && file_exists($tmp_post)) {
            @unlink(realpath($tmp_post));
        }
        chdir($cwd); // in case the test moves us around

        $this->_testCleanup($section_text, $temp_clean);

        /* when using CGI, strip the headers from the output */
        $output = $this->_stripHeadersCGI($output);

        if (isset($section_text['EXPECTHEADERS'])) {
            $testheaders = $this->_processHeaders($section_text['EXPECTHEADERS']);
            $missing = array_diff_assoc($testheaders, $this->_headers);
            $changed = '';
            foreach ($missing as $header => $value) {
                if (isset($this->_headers[$header])) {
                    $changed .= "-$header: $value\n+$header: ";
                    $changed .= $this->_headers[$header];
                } else {
                    $changed .= "-$header: $value\n";
                }
            }
            if ($missing) {
                // tack on failed headers to output:
                $output .= "\n====EXPECTHEADERS FAILURE====:\n$changed";
            }
        }
        // Does the output match what is expected?
        do {
            if (isset($section_text['EXPECTF']) || isset($section_text['EXPECTREGEX'])) {
                if (isset($section_text['EXPECTF'])) {
                    $wanted = trim($section_text['EXPECTF']);
                } else {
                    $wanted = trim($section_text['EXPECTREGEX']);
                }
                $wanted_re = preg_replace('/\r\n/', "\n", $wanted);
                if (isset($section_text['EXPECTF'])) {
                    $wanted_re = preg_quote($wanted_re, '/');
                    // Stick to basics
                    $wanted_re = str_replace("%s", ".+?", $wanted_re); //not greedy
                    $wanted_re = str_replace("%i", "[+\-]?[0-9]+", $wanted_re);
                    $wanted_re = str_replace("%d", "[0-9]+", $wanted_re);
                    $wanted_re = str_replace("%x", "[0-9a-fA-F]+", $wanted_re);
                    $wanted_re = str_replace("%f", "[+\-]?\.?[0-9]+\.?[0-9]*(E-?[0-9]+)?", $wanted_re);
                    $wanted_re = str_replace("%c", ".", $wanted_re);
                    // %f allows two points "-.0.0" but that is the best *simple* expression
                }
    /* DEBUG YOUR REGEX HERE
            var_dump($wanted_re);
            print(str_repeat('=', 80) . "\n");
            var_dump($output);
    */
                if (!$returnfail && preg_match("/^$wanted_re\$/s", $output)) {
                    if (file_exists($temp_file)) {
                        unlink($temp_file);
                    }
                    if (array_key_exists('FAIL', $section_text)) {
                        break;
                    }
                    if (!isset($this->_options['quiet'])) {
                        $this->_logger->log(0, "PASS $test_nr$tested$info");
                    }
                    if (isset($this->_options['tapoutput'])) {
                        return array('ok', ' - ' . $tested);
                    }
                    return 'PASSED';
                }
            } else {
                if (isset($section_text['EXPECTFILE'])) {
                    $f = $temp_dir . '/' . trim($section_text['EXPECTFILE']);
                    if (!($fp = @fopen($f, 'rb'))) {
                        return PEAR::raiseError('--EXPECTFILE-- section file ' .
                            $f . ' not found');
                    }
                    fclose($fp);
                    $section_text['EXPECT'] = file_get_contents($f);
                }
                $wanted = preg_replace('/\r\n/', "\n", trim($section_text['EXPECT']));
                // compare and leave on success
                if (!$returnfail && 0 == strcmp($output, $wanted)) {
                    if (file_exists($temp_file)) {
                        unlink($temp_file);
                    }
                    if (array_key_exists('FAIL', $section_text)) {
                        break;
                    }
                    if (!isset($this->_options['quiet'])) {
                        $this->_logger->log(0, "PASS $test_nr$tested$info");
                    }
                    if (isset($this->_options['tapoutput'])) {
                        return array('ok', ' - ' . $tested);
                    }
                    return 'PASSED';
                }
            }
        } while (false);

        if (array_key_exists('FAIL', $section_text)) {
            // we expect a particular failure
            // this is only used for testing PEAR_RunTest
            $expectf  = isset($section_text['EXPECTF']) ? $wanted_re : null;
            $faildiff = $this->generate_diff($wanted, $output, null, $expectf);
            $faildiff = preg_replace('/\r/', '', $faildiff);
            $wanted   = preg_replace('/\r/', '', trim($section_text['FAIL']));
            if ($faildiff == $wanted) {
                if (!isset($this->_options['quiet'])) {
                    $this->_logger->log(0, "PASS $test_nr$tested$info");
                }
                if (isset($this->_options['tapoutput'])) {
                    return array('ok', ' - ' . $tested);
                }
                return 'PASSED';
            }
            unset($section_text['EXPECTF']);
            $output = $faildiff;
            if (isset($section_text['RETURNS'])) {
                return PEAR::raiseError('Cannot have both RETURNS and FAIL in the same test: ' .
                    $file);
            }
        }

        // Test failed so we need to report details.
        $txt = $warn ? 'WARN ' : 'FAIL ';
        $this->_logger->log(0, $txt . $test_nr . $tested . $info);

        // write .exp
        $res = $this->_writeLog($exp_filename, $wanted);
        if (PEAR::isError($res)) {
            return $res;
        }

        // write .out
        $res = $this->_writeLog($output_filename, $output);
        if (PEAR::isError($res)) {
            return $res;
        }

        // write .diff
        $returns = isset($section_text['RETURNS']) ?
                        array(trim($section_text['RETURNS']), $return_value) : null;
        $expectf = isset($section_text['EXPECTF']) ? $wanted_re : null;
        $data = $this->generate_diff($wanted, $output, $returns, $expectf);
        $res  = $this->_writeLog($diff_filename, $data);
        if (PEAR::isError($res)) {
            return $res;
        }

        // write .log
        $data = "
---- EXPECTED OUTPUT
$wanted
---- ACTUAL OUTPUT
$output
---- FAILED
";

        if ($returnfail) {
            $data .= "
---- EXPECTED RETURN
$section_text[RETURNS]
---- ACTUAL RETURN
$return_value
";
        }

        $res = $this->_writeLog($log_filename, $data);
        if (PEAR::isError($res)) {
            return $res;
        }

        if (isset($this->_options['tapoutput'])) {
            $wanted = explode("\n", $wanted);
            $wanted = "# Expected output:\n#\n#" . implode("\n#", $wanted);
            $output = explode("\n", $output);
            $output = "#\n#\n# Actual output:\n#\n#" . implode("\n#", $output);
            return array($wanted . $output . 'not ok', ' - ' . $tested);
        }
        return $warn ? 'WARNED' : 'FAILED';
    }

    function generate_diff($wanted, $output, $rvalue, $wanted_re)
    {
        $w  = explode("\n", $wanted);
        $o  = explode("\n", $output);
        $wr = explode("\n", $wanted_re);
        $w1 = array_diff_assoc($w, $o);
        $o1 = array_diff_assoc($o, $w);
        $o2 = $w2 = array();
        foreach ($w1 as $idx => $val) {
            if (!$wanted_re || !isset($wr[$idx]) || !isset($o1[$idx]) ||
                  !preg_match('/^' . $wr[$idx] . '\\z/', $o1[$idx])) {
                $w2[sprintf("%03d<", $idx)] = sprintf("%03d- ", $idx + 1) . $val;
            }
        }
        foreach ($o1 as $idx => $val) {
            if (!$wanted_re || !isset($wr[$idx]) ||
                  !preg_match('/^' . $wr[$idx] . '\\z/', $val)) {
                $o2[sprintf("%03d>", $idx)] = sprintf("%03d+ ", $idx + 1) . $val;
            }
        }
        $diff = array_merge($w2, $o2);
        ksort($diff);
        $extra = $rvalue ? "##EXPECTED: $rvalue[0]\r\n##RETURNED: $rvalue[1]" : '';
        return implode("\r\n", $diff) . $extra;
    }

    //  Write the given text to a temporary file, and return the filename.
    function save_text($filename, $text)
    {
        if (!$fp = fopen($filename, 'w')) {
            return PEAR::raiseError("Cannot open file '" . $filename . "' (save_text)");
        }
        fwrite($fp, $text);
        fclose($fp);
    if (1 < DETAILED) echo "
FILE $filename {{{
$text
}}}
";
    }

    function _cleanupOldFiles($file)
    {
        $temp_dir = realpath(dirname($file));
        $mainFileName = basename($file, 'phpt');
        $diff_filename     = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'diff';
        $log_filename      = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'log';
        $exp_filename      = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'exp';
        $output_filename   = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'out';
        $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'mem';
        $temp_file         = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'php';
        $temp_skipif       = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'skip.php';
        $temp_clean        = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'clean.php';
        $tmp_post          = $temp_dir . DIRECTORY_SEPARATOR . uniqid('phpt.');

        // unlink old test results
        @unlink($diff_filename);
        @unlink($log_filename);
        @unlink($exp_filename);
        @unlink($output_filename);
        @unlink($memcheck_filename);
        @unlink($temp_file);
        @unlink($temp_skipif);
        @unlink($tmp_post);
        @unlink($temp_clean);
    }

    function _runSkipIf($section_text, $temp_skipif, $tested, $ini_settings)
    {
        $info = '';
        $warn = false;
        if (array_key_exists('SKIPIF', $section_text) && trim($section_text['SKIPIF'])) {
            $this->save_text($temp_skipif, $section_text['SKIPIF']);
            $output = $this->system_with_timeout("$this->_php$ini_settings -f \"$temp_skipif\"");
            $output = $output[1];
            $loutput = ltrim($output);
            unlink($temp_skipif);
            if (!strncasecmp('skip', $loutput, 4)) {
                $skipreason = "SKIP $tested";
                if (preg_match('/^\s*skip\s*(.+)\s*/i', $output, $m)) {
                    $skipreason .= '(reason: ' . $m[1] . ')';
                }
                if (!isset($this->_options['quiet'])) {
                    $this->_logger->log(0, $skipreason);
                }
                if (isset($this->_options['tapoutput'])) {
                    return array('ok', ' # skip ' . $reason);
                }
                return 'SKIPPED';
            }

            if (!strncasecmp('info', $loutput, 4)
                && preg_match('/^\s*info\s*(.+)\s*/i', $output, $m)) {
                $info = " (info: $m[1])";
            }

            if (!strncasecmp('warn', $loutput, 4)
                && preg_match('/^\s*warn\s*(.+)\s*/i', $output, $m)) {
                $warn = true; /* only if there is a reason */
                $info = " (warn: $m[1])";
            }
        }

        return array('warn' => $warn, 'info' => $info);
    }

    function _stripHeadersCGI($output)
    {
        $this->headers = array();
        if (!empty($this->_options['cgi']) &&
              $this->_php == $this->_options['cgi'] &&
              preg_match("/^(.*?)(?:\n\n(.*)|\\z)/s", $output, $match)) {
            $output = isset($match[2]) ? trim($match[2]) : '';
            $this->_headers = $this->_processHeaders($match[1]);
        }

        return $output;
    }

    /**
     * Return an array that can be used with array_diff() to compare headers
     *
     * @param string $text
     */
    function _processHeaders($text)
    {
        $headers = array();
        $rh = preg_split("/[\n\r]+/", $text);
        foreach ($rh as $line) {
            if (strpos($line, ':')!== false) {
                $line = explode(':', $line, 2);
                $headers[trim($line[0])] = trim($line[1]);
            }
        }
        return $headers;
    }

    function _readFile($file)
    {
        // Load the sections of the test file.
        $section_text = array(
            'TEST'   => '(unnamed test)',
            'SKIPIF' => '',
            'GET'    => '',
            'COOKIE' => '',
            'POST'   => '',
            'ARGS'   => '',
            'INI'    => '',
            'CLEAN'  => '',
        );

        if (!is_file($file) || !$fp = fopen($file, "r")) {
            return PEAR::raiseError("Cannot open test file: $file");
        }

        $section = '';
        while (!feof($fp)) {
            $line = fgets($fp);

            // Match the beginning of a section.
            if (preg_match('/^--([_A-Z]+)--/', $line, $r)) {
                $section = $r[1];
                $section_text[$section] = '';
                continue;
            } elseif (empty($section)) {
                fclose($fp);
                return PEAR::raiseError("Invalid sections formats in test file: $file");
            }

            // Add to the section text.
            $section_text[$section] .= $line;
        }
        fclose($fp);

        return $section_text;
    }

    function _writeLog($logname, $data)
    {
        if (!$log = fopen($logname, 'w')) {
            return PEAR::raiseError("Cannot create test log - $logname");
        }
        fwrite($log, $data);
        fclose($log);
    }

    function _resetEnv($section_text, $temp_file)
    {
        $env = $_ENV;
        $env['REDIRECT_STATUS'] = '';
        $env['QUERY_STRING']    = '';
        $env['PATH_TRANSLATED'] = '';
        $env['SCRIPT_FILENAME'] = '';
        $env['REQUEST_METHOD']  = '';
        $env['CONTENT_TYPE']    = '';
        $env['CONTENT_LENGTH']  = '';
        if (!empty($section_text['ENV'])) {
            foreach (explode("\n", trim($section_text['ENV'])) as $e) {
                $e = explode('=', trim($e), 2);
                if (!empty($e[0]) && isset($e[1])) {
                    $env[$e[0]] = $e[1];
                }
            }
        }
        if (array_key_exists('GET', $section_text)) {
            $env['QUERY_STRING'] = trim($section_text['GET']);
        } else {
            $env['QUERY_STRING'] = '';
        }
        if (array_key_exists('COOKIE', $section_text)) {
            $env['HTTP_COOKIE'] = trim($section_text['COOKIE']);
        } else {
            $env['HTTP_COOKIE'] = '';
        }
        $env['REDIRECT_STATUS'] = '1';
        $env['PATH_TRANSLATED'] = $temp_file;
        $env['SCRIPT_FILENAME'] = $temp_file;

        return $env;
    }

    function _processUpload($section_text, $file)
    {
        if (array_key_exists('UPLOAD', $section_text) && !empty($section_text['UPLOAD'])) {
            $upload_files = trim($section_text['UPLOAD']);
            $upload_files = explode("\n", $upload_files);

            $request = "Content-Type: multipart/form-data; boundary=---------------------------20896060251896012921717172737\n" .
                       "-----------------------------20896060251896012921717172737\n";
            foreach ($upload_files as $fileinfo) {
                $fileinfo = explode('=', $fileinfo);
                if (count($fileinfo) != 2) {
                    return PEAR::raiseError("Invalid UPLOAD section in test file: $file");
                }
                if (!realpath(dirname($file) . '/' . $fileinfo[1])) {
                    return PEAR::raiseError("File for upload does not exist: $fileinfo[1] " .
                        "in test file: $file");
                }
                $file_contents = file_get_contents(dirname($file) . '/' . $fileinfo[1]);
                $fileinfo[1] = basename($fileinfo[1]);
                $request .= "Content-Disposition: form-data; name=\"$fileinfo[0]\"; filename=\"$fileinfo[1]\"\n";
                $request .= "Content-Type: text/plain\n\n";
                $request .= $file_contents . "\n" .
                    "-----------------------------20896060251896012921717172737\n";
            }

            if (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) {
                // encode POST raw
                $post = trim($section_text['POST']);
                $post = explode('&', $post);
                foreach ($post as $i => $post_info) {
                    $post_info = explode('=', $post_info);
                    if (count($post_info) != 2) {
                        return PEAR::raiseError("Invalid POST data in test file: $file");
                    }
                    $post_info[0] = rawurldecode($post_info[0]);
                    $post_info[1] = rawurldecode($post_info[1]);
                    $post[$i] = $post_info;
                }
                foreach ($post as $post_info) {
                    $request .= "Content-Disposition: form-data; name=\"$post_info[0]\"\n\n";
                    $request .= $post_info[1] . "\n" .
                        "-----------------------------20896060251896012921717172737\n";
                }
                unset($section_text['POST']);
            }
            $section_text['POST_RAW'] = $request;
        }

        return $section_text;
    }

    function _testCleanup($section_text, $temp_clean)
    {
        if ($section_text['CLEAN']) {
            // perform test cleanup
            $this->save_text($temp_clean, $section_text['CLEAN']);
            $this->system_with_timeout("$this->_php $temp_clean");
            if (file_exists($temp_clean)) {
                unlink($temp_clean);
            }
        }
    }
}PEAR-1.7.2/PEAR/Validate.php100644   1750   1750       53476 11014312742  10515 <?php
/**
 * PEAR_Validate
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Validate.php,v 1.52 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**#@+
 * Constants for install stage
 */
define('PEAR_VALIDATE_INSTALLING', 1);
define('PEAR_VALIDATE_UNINSTALLING', 2); // this is not bit-mapped like the others
define('PEAR_VALIDATE_NORMAL', 3);
define('PEAR_VALIDATE_DOWNLOADING', 4); // this is not bit-mapped like the others
define('PEAR_VALIDATE_PACKAGING', 7);
/**#@-*/
require_once 'PEAR/Common.php';
require_once 'PEAR/Validator/PECL.php';

/**
 * Validation class for package.xml - channel-level advanced validation
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Validate
{
    var $packageregex = _PEAR_COMMON_PACKAGE_NAME_PREG;
    /**
     * @var PEAR_PackageFile_v1|PEAR_PackageFile_v2
     */
    var $_packagexml;
    /**
     * @var int one of the PEAR_VALIDATE_* constants
     */
    var $_state = PEAR_VALIDATE_NORMAL;
    /**
     * Format: ('error' => array('field' => name, 'reason' => reason), 'warning' => same)
     * @var array
     * @access private
     */
    var $_failures = array('error' => array(), 'warning' => array());

    /**
     * Override this method to handle validation of normal package names
     * @param string
     * @return bool
     * @access protected
     */
    function _validPackageName($name)
    {
        return (bool) preg_match('/^' . $this->packageregex . '\\z/', $name);
    }

    /**
     * @param string package name to validate
     * @param string name of channel-specific validation package
     * @final
     */
    function validPackageName($name, $validatepackagename = false)
    {
        if ($validatepackagename) {
            if (strtolower($name) == strtolower($validatepackagename)) {
                return (bool) preg_match('/^[a-zA-Z0-9_]+(?:\.[a-zA-Z0-9_]+)*\\z/', $name);
            }
        }
        return $this->_validPackageName($name);
    }

    /**
     * This validates a bundle name, and bundle names must conform
     * to the PEAR naming convention, so the method is final and static.
     * @param string
     * @final
     * @static
     */
    function validGroupName($name)
    {
        return (bool) preg_match('/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '\\z/', $name);
    }

    /**
     * Determine whether $state represents a valid stability level
     * @param string
     * @return bool
     * @static
     * @final
     */
    function validState($state)
    {
        return in_array($state, array('snapshot', 'devel', 'alpha', 'beta', 'stable'));
    }

    /**
     * Get a list of valid stability levels
     * @return array
     * @static
     * @final
     */
    function getValidStates()
    {
        return array('snapshot', 'devel', 'alpha', 'beta', 'stable');
    }

    /**
     * Determine whether a version is a properly formatted version number that can be used
     * by version_compare
     * @param string
     * @return bool
     * @static
     * @final
     */
    function validVersion($ver)
    {
        return (bool) preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver);
    }

    /**
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     */
    function setPackageFile(&$pf)
    {
        $this->_packagexml = &$pf;
    }

    /**
     * @access private
     */
    function _addFailure($field, $reason)
    {
        $this->_failures['errors'][] = array('field' => $field, 'reason' => $reason);
    }

    /**
     * @access private
     */
    function _addWarning($field, $reason)
    {
        $this->_failures['warnings'][] = array('field' => $field, 'reason' => $reason);
    }

    function getFailures()
    {
        $failures = $this->_failures;
        $this->_failures = array('warnings' => array(), 'errors' => array());
        return $failures;
    }

    /**
     * @param int one of the PEAR_VALIDATE_* constants
     */
    function validate($state = null)
    {
        if (!isset($this->_packagexml)) {
            return false;
        }
        if ($state !== null) {
            $this->_state = $state;
        }
        $this->_failures = array('warnings' => array(), 'errors' => array());
        $this->validatePackageName();
        $this->validateVersion();
        $this->validateMaintainers();
        $this->validateDate();
        $this->validateSummary();
        $this->validateDescription();
        $this->validateLicense();
        $this->validateNotes();
        if ($this->_packagexml->getPackagexmlVersion() == '1.0') {
            $this->validateState();
            $this->validateFilelist();
        } elseif ($this->_packagexml->getPackagexmlVersion() == '2.0' ||
                  $this->_packagexml->getPackagexmlVersion() == '2.1') {
            $this->validateTime();
            $this->validateStability();
            $this->validateDeps();
            $this->validateMainFilelist();
            $this->validateReleaseFilelist();
            //$this->validateGlobalTasks();
            $this->validateChangelog();
        }
        return !((bool) count($this->_failures['errors']));
    }

    /**
     * @access protected
     */
    function validatePackageName()
    {
        if ($this->_state == PEAR_VALIDATE_PACKAGING ||
              $this->_state == PEAR_VALIDATE_NORMAL) {
            if (($this->_packagexml->getPackagexmlVersion() == '2.0' ||
                 $this->_packagexml->getPackagexmlVersion() == '2.1') &&
                  $this->_packagexml->getExtends()) {
                $version = $this->_packagexml->getVersion() . '';
                $name = $this->_packagexml->getPackage();
                $test = array_shift($a = explode('.', $version));
                if ($test == '0') {
                    return true;
                }
                $vlen = strlen($test);
                $majver = substr($name, strlen($name) - $vlen);
                while ($majver && !is_numeric($majver{0})) {
                    $majver = substr($majver, 1);
                }
                if ($majver != $test) {
                    $this->_addWarning('package', "package $name extends package " .
                        $this->_packagexml->getExtends() . ' and so the name should ' .
                        'have a postfix equal to the major version like "' .
                        $this->_packagexml->getExtends() . $test . '"');
                    return true;
                } elseif (substr($name, 0, strlen($name) - $vlen) !=
                            $this->_packagexml->getExtends()) {
                    $this->_addWarning('package', "package $name extends package " .
                        $this->_packagexml->getExtends() . ' and so the name must ' .
                        'be an extension like "' . $this->_packagexml->getExtends() .
                        $test . '"');
                    return true;
                }
            }
        }
        if (!$this->validPackageName($this->_packagexml->getPackage())) {
            $this->_addFailure('name', 'package name "' .
                $this->_packagexml->getPackage() . '" is invalid');
            return false;
        }
    }

    /**
     * @access protected
     */
    function validateVersion()
    {
        if ($this->_state != PEAR_VALIDATE_PACKAGING) {
            if (!$this->validVersion($this->_packagexml->getVersion())) {
                $this->_addFailure('version',
                    'Invalid version number "' . $this->_packagexml->getVersion() . '"');
            }
            return false;
        }
        $version = $this->_packagexml->getVersion();
        $versioncomponents = explode('.', $version);
        if (count($versioncomponents) != 3) {
            $this->_addWarning('version',
                'A version number should have 3 decimals (x.y.z)');
            return true;
        }
        $name = $this->_packagexml->getPackage();
        // version must be based upon state
        switch ($this->_packagexml->getState()) {
            case 'snapshot' :
                return true;
            case 'devel' :
                if ($versioncomponents[0] . 'a' == '0a') {
                    return true;
                }
                if ($versioncomponents[0] == 0) {
                    $versioncomponents[0] = '0';
                    $this->_addWarning('version',
                        'version "' . $version . '" should be "' .
                        implode('.' ,$versioncomponents) . '"');
                } else {
                    $this->_addWarning('version',
                        'packages with devel stability must be < version 1.0.0');
                }
                return true;
            break;
            case 'alpha' :
            case 'beta' :
                // check for a package that extends a package,
                // like Foo and Foo2
                if ($this->_state == PEAR_VALIDATE_PACKAGING) {
                    if (substr($versioncomponents[2], 1, 2) == 'rc') {
                        $this->_addFailure('version', 'Release Candidate versions ' .
                            'must have capital RC, not lower-case rc');
                        return false;
                    }
                }
                if (!$this->_packagexml->getExtends()) {
                    if ($versioncomponents[0] == '1') {
                        if ($versioncomponents[2]{0} == '0') {
                            if ($versioncomponents[2] == '0') {
                                // version 1.*.0000
                                $this->_addWarning('version',
                                    'version 1.' . $versioncomponents[1] .
                                        '.0 probably should not be alpha or beta');
                                return true;
                            } elseif (strlen($versioncomponents[2]) > 1) {
                                // version 1.*.0RC1 or 1.*.0beta24 etc.
                                return true;
                            } else {
                                // version 1.*.0
                                $this->_addWarning('version',
                                    'version 1.' . $versioncomponents[1] .
                                        '.0 probably should not be alpha or beta');
                                return true;
                            }
                        } else {
                            $this->_addWarning('version',
                                'bugfix versions (1.3.x where x > 0) probably should ' .
                                'not be alpha or beta');
                            return true;
                        }
                    } elseif ($versioncomponents[0] != '0') {
                        $this->_addWarning('version',
                            'major versions greater than 1 are not allowed for packages ' .
                            'without an <extends> tag or an identical postfix (foo2 v2.0.0)');
                        return true;
                    }
                    if ($versioncomponents[0] . 'a' == '0a') {
                        return true;
                    }
                    if ($versioncomponents[0] == 0) {
                        $versioncomponents[0] = '0';
                        $this->_addWarning('version',
                            'version "' . $version . '" should be "' .
                            implode('.' ,$versioncomponents) . '"');
                    }
                } else {
                    $vlen = strlen($versioncomponents[0] . '');
                    $majver = substr($name, strlen($name) - $vlen);
                    while ($majver && !is_numeric($majver{0})) {
                        $majver = substr($majver, 1);
                    }
                    if (($versioncomponents[0] != 0) && $majver != $versioncomponents[0]) {
                        $this->_addWarning('version', 'first version number "' .
                            $versioncomponents[0] . '" must match the postfix of ' .
                            'package name "' . $name . '" (' .
                            $majver . ')');
                        return true;
                    }
                    if ($versioncomponents[0] == $majver) {
                        if ($versioncomponents[2]{0} == '0') {
                            if ($versioncomponents[2] == '0') {
                                // version 2.*.0000
                                $this->_addWarning('version',
                                    "version $majver." . $versioncomponents[1] .
                                        '.0 probably should not be alpha or beta');
                                return false;
                            } elseif (strlen($versioncomponents[2]) > 1) {
                                // version 2.*.0RC1 or 2.*.0beta24 etc.
                                return true;
                            } else {
                                // version 2.*.0
                                $this->_addWarning('version',
                                    "version $majver." . $versioncomponents[1] .
                                        '.0 cannot be alpha or beta');
                                return true;
                            }
                        } else {
                            $this->_addWarning('version',
                                "bugfix versions ($majver.x.y where y > 0) should " .
                                'not be alpha or beta');
                            return true;
                        }
                    } elseif ($versioncomponents[0] != '0') {
                        $this->_addWarning('version',
                            "only versions 0.x.y and $majver.x.y are allowed for alpha/beta releases");
                        return true;
                    }
                    if ($versioncomponents[0] . 'a' == '0a') {
                        return true;
                    }
                    if ($versioncomponents[0] == 0) {
                        $versioncomponents[0] = '0';
                        $this->_addWarning('version',
                            'version "' . $version . '" should be "' .
                            implode('.' ,$versioncomponents) . '"');
                    }
                }
                return true;
            break;
            case 'stable' :
                if ($versioncomponents[0] == '0') {
                    $this->_addWarning('version', 'versions less than 1.0.0 cannot ' .
                    'be stable');
                    return true;
                }
                if (!is_numeric($versioncomponents[2])) {
                    if (preg_match('/\d+(rc|a|alpha|b|beta)\d*/i',
                          $versioncomponents[2])) {
                        $this->_addWarning('version', 'version "' . $version . '" or any ' .
                            'RC/beta/alpha version cannot be stable');
                        return true;
                    }
                }
                // check for a package that extends a package,
                // like Foo and Foo2
                if ($this->_packagexml->getExtends()) {
                    $vlen = strlen($versioncomponents[0] . '');
                    $majver = substr($name, strlen($name) - $vlen);
                    while ($majver && !is_numeric($majver{0})) {
                        $majver = substr($majver, 1);
                    }
                    if (($versioncomponents[0] != 0) && $majver != $versioncomponents[0]) {
                        $this->_addWarning('version', 'first version number "' .
                            $versioncomponents[0] . '" must match the postfix of ' .
                            'package name "' . $name . '" (' .
                            $majver . ')');
                        return true;
                    }
                } elseif ($versioncomponents[0] > 1) {
                    $this->_addWarning('version', 'major version x in x.y.z may not be greater than ' .
                        '1 for any package that does not have an <extends> tag');
                }
                return true;
            break;
            default :
                return false;
            break;
        }
    }

    /**
     * @access protected
     */
    function validateMaintainers()
    {
        // maintainers can only be truly validated server-side for most channels
        // but allow this customization for those who wish it
        return true;
    }

    /**
     * @access protected
     */
    function validateDate()
    {
        if ($this->_state == PEAR_VALIDATE_NORMAL ||
              $this->_state == PEAR_VALIDATE_PACKAGING) {

            if (!preg_match('/(\d\d\d\d)\-(\d\d)\-(\d\d)/',
                  $this->_packagexml->getDate(), $res) ||
                  count($res) < 4
                  || !checkdate($res[2], $res[3], $res[1])
                ) {
                $this->_addFailure('date', 'invalid release date "' .
                    $this->_packagexml->getDate() . '"');
                return false;
            }


            if ($this->_state == PEAR_VALIDATE_PACKAGING &&
                  $this->_packagexml->getDate() != date('Y-m-d')) {
                $this->_addWarning('date', 'Release Date "' .
                    $this->_packagexml->getDate() . '" is not today');
            }
        }
        return true;
    }

    /**
     * @access protected
     */
    function validateTime()
    {
        if (!$this->_packagexml->getTime()) {
            // default of no time value set
            return true;
        }
        // packager automatically sets time, so only validate if
        // pear validate is called
        if ($this->_state = PEAR_VALIDATE_NORMAL) {
            if (!preg_match('/\d\d:\d\d:\d\d/',
                  $this->_packagexml->getTime())) {
                $this->_addFailure('time', 'invalid release time "' .
                    $this->_packagexml->getTime() . '"');
                return false;
            }
            if (strtotime($this->_packagexml->getTime()) == -1) {
                $this->_addFailure('time', 'invalid release time "' .
                    $this->_packagexml->getTime() . '"');
                return false;
            }
        }
        return true;
    }

    /**
     * @access protected
     */
    function validateState()
    {
        // this is the closest to "final" php4 can get
        if (!PEAR_Validate::validState($this->_packagexml->getState())) {
            if (strtolower($this->_packagexml->getState() == 'rc')) {
                $this->_addFailure('state', 'RC is not a state, it is a version ' .
                    'postfix, use ' . $this->_packagexml->getVersion() . 'RC1, state beta');
            }
            $this->_addFailure('state', 'invalid release state "' .
                $this->_packagexml->getState() . '", must be one of: ' .
                implode(', ', PEAR_Validate::getValidStates()));
            return false;
        }
        return true;
    }

    /**
     * @access protected
     */
    function validateStability()
    {
        $ret = true;
        $packagestability = $this->_packagexml->getState();
        $apistability = $this->_packagexml->getState('api');
        if (!PEAR_Validate::validState($packagestability)) {
            $this->_addFailure('state', 'invalid release stability "' .
                $this->_packagexml->getState() . '", must be one of: ' .
                implode(', ', PEAR_Validate::getValidStates()));
            $ret = false;
        }
        $apistates = PEAR_Validate::getValidStates();
        array_shift($apistates); // snapshot is not allowed
        if (!in_array($apistability, $apistates)) {
            $this->_addFailure('state', 'invalid API stability "' .
                $this->_packagexml->getState('api') . '", must be one of: ' .
                implode(', ', $apistates));
            $ret = false;
        }
        return $ret;
    }

    /**
     * @access protected
     */
    function validateSummary()
    {
        return true;
    }

    /**
     * @access protected
     */
    function validateDescription()
    {
        return true;
    }

    /**
     * @access protected
     */
    function validateLicense()
    {
        return true;
    }

    /**
     * @access protected
     */
    function validateNotes()
    {
        return true;
    }

    /**
     * for package.xml 2.0 only - channels can't use package.xml 1.0
     * @access protected
     */
    function validateDependencies()
    {
        return true;
    }

    /**
     * for package.xml 1.0 only
     * @access private
     */
    function _validateFilelist()
    {
        return true; // placeholder for now
    }

    /**
     * for package.xml 2.0 only
     * @access protected
     */
    function validateMainFilelist()
    {
        return true; // placeholder for now
    }

    /**
     * for package.xml 2.0 only
     * @access protected
     */
    function validateReleaseFilelist()
    {
        return true; // placeholder for now
    }

    /**
     * @access protected
     */
    function validateChangelog()
    {
        return true;
    }

    /**
     * @access protected
     */
    function validateFilelist()
    {
        return true;
    }

    /**
     * @access protected
     */
    function validateDeps()
    {
        return true;
    }
}
?>
PEAR-1.7.2/PEAR/XMLParser.php100644   1750   1750       17070 11014312742  10567 <?php
/**
 * PEAR_XMLParser
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @author     Stephan Schmidt (original XML_Unserializer code)
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: XMLParser.php,v 1.13 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * Parser for any xml file
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @author     Stephan Schmidt (original XML_Unserializer code)
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_XMLParser
{
    /**
     * unserilialized data
     * @var string $_serializedData
     */
    var $_unserializedData = null;

    /**
     * name of the root tag
     * @var string $_root
     */
    var $_root = null;

    /**
     * stack for all data that is found
     * @var array    $_dataStack
     */
    var $_dataStack  =   array();

    /**
     * stack for all values that are generated
     * @var array    $_valStack
     */
    var $_valStack  =   array();

    /**
     * current tag depth
     * @var int    $_depth
     */
    var $_depth = 0;

    /**
     * @return array
     */
    function getData()
    {
        return $this->_unserializedData;
    }

    /**
     * @param string xml content
     * @return true|PEAR_Error
     */
    function parse($data)
    {
        if (!extension_loaded('xml')) {
            include_once 'PEAR.php';
            return PEAR::raiseError("XML Extension not found", 1);
        }
        $this->_valStack = array();
        $this->_dataStack = array();
        $this->_depth = 0;

        if (version_compare(phpversion(), '5.0.0', 'lt')) {
            if (strpos($data, 'encoding="UTF-8"')) {
                $data = utf8_decode($data);
            }
            $xp = xml_parser_create('ISO-8859-1');
        } else {
            if (strpos($data, 'encoding="UTF-8"')) {
                $xp = xml_parser_create('UTF-8');
            } else {
                $xp = xml_parser_create('ISO-8859-1');
            }
        }
        xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, 0);
        xml_set_object($xp, $this);
        xml_set_element_handler($xp, 'startHandler', 'endHandler');
        xml_set_character_data_handler($xp, 'cdataHandler');
        if (!xml_parse($xp, $data)) {
            $msg = xml_error_string(xml_get_error_code($xp));
            $line = xml_get_current_line_number($xp);
            xml_parser_free($xp);
            include_once 'PEAR.php';
            return PEAR::raiseError("XML Error: '$msg' on line '$line'", 2);
        }
        xml_parser_free($xp);
        return true;
    }

    /**
     * Start element handler for XML parser
     *
     * @access private
     * @param  object $parser  XML parser object
     * @param  string $element XML element
     * @param  array  $attribs attributes of XML tag
     * @return void
     */
    function startHandler($parser, $element, $attribs)
    {
        $type = 'string';

        $this->_depth++;
        $this->_dataStack[$this->_depth] = null;

        $val = array(
                     'name'         => $element,
                     'value'        => null,
                     'type'         => $type,
                     'childrenKeys' => array(),
                     'aggregKeys'   => array()
                    );

        if (count($attribs) > 0) {
            $val['children'] = array();
            $val['type'] = 'array';

            $val['children']['attribs'] = $attribs;

        }

        array_push($this->_valStack, $val);
    }

    /**
     * post-process data
     *
     * @param string $data
     * @param string $element element name
     */
    function postProcess($data, $element)
    {
        return trim($data);
    }

    /**
     * End element handler for XML parser
     *
     * @access private
     * @param  object XML parser object
     * @param  string
     * @return void
     */
    function endHandler($parser, $element)
    {
        $value = array_pop($this->_valStack);
        $data  = $this->postProcess($this->_dataStack[$this->_depth], $element);

        // adjust type of the value
        switch(strtolower($value['type'])) {

            /*
             * unserialize an array
             */
            case 'array':
                if ($data !== '') {
                    $value['children']['_content'] = $data;
                }
                if (isset($value['children'])) {
                    $value['value'] = $value['children'];
                } else {
                    $value['value'] = array();
                }
                break;

            /*
             * unserialize a null value
             */
            case 'null':
                $data = null;
                break;

            /*
             * unserialize any scalar value
             */
            default:
                settype($data, $value['type']);
                $value['value'] = $data;
                break;
        }
        $parent = array_pop($this->_valStack);
        if ($parent === null) {
            $this->_unserializedData = &$value['value'];
            $this->_root = &$value['name'];
            return true;
        } else {
            // parent has to be an array
            if (!isset($parent['children']) || !is_array($parent['children'])) {
                $parent['children'] = array();
                if ($parent['type'] != 'array') {
                    $parent['type'] = 'array';
                }
            }

            if (!empty($value['name'])) {
                // there already has been a tag with this name
                if (in_array($value['name'], $parent['childrenKeys'])) {
                    // no aggregate has been created for this tag
                    if (!in_array($value['name'], $parent['aggregKeys'])) {
                        if (isset($parent['children'][$value['name']])) {
                            $parent['children'][$value['name']] = array($parent['children'][$value['name']]);
                        } else {
                            $parent['children'][$value['name']] = array();
                        }
                        array_push($parent['aggregKeys'], $value['name']);
                    }
                    array_push($parent['children'][$value['name']], $value['value']);
                } else {
                    $parent['children'][$value['name']] = &$value['value'];
                    array_push($parent['childrenKeys'], $value['name']);
                }
            } else {
                array_push($parent['children'],$value['value']);
            }
            array_push($this->_valStack, $parent);
        }

        $this->_depth--;
    }

    /**
     * Handler for character data
     *
     * @access private
     * @param  object XML parser object
     * @param  string CDATA
     * @return void
     */
    function cdataHandler($parser, $cdata)
    {
        $this->_dataStack[$this->_depth] .= $cdata;
    }
}
?>PEAR-1.7.2/scripts/pear.bat100755   1750   1750       11102 11014312742  10611 @ECHO OFF

REM ----------------------------------------------------------------------
REM PHP version 5
REM ----------------------------------------------------------------------
REM Copyright (c) 1997-2004 The PHP Group
REM ----------------------------------------------------------------------
REM  This source file is subject to version 3.0 of the PHP license, 
REM  that is bundled with this package in the file LICENSE, and is
REM  available at through the world-wide-web at
REM  http://www.php.net/license/3_0.txt. 
REM  If you did not receive a copy of the PHP license and are unable to
REM  obtain it through the world-wide-web, please send a note to
REM  license@php.net so we can mail you a copy immediately.
REM ----------------------------------------------------------------------
REM  Authors:     Alexander Merz (alexmerz@php.net)
REM ----------------------------------------------------------------------
REM
REM  Last updated 12/29/2004 ($Id$ is not replaced if the file is binary)

REM change this lines to match the paths of your system
REM -------------------


REM Test to see if this is a raw pear.bat (uninstalled version)
SET TMPTMPTMPTMPT=@includ
SET PMTPMTPMT=%TMPTMPTMPTMPT%e_path@
FOR %%x IN ("@include_path@") DO (if %%x=="%PMTPMTPMT%" GOTO :NOTINSTALLED)

REM 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@"
GOTO :INSTALLED

:NOTINSTALLED
ECHO WARNING: This is a raw, uninstalled pear.bat

REM Check to see if we can grab the directory of this file (Windows NT+)
IF %~n0 == pear (
FOR %%x IN (cli\php.exe php.exe) DO (if "%%~$PATH:x" NEQ "" (
SET "PHP_PEAR_PHP_BIN=%%~$PATH:x"
echo Using PHP Executable "%PHP_PEAR_PHP_BIN%"
"%PHP_PEAR_PHP_BIN%" -v
GOTO :NEXTTEST
))
GOTO :FAILAUTODETECT
:NEXTTEST
IF "%PHP_PEAR_PHP_BIN%" NEQ "" (

REM We can use this PHP to run a temporary php file to get the dirname of pear

echo ^<?php $s=getcwd^(^);chdir^($a=dirname^(__FILE__^).'\\'^);if^(stristr^($a,'\\scripts'^)^)$a=dirname^(dirname^($a^)^).'\\';$f=fopen^($s.'\\~a.a','wb'^);echo$s.'\\~a.a';fwrite^($f,$a^);fclose^($f^);chdir^($s^);?^> > ~~getloc.php
"%PHP_PEAR_PHP_BIN%" ~~getloc.php
set /p PHP_PEAR_BIN_DIR=fakeprompt < ~a.a
DEL ~a.a
DEL ~~getloc.php
set "PHP_PEAR_INSTALL_DIR=%PHP_PEAR_BIN_DIR%pear"

REM Make sure there is a pearcmd.php at our disposal

IF NOT EXIST %PHP_PEAR_INSTALL_DIR%\pearcmd.php (
IF EXIST %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php COPY %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
IF EXIST pearcmd.php COPY pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
IF EXIST %~dp0\scripts\pearcmd.php COPY %~dp0\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
)
)
GOTO :INSTALLED
) ELSE (
REM Windows Me/98 cannot succeed, so allow the batch to fail
)
:FAILAUTODETECT
echo WARNING: failed to auto-detect pear information
:INSTALLED

REM 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
REM launch pearcmd
GOTO RUN
:PEAR_INSTALL_ERROR
ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
ECHO Please fix it using your environment variable or modify
ECHO the default value in pear.bat
ECHO The current value is:
ECHO %PHP_PEAR_INSTALL_DIR%
GOTO END
:PEAR_INSTALL_ERROR2
ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
ECHO pearcmd.php could not be found there.
ECHO Please fix it using your environment variable or modify
ECHO the default value in pear.bat
ECHO The current value is:
ECHO %PHP_PEAR_INSTALL_DIR%
GOTO END
:PEAR_BIN_ERROR
ECHO PHP_PEAR_BIN_DIR is not set correctly.
ECHO Please fix it using your environment variable or modify
ECHO the default value in pear.bat
ECHO The current value is:
ECHO %PHP_PEAR_BIN_DIR%
GOTO END
:PEAR_PHPBIN_ERROR
ECHO PHP_PEAR_PHP_BIN is not set correctly.
ECHO Please fix it using your environment variable or modify
ECHO the default value in pear.bat
ECHO The current value is:
ECHO %PHP_PEAR_PHP_BIN%
GOTO END
:RUN
"%PHP_PEAR_PHP_BIN%" -C -d output_buffering=1 -d safe_mode=0 -d open_basedir="" -d auto_prepend_file="" -d auto_append_file="" -d variables_order=EGPCS -d register_argc_argv="On" -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 ONPEAR-1.7.2/scripts/peardev.bat100644   1750   1750       11112 11014312742  11306 @ECHO OFF

REM ----------------------------------------------------------------------
REM PHP version 5
REM ----------------------------------------------------------------------
REM Copyright (c) 1997-2004 The PHP Group
REM ----------------------------------------------------------------------
REM  This source file is subject to version 3.0 of the PHP license, 
REM  that is bundled with this package in the file LICENSE, and is
REM  available at through the world-wide-web at
REM  http://www.php.net/license/3_0.txt. 
REM  If you did not receive a copy of the PHP license and are unable to
REM  obtain it through the world-wide-web, please send a note to
REM  license@php.net so we can mail you a copy immediately.
REM ----------------------------------------------------------------------
REM  Authors:     Alexander Merz (alexmerz@php.net)
REM ----------------------------------------------------------------------
REM
REM  $Id: peardev.bat,v 1.6 2007/09/03 03:00:17 cellog Exp $

REM change this lines to match the paths of your system
REM -------------------


REM Test to see if this is a raw pear.bat (uninstalled version)
SET TMPTMPTMPTMPT=@includ
SET PMTPMTPMT=%TMPTMPTMPTMPT%e_path@
FOR %%x IN ("@include_path@") DO (if %%x=="%PMTPMTPMT%" GOTO :NOTINSTALLED)

REM 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@"
GOTO :INSTALLED

:NOTINSTALLED
ECHO WARNING: This is a raw, uninstalled pear.bat

REM Check to see if we can grab the directory of this file (Windows NT+)
IF %~n0 == pear (
FOR %%x IN (cli\php.exe php.exe) DO (if "%%~$PATH:x" NEQ "" (
SET "PHP_PEAR_PHP_BIN=%%~$PATH:x"
echo Using PHP Executable "%PHP_PEAR_PHP_BIN%"
"%PHP_PEAR_PHP_BIN%" -v
GOTO :NEXTTEST
))
GOTO :FAILAUTODETECT
:NEXTTEST
IF "%PHP_PEAR_PHP_BIN%" NEQ "" (

REM We can use this PHP to run a temporary php file to get the dirname of pear

echo ^<?php $s=getcwd^(^);chdir^($a=dirname^(__FILE__^).'\\'^);if^(stristr^($a,'\\scripts'^)^)$a=dirname^(dirname^($a^)^).'\\';$f=fopen^($s.'\\~a.a','wb'^);echo$s.'\\~a.a';fwrite^($f,$a^);fclose^($f^);chdir^($s^);?^> > ~~getloc.php
"%PHP_PEAR_PHP_BIN%" ~~getloc.php
set /p PHP_PEAR_BIN_DIR=fakeprompt < ~a.a
DEL ~a.a
DEL ~~getloc.php
set "PHP_PEAR_INSTALL_DIR=%PHP_PEAR_BIN_DIR%pear"

REM Make sure there is a pearcmd.php at our disposal

IF NOT EXIST %PHP_PEAR_INSTALL_DIR%\pearcmd.php (
IF EXIST %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php COPY %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
IF EXIST pearcmd.php COPY pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
IF EXIST %~dp0\scripts\pearcmd.php COPY %~dp0\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
)
)
GOTO :INSTALLED
) ELSE (
REM Windows Me/98 cannot succeed, so allow the batch to fail
)
:FAILAUTODETECT
echo WARNING: failed to auto-detect pear information
:INSTALLED

REM 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
REM launch pearcmd
GOTO RUN
:PEAR_INSTALL_ERROR
ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
ECHO Please fix it using your environment variable or modify
ECHO the default value in pear.bat
ECHO The current value is:
ECHO %PHP_PEAR_INSTALL_DIR%
GOTO END
:PEAR_INSTALL_ERROR2
ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
ECHO pearcmd.php could not be found there.
ECHO Please fix it using your environment variable or modify
ECHO the default value in pear.bat
ECHO The current value is:
ECHO %PHP_PEAR_INSTALL_DIR%
GOTO END
:PEAR_BIN_ERROR
ECHO PHP_PEAR_BIN_DIR is not set correctly.
ECHO Please fix it using your environment variable or modify
ECHO the default value in pear.bat
ECHO The current value is:
ECHO %PHP_PEAR_BIN_DIR%
GOTO END
:PEAR_PHPBIN_ERROR
ECHO PHP_PEAR_PHP_BIN is not set correctly.
ECHO Please fix it using your environment variable or modify
ECHO the default value in pear.bat
ECHO The current value is:
ECHO %PHP_PEAR_PHP_BIN%
GOTO END
:RUN
"%PHP_PEAR_PHP_BIN%" -C -d memory_limit="-1" -d safe_mode=0 -d register_argc_argv="On" -d auto_prepend_file="" -d auto_append_file="" -d variables_order=EGPCS -d open_basedir="" -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 ONPEAR-1.7.2/scripts/pecl.bat100644   1750   1750       11003 11014312742  10602 @ECHO OFF

REM ----------------------------------------------------------------------
REM PHP version 5
REM ----------------------------------------------------------------------
REM Copyright (c) 1997-2004 The PHP Group
REM ----------------------------------------------------------------------
REM  This source file is subject to version 3.0 of the PHP license, 
REM  that is bundled with this package in the file LICENSE, and is
REM  available at through the world-wide-web at
REM  http://www.php.net/license/3_0.txt. 
REM  If you did not receive a copy of the PHP license and are unable to
REM  obtain it through the world-wide-web, please send a note to
REM  license@php.net so we can mail you a copy immediately.
REM ----------------------------------------------------------------------
REM  Authors:     Alexander Merz (alexmerz@php.net)
REM ----------------------------------------------------------------------
REM
REM  Last updated 02/08/2004 ($Id$ is not replaced if the file is binary)

REM change this lines to match the paths of your system
REM -------------------


REM Test to see if this is a raw pear.bat (uninstalled version)
SET TMPTMPTMPTMPT=@includ
SET PMTPMTPMT=%TMPTMPTMPTMPT%e_path@
FOR %%x IN ("@include_path@") DO (if %%x=="%PMTPMTPMT%" GOTO :NOTINSTALLED)

REM 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@"
GOTO :INSTALLED

:NOTINSTALLED
ECHO WARNING: This is a raw, uninstalled pear.bat

REM Check to see if we can grab the directory of this file (Windows NT+)
IF %~n0 == pear (
FOR %%x IN (cli\php.exe php.exe) DO (if "%%~$PATH:x" NEQ "" (
SET "PHP_PEAR_PHP_BIN=%%~$PATH:x"
echo Using PHP Executable "%PHP_PEAR_PHP_BIN%"
"%PHP_PEAR_PHP_BIN%" -v
GOTO :NEXTTEST
))
GOTO :FAILAUTODETECT
:NEXTTEST
IF "%PHP_PEAR_PHP_BIN%" NEQ "" (

REM We can use this PHP to run a temporary php file to get the dirname of pear

echo ^<?php $s=getcwd^(^);chdir^($a=dirname^(__FILE__^).'\\'^);if^(stristr^($a,'\\scripts'^)^)$a=dirname^(dirname^($a^)^).'\\';$f=fopen^($s.'\\~a.a','wb'^);echo$s.'\\~a.a';fwrite^($f,$a^);fclose^($f^);chdir^($s^);?^> > ~~getloc.php
"%PHP_PEAR_PHP_BIN%" ~~getloc.php
set /p PHP_PEAR_BIN_DIR=fakeprompt < ~a.a
DEL ~a.a
DEL ~~getloc.php
set "PHP_PEAR_INSTALL_DIR=%PHP_PEAR_BIN_DIR%pear"

REM Make sure there is a pearcmd.php at our disposal

IF NOT EXIST %PHP_PEAR_INSTALL_DIR%\pearcmd.php (
IF EXIST %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php COPY %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
IF EXIST pearcmd.php COPY pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
IF EXIST %~dp0\scripts\pearcmd.php COPY %~dp0\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
)
)
GOTO :INSTALLED
) ELSE (
REM Windows Me/98 cannot succeed, so allow the batch to fail
)
:FAILAUTODETECT
echo WARNING: failed to auto-detect pear information
:INSTALLED

REM 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
REM launch pearcmd
GOTO RUN
:PEAR_INSTALL_ERROR
ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
ECHO Please fix it using your environment variable or modify
ECHO the default value in pear.bat
ECHO The current value is:
ECHO %PHP_PEAR_INSTALL_DIR%
GOTO END
:PEAR_INSTALL_ERROR2
ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
ECHO pearcmd.php could not be found there.
ECHO Please fix it using your environment variable or modify
ECHO the default value in pear.bat
ECHO The current value is:
ECHO %PHP_PEAR_INSTALL_DIR%
GOTO END
:PEAR_BIN_ERROR
ECHO PHP_PEAR_BIN_DIR is not set correctly.
ECHO Please fix it using your environment variable or modify
ECHO the default value in pear.bat
ECHO The current value is:
ECHO %PHP_PEAR_BIN_DIR%
GOTO END
:PEAR_PHPBIN_ERROR
ECHO PHP_PEAR_PHP_BIN is not set correctly.
ECHO Please fix it using your environment variable or modify
ECHO the default value in pear.bat
ECHO The current value is:
ECHO %PHP_PEAR_PHP_BIN%
GOTO END
:RUN
"%PHP_PEAR_PHP_BIN%" -C -n -d output_buffering=1 -d safe_mode=0 -d include_path="%PHP_PEAR_INSTALL_DIR%" -d register_argc_argv="On" -d variables_order=EGPCS -f "%PHP_PEAR_INSTALL_DIR%\peclcmd.php" -- %1 %2 %3 %4 %5 %6 %7 %8 %9
:END
@ECHO ONPEAR-1.7.2/scripts/pear.sh100644   1750   1750        1362 11014312742  10441 #!/bin/sh

# first find which PHP binary to use
if test "x$PHP_PEAR_PHP_BIN" != "x"; then
  PHP="$PHP_PEAR_PHP_BIN"
else
  if test "@php_bin@" = '@'php_bin'@'; then
    PHP=php 
  else
    PHP="@php_bin@"
  fi
fi

# then look for the right pear include dir
if test "x$PHP_PEAR_INSTALL_DIR" != "x"; then
  INCDIR=$PHP_PEAR_INSTALL_DIR
  INCARG="-d include_path=$PHP_PEAR_INSTALL_DIR"
else
  if test "@php_dir@" = '@'php_dir'@'; then
    INCDIR=`dirname $0`
    INCARG=""  
  else
    INCDIR="@php_dir@"
    INCARG="-d include_path=@php_dir@"
  fi
fi

exec $PHP -C -q $INCARG -d output_buffering=1 -d variables_order=EGPCS -d open_basedir="" -d safe_mode=0 -d register_argc_argv="On" -d auto_prepend_file="" -d auto_append_file="" $INCDIR/pearcmd.php "$@"
PEAR-1.7.2/scripts/peardev.sh100644   1750   1750        1407 11014312742  11140 #!/bin/sh

# first find which PHP binary to use
if test "x$PHP_PEAR_PHP_BIN" != "x"; then
  PHP="$PHP_PEAR_PHP_BIN"
else
  if test "@php_bin@" = '@'php_bin'@'; then
    PHP=php 
  else
    PHP="@php_bin@"
  fi
fi

# then look for the right pear include dir
if test "x$PHP_PEAR_INSTALL_DIR" != "x"; then
  INCDIR=$PHP_PEAR_INSTALL_DIR
  INCARG="-d include_path=$PHP_PEAR_INSTALL_DIR"
else
  if test "@php_dir@" = '@'php_dir'@'; then
    INCDIR=`dirname $0`
    INCARG=""  
  else
    INCDIR="@php_dir@"
    INCARG="-d include_path=@php_dir@"
  fi
fi

exec $PHP -d memory_limit="-1" -C -q $INCARG -d output_buffering=1 -d open_basedir="" -d safe_mode=0 -d register_argc_argv="On" -d auto_prepend_file="" -d variables_order=EGPCS -d auto_append_file="" $INCDIR/pearcmd.php "$@"
PEAR-1.7.2/scripts/pecl.sh100644   1750   1750        1263 11014312742  10435 #!/bin/sh

# first find which PHP binary to use
if test "x$PHP_PEAR_PHP_BIN" != "x"; then
  PHP="$PHP_PEAR_PHP_BIN"
else
  if test "@php_bin@" = '@'php_bin'@'; then
    PHP=php 
  else
    PHP="@php_bin@"
  fi
fi

# then look for the right pear include dir
if test "x$PHP_PEAR_INSTALL_DIR" != "x"; then
  INCDIR=$PHP_PEAR_INSTALL_DIR
  INCARG="-d include_path=$PHP_PEAR_INSTALL_DIR"
else
  if test "@php_dir@" = '@'php_dir'@'; then
    INCDIR=`dirname $0`
    INCARG=""  
  else
    INCDIR="@php_dir@"
    INCARG="-d include_path=@php_dir@"
  fi
fi

exec $PHP -C -n -q $INCARG -d output_buffering=1 -d variables_order=EGPCS -d safe_mode=0 -d register_argc_argv="On" $INCDIR/peclcmd.php "$@"
PEAR-1.7.2/scripts/pearcmd.php100644   1750   1750       36342 11014312742  11330 <?php
//
// +----------------------------------------------------------------------+
// | PHP Version 5                                                        |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2004 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>                             |
// |                                                                      |
// +----------------------------------------------------------------------+
//
// $Id: pearcmd.php,v 1.39 2008/03/25 04:58:42 cellog Exp $

ob_end_clean();
if (!defined('PEAR_RUNTYPE')) {
    // this is defined in peclcmd.php as 'pecl'
    define('PEAR_RUNTYPE', 'pear');
}
define('PEAR_IGNORE_BACKTRACE', 1);
/**
 * @nodep Gtk
 */
if ('@include_path@' != '@'.'include_path'.'@') {
    ini_set('include_path', '@include_path@');
    $raw = false;
} else {
    // this is a raw, uninstalled pear, either a cvs checkout, or php distro
    $raw = true;
}
@ini_set('allow_url_fopen', true);
if (!ini_get('safe_mode')) {
    @set_time_limit(0);
}
ob_implicit_flush(true);
@ini_set('track_errors', true);
@ini_set('html_errors', false);
@ini_set('magic_quotes_runtime', false);
$_PEAR_PHPDIR = '#$%^&*';
set_error_handler('error_handler');

$pear_package_version = "@pear_version@";

require_once 'PEAR.php';
require_once 'PEAR/Frontend.php';
require_once 'PEAR/Config.php';
require_once 'PEAR/Command.php';
require_once 'Console/Getopt.php';


PEAR_Command::setFrontendType('CLI');
$all_commands = PEAR_Command::getCommands();

// remove this next part when we stop supporting that crap-ass PHP 4.2
if (!isset($_SERVER['argv']) && !isset($argv) && !isset($HTTP_SERVER_VARS['argv'])) {
    echo 'ERROR: either use the CLI php executable, or set register_argc_argv=On in php.ini';
    exit(1);
}
$argv = Console_Getopt::readPHPArgv();
// fix CGI sapi oddity - the -- in pear.bat/pear is not removed
if (php_sapi_name() != 'cli' && isset($argv[1]) && $argv[1] == '--') {
    unset($argv[1]);
    $argv = array_values($argv);
}
$progname = PEAR_RUNTYPE;
if (in_array('getopt2', get_class_methods('Console_Getopt'))) {
    array_shift($argv);
    $options = Console_Getopt::getopt2($argv, "c:C:d:D:Gh?sSqu:vV");
} else {
    $options = Console_Getopt::getopt($argv, "c:C:d:D:Gh?sSqu:vV");
}
if (PEAR::isError($options)) {
    usage($options);
}

$opts = $options[0];

$fetype = 'CLI';
if ($progname == 'gpear' || $progname == 'pear-gtk') {
    $fetype = 'Gtk';
} else {
    foreach ($opts as $opt) {
        if ($opt[0] == 'G') {
            $fetype = 'Gtk';
        }
    }
}
//Check if Gtk and PHP >= 5.1.0
if ($fetype == 'Gtk' && version_compare(phpversion(), '5.1.0', '>=')) {
    $fetype = 'Gtk2';
}

$pear_user_config = '';
$pear_system_config = '';
$store_user_config = false;
$store_system_config = false;
$verbose = 1;

foreach ($opts as $opt) {
    switch ($opt[0]) {
        case 'c':
            $pear_user_config = $opt[1];
            break;
        case 'C':
            $pear_system_config = $opt[1];
            break;
    }
}

PEAR_Command::setFrontendType($fetype);
$ui = &PEAR_Command::getFrontendObject();
$config = &PEAR_Config::singleton($pear_user_config, $pear_system_config);

if (PEAR::isError($config)) {
    $_file = '';
    if ($pear_user_config !== false) {
       $_file .= $pear_user_config;
    }
    if ($pear_system_config !== false) {
       $_file .= '/' . $pear_system_config;
    }
    if ($_file == '/') {
        $_file = 'The default config file';
    }
    $config->getMessage();
    $ui->outputData("ERROR: $_file is not a valid config file or is corrupted.");
    // We stop, we have no idea where we are :)
    exit(1);
}

// this is used in the error handler to retrieve a relative path
$_PEAR_PHPDIR = $config->get('php_dir');
$ui->setConfig($config);
PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, array($ui, "displayFatalError"));
if (ini_get('safe_mode')) {
    $ui->outputData('WARNING: running in safe mode requires that all files created ' .
        'be the same uid as the current script.  PHP reports this script is uid: ' .
        @getmyuid() . ', and current user is: ' . @get_current_user());
}

$verbose = $config->get("verbose");
$cmdopts = array();

if ($raw) {
    if (!$config->isDefinedLayer('user') && !$config->isDefinedLayer('system')) {
        $found = false;
        foreach ($opts as $opt) {
            if ($opt[0] == 'd' || $opt[0] == 'D') {
                $found = true; // the user knows what they are doing, and are setting config values
            }
        }
        if (!$found) {
            // no prior runs, try to install PEAR
            if (strpos(dirname(__FILE__), 'scripts')) {
                $packagexml = dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'package2.xml';
                $pearbase = dirname(dirname(__FILE__));
            } else {
                $packagexml = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'package2.xml';
                $pearbase = dirname(__FILE__);
            }
            if (file_exists($packagexml)) {
                $options[1] = array(
                    'install',
                    $packagexml
                );
                $config->set('php_dir', $pearbase . DIRECTORY_SEPARATOR . 'php');
                $config->set('data_dir', $pearbase . DIRECTORY_SEPARATOR . 'data');
                $config->set('doc_dir', $pearbase . DIRECTORY_SEPARATOR . 'docs');
                $config->set('test_dir', $pearbase . DIRECTORY_SEPARATOR . 'tests');
                $config->set('ext_dir', $pearbase . DIRECTORY_SEPARATOR . 'extensions');
                $config->set('bin_dir', $pearbase);
                $config->mergeConfigFile($pearbase . 'pear.ini', false);
                $config->store();
                $config->set('auto_discover', 1);
            }
        }
    }
}
foreach ($opts as $opt) {
    $param = !empty($opt[1]) ? $opt[1] : true;
    switch ($opt[0]) {
        case 'd':
            if ($param === true) {
                die('Invalid usage of "-d" option, expected -d config_value=value, ' .
                    'received "-d"' . "\n");
            }
            $possible = explode('=', $param);
            if (count($possible) != 2) {
                die('Invalid usage of "-d" option, expected -d config_value=value, received "' .
                    $param . '"' . "\n");
            }
            list($key, $value) = explode('=', $param);
            $config->set($key, $value, 'user');
            break;
        case 'D':
            if ($param === true) {
                die('Invalid usage of "-d" option, expected -d config_value=value, ' .
                    'received "-d"' . "\n");
            }
            $possible = explode('=', $param);
            if (count($possible) != 2) {
                die('Invalid usage of "-d" option, expected -d config_value=value, received "' .
                    $param . '"' . "\n");
            }
            list($key, $value) = explode('=', $param);
            $config->set($key, $value, 'system');
            break;
        case 's':
            $store_user_config = true;
            break;
        case 'S':
            $store_system_config = true;
            break;
        case 'u':
            $config->remove($param, 'user');
            break;
        case 'v':
            $config->set('verbose', $config->get('verbose') + 1);
            break;
        case 'q':
            $config->set('verbose', $config->get('verbose') - 1);
            break;
        case 'V':
            usage(null, 'version');
        case 'c':
        case 'C':
            break;
        default:
            // all non pear params goes to the command
            $cmdopts[$opt[0]] = $param;
            break;
    }
}

if ($store_system_config) {
    $config->store('system');
}

if ($store_user_config) {
    $config->store('user');
}

$command = (isset($options[1][0])) ? $options[1][0] : null;

if (empty($command) && ($store_user_config || $store_system_config)) {
    exit;
}

if ($fetype == 'Gtk' || $fetype == 'Gtk2') {
    if (!$config->validConfiguration()) {
        PEAR::raiseError('CRITICAL ERROR: no existing valid configuration files found in files ' .
            "'$pear_user_config' or '$pear_system_config', please copy an existing configuration" .
            'file to one of these locations, or use the -c and -s options to create one');
    }
    Gtk::main();
} else do {
    if ($command == 'help') {
        usage(null, @$options[1][1]);
    }
    if (!$config->validConfiguration()) {
        PEAR::raiseError('CRITICAL ERROR: no existing valid configuration files found in files ' .
            "'$pear_user_config' or '$pear_system_config', please copy an existing configuration" .
            'file to one of these locations, or use the -c and -s options to create one');
    }

    PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
    $cmd = PEAR_Command::factory($command, $config);
    PEAR::popErrorHandling();
    if (PEAR::isError($cmd)) {
        usage(null, @$options[1][0]);
    }

    $short_args = $long_args = null;
    PEAR_Command::getGetoptArgs($command, $short_args, $long_args);
    if (in_array('getopt2', get_class_methods('Console_Getopt'))) {
        array_shift($options[1]);
        $tmp = Console_Getopt::getopt2($options[1], $short_args, $long_args);
    } else {
        $tmp = Console_Getopt::getopt($options[1], $short_args, $long_args);
    }
    if (PEAR::isError($tmp)) {
        break;
    }
    list($tmpopt, $params) = $tmp;
    $opts = array();
    foreach ($tmpopt as $foo => $tmp2) {
        list($opt, $value) = $tmp2;
        if ($value === null) {
            $value = true; // options without args
        }
        if (strlen($opt) == 1) {
            $cmdoptions = $cmd->getOptions($command);
            foreach ($cmdoptions as $o => $d) {
                if (@$d['shortopt'] == $opt) {
                    $opts[$o] = $value;
                }
            }
        } else {
            if (substr($opt, 0, 2) == '--') {
                $opts[substr($opt, 2)] = $value;
            }
        }
    }
    $ok = $cmd->run($command, $opts, $params);
    if ($ok === false) {
        PEAR::raiseError("unknown command `$command'");
    }
    if (PEAR::isError($ok)) {
        PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, array($ui, "displayFatalError"));
        PEAR::raiseError($ok);
    }
} while (false);

// {{{ usage()

function usage($error = null, $helpsubject = null)
{
    global $progname, $all_commands;
    $stderr = fopen('php://stderr', 'w');
    if (PEAR::isError($error)) {
        fputs($stderr, $error->getMessage() . "\n");
    } elseif ($error !== null) {
        fputs($stderr, "$error\n");
    }
    if ($helpsubject != null) {
        $put = cmdHelp($helpsubject);
    } else {
        $put =
            "Commands:\n";
        $maxlen = max(array_map("strlen", $all_commands));
        $formatstr = "%-{$maxlen}s  %s\n";
        ksort($all_commands);
        foreach ($all_commands as $cmd => $class) {
            $put .= sprintf($formatstr, $cmd, PEAR_Command::getDescription($cmd));
        }
        $put .=
            "Usage: $progname [options] command [command-options] <parameters>\n".
            "Type \"$progname help options\" to list all options.\n".
            "Type \"$progname help shortcuts\" to list all command shortcuts.\n".
            "Type \"$progname help <command>\" to get the help for the specified command.";
    }
    fputs($stderr, "$put\n");
    fclose($stderr);
    exit(1);
}

function cmdHelp($command)
{
    global $progname, $all_commands, $config;
    if ($command == "options") {
        return
        "Options:\n".
        "     -v         increase verbosity level (default 1)\n".
        "     -q         be quiet, decrease verbosity level\n".
        "     -c file    find user configuration in `file'\n".
        "     -C file    find system configuration in `file'\n".
        "     -d foo=bar set user config variable `foo' to `bar'\n".
        "     -D foo=bar set system config variable `foo' to `bar'\n".
        "     -G         start in graphical (Gtk) mode\n".
        "     -s         store user configuration\n".
        "     -S         store system configuration\n".
        "     -u foo     unset `foo' in the user configuration\n".
        "     -h, -?     display help/usage (this message)\n".
        "     -V         version information\n";
    } elseif ($command == "shortcuts") {
        $sc = PEAR_Command::getShortcuts();
        $ret = "Shortcuts:\n";
        foreach ($sc as $s => $c) {
            $ret .= sprintf("     %-8s %s\n", $s, $c);
        }
        return $ret;

    } elseif ($command == "version") {
        return "PEAR Version: ".$GLOBALS['pear_package_version'].
               "\nPHP Version: ".phpversion().
               "\nZend Engine Version: ".zend_version().
               "\nRunning on: ".php_uname();

    } elseif ($help = PEAR_Command::getHelp($command)) {
        if (is_string($help)) {
            return "$progname $command [options] $help\n";
        }
        if ($help[1] === null) {
            return "$progname $command $help[0]";
        } else {
            return "$progname $command [options] $help[0]\n$help[1]";
        }
    }
    return "Command '$command' is not valid, try '$progname help'";
}

// }}}

function error_handler($errno, $errmsg, $file, $line, $vars) {
    if ((defined('E_STRICT') && $errno & E_STRICT) || (defined('E_DEPRECATED') &&
          $errno & E_DEPRECATED) || !error_reporting()) {
        if (defined('E_STRICT') && $errno & E_STRICT) {
            return; // E_STRICT
        }
        if (defined('E_DEPRECATED') && $errno & E_DEPRECATED) {
            return; // E_DEPRECATED
        }
        if ($GLOBALS['config']->get('verbose') < 4) {
            return false; // @silenced error, show all if debug is high enough
        }
    }
    $errortype = array (
        E_ERROR   =>  "Error",
        E_WARNING   =>  "Warning",
        E_PARSE   =>  "Parsing Error",
        E_NOTICE   =>  "Notice",
        E_CORE_ERROR  =>  "Core Error",
        E_CORE_WARNING  =>  "Core Warning",
        E_COMPILE_ERROR  =>  "Compile Error",
        E_COMPILE_WARNING =>  "Compile Warning",
        E_USER_ERROR =>  "User Error",
        E_USER_WARNING =>  "User Warning",
        E_USER_NOTICE =>  "User Notice"
    );
    $prefix = $errortype[$errno];
    global $_PEAR_PHPDIR;
    if (stristr($file, $_PEAR_PHPDIR)) {
        $file = substr($file, strlen($_PEAR_PHPDIR) + 1);
    } else {
        $file = basename($file);
    }
    print "\n$prefix: $errmsg in $file on line $line\n";
    return false;
}


/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * indent-tabs-mode: nil
 * mode: php
 * End:
 */
// vim600:syn=php

?>
PEAR-1.7.2/scripts/peclcmd.php100644   1750   1750        3406 11014312742  11277 <?php
//
// +----------------------------------------------------------------------+
// | PHP Version 5                                                        |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2004 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>                             |
// |                                                                      |
// +----------------------------------------------------------------------+
//
// $Id: peclcmd.php,v 1.1 2005/02/21 05:30:56 cellog Exp $

/**
 * @nodep Gtk
 */
if ('@include_path@' != '@'.'include_path'.'@') {
    ini_set('include_path', '@include_path@');
    $raw = false;
} else {
    // this is a raw, uninstalled pear, either a cvs checkout, or php distro
    $raw = true;
}
define('PEAR_RUNTYPE', 'pecl');
require_once 'pearcmd.php';
/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * indent-tabs-mode: nil
 * mode: php
 * End:
 */
// vim600:syn=php

?>
PEAR-1.7.2/INSTALL100644   1750   1750        3643 11014312742   6524 PEAR - The PEAR Installer
=========================
Installing the PEAR Installer.

You should install PEAR on a local development machine first.  Installing
PEAR on a remote production machine should only be done after you are
familiar with PEAR and have tested code using PEAR on your development
machine.

There are two methods of installing PEAR
 - PEAR bundled in PHP
 - go-pear

We will first examine how to install PEAR that is bundled with PHP.

Microsoft Windows
=================
If you are running PHP 5.2.0 or newer, simply download and
run the windows installer (.msi) and PEAR can be automatically
installed.

Otherwise, for older PHP versions, download the .zip of windows,
there is a script included with your PHP distribution that is called
"go-pear".  You must open a command box in order to run it.  Click
"start" then click "Run..." and type "cmd.exe" to open a command box.
Use "cd" to change directory to the location of PHP where you unzipped it,
and run the go-pear command.

Unix
====
make sure you have enabled default extensions, and if you want faster
downloads, enable the zlib extension.  You must also enable the CLI
SAPI with the --enable-cli extension directive.  After this, simply run:

make install-pear

and PEAR will be automatically configured for you.

go-pear
=======
For users who cannot perform the above steps, or who wish to obtain the
latest PEAR with a slightly higher risk of failure, use go-pear.  go-pear
is obtained by downloading http://go-pear.org and saving it as go-pear.php.
After downloading, simply run "php go-pear.php" or open it in a web browser
(windows only) to download and install PEAR.

You can always ask general installation questions on pear-general@lists.php.net,
a public mailing list devoted to support for PEAR packages and installation-
related issues.

Happy PHPing, we hope PEAR will be a great tool for your development work!

$Id: INSTALL,v 1.1 2006/09/22 03:31:36 cellog Exp $PEAR-1.7.2/package.dtd100644   1750   1750        6477 11014312742   7573 <!--
     $Id: package.dtd,v 1.38 2005/11/12 02:23:07 cellog Exp $

     This is the PEAR package description, version 1.0.
     It should be used with the informal public identifier:

         "-//PHP Group//DTD PEAR Package 1.0//EN//XML"

     Copyright (c) 1997-2005 The PHP Group             

     This source file is subject to version 3.00 of the PHP license,
     that is bundled with this package in the file LICENSE, and is
     available at through the world-wide-web at
     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 S. Bakken <ssb@fast.no>
         Gregory Beaver <cellog@php.net>

  -->
<!ENTITY % NUMBER "CDATA">
<!ELEMENT package (name,summary,description,license?,maintainers,release,changelog?)>
<!ATTLIST package type    (source|binary|empty) "empty"
                  version CDATA                 #REQUIRED
                  packagerversion CDATA         #IMPLIED>

<!ELEMENT name (#PCDATA)>

<!ELEMENT summary (#PCDATA)>

<!ELEMENT license (#PCDATA)>

<!ELEMENT description (#PCDATA)>

<!ELEMENT maintainers (maintainer)+>

<!ELEMENT maintainer (user|role|name|email)+>

<!ELEMENT user (#PCDATA)>

<!ELEMENT role (#PCDATA)>

<!ELEMENT email (#PCDATA)>

<!ELEMENT changelog (release)+>

<!ELEMENT release (version,date,license,state,notes,warnings?,provides*,deps?,configureoptions?,filelist?)>

<!ELEMENT version (#PCDATA)>

<!ELEMENT date (#PCDATA)>

<!ELEMENT state (#PCDATA)>

<!ELEMENT notes (#PCDATA)>

<!ELEMENT warnings (#PCDATA)>

<!ELEMENT deps (dep*)>

<!ELEMENT dep (#PCDATA)>
<!ATTLIST dep type    (pkg|ext|php) #REQUIRED
	                       rel     (has|eq|lt|le|gt|ge)                          #IMPLIED
	                       version CDATA                                     #IMPLIED
	                       optional (yes|no)     'no'>

<!ELEMENT configureoptions (configureoption)+>

<!ELEMENT configureoption EMPTY>
<!ATTLIST configureoption name CDATA #REQUIRED
                                           default CDATA #IMPLIED
                                           prompt CDATA #REQUIRED>

<!ELEMENT provides EMPTY>
<!ATTLIST provides type (ext|prog|class|function|feature|api) #REQUIRED
                                name CDATA #REQUIRED
                                extends CDATA #IMPLIED>
<!ELEMENT filelist (dir|file)+>

<!ELEMENT dir (dir|file)+>
<!ATTLIST dir name           CDATA #REQUIRED
              role           (php|ext|src|test|doc|data|script) 'php'
              baseinstalldir CDATA #IMPLIED>

<!ELEMENT file (replace*)>
<!ATTLIST file role           (php|ext|src|test|doc|data|script) 'php'
               debug          (na|on|off)        'na'
               format         CDATA              #IMPLIED
               baseinstalldir CDATA              #IMPLIED
               platform       CDATA              #IMPLIED
               md5sum         CDATA              #IMPLIED
               name           CDATA              #REQUIRED
               install-as     CDATA              #IMPLIED>

<!ELEMENT replace EMPTY>
<!ATTLIST replace type (php-const|pear-config|package-info) #REQUIRED
                              from CDATA #REQUIRED
                              to CDATA #REQUIRED>


PEAR-1.7.2/PEAR.php100644   1750   1750      103775 11014312742   7002 <?php
/**
 * PEAR, the PHP Extension and Application Repository
 *
 * PEAR class and PEAR_Error class
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Sterling Hughes <sterling@php.net>
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V.V.Cox <cox@idecnet.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: PEAR.php,v 1.104 2008/01/03 20:26:34 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**#@+
 * ERROR constants
 */
define('PEAR_ERROR_RETURN',     1);
define('PEAR_ERROR_PRINT',      2);
define('PEAR_ERROR_TRIGGER',    4);
define('PEAR_ERROR_DIE',        8);
define('PEAR_ERROR_CALLBACK',  16);
/**
 * WARNING: obsolete
 * @deprecated
 */
define('PEAR_ERROR_EXCEPTION', 32);
/**#@-*/
define('PEAR_ZE2', (function_exists('version_compare') &&
                    version_compare(zend_version(), "2-dev", "ge")));

if (substr(PHP_OS, 0, 3) == 'WIN') {
    define('OS_WINDOWS', true);
    define('OS_UNIX',    false);
    define('PEAR_OS',    'Windows');
} else {
    define('OS_WINDOWS', false);
    define('OS_UNIX',    true);
    define('PEAR_OS',    'Unix'); // blatant assumption
}

// instant backwards compatibility
if (!defined('PATH_SEPARATOR')) {
    if (OS_WINDOWS) {
        define('PATH_SEPARATOR', ';');
    } else {
        define('PATH_SEPARATOR', ':');
    }
}

$GLOBALS['_PEAR_default_error_mode']     = PEAR_ERROR_RETURN;
$GLOBALS['_PEAR_default_error_options']  = E_USER_NOTICE;
$GLOBALS['_PEAR_destructor_object_list'] = array();
$GLOBALS['_PEAR_shutdown_funcs']         = array();
$GLOBALS['_PEAR_error_handler_stack']    = array();

@ini_set('track_errors', true);

/**
 * Base class for other PEAR classes.  Provides rudimentary
 * emulation of destructors.
 *
 * If you want a destructor in your class, inherit PEAR and make a
 * destructor method called _yourclassname (same name as the
 * constructor, but with a "_" prefix).  Also, in your constructor you
 * have to call the PEAR constructor: $this->PEAR();.
 * The destructor method will be called without parameters.  Note that
 * at in some SAPI implementations (such as Apache), any output during
 * the request shutdown (in which destructors are called) seems to be
 * discarded.  If you need to get any debug information from your
 * destructor, use error_log(), syslog() or something similar.
 *
 * IMPORTANT! To use the emulated destructors you need to create the
 * objects by reference: $obj =& new PEAR_child;
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V.V. Cox <cox@idecnet.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2006 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @see        PEAR_Error
 * @since      Class available since PHP 4.0.2
 * @link        http://pear.php.net/manual/en/core.pear.php#core.pear.pear
 */
class PEAR
{
    // {{{ properties

    /**
     * Whether to enable internal debug messages.
     *
     * @var     bool
     * @access  private
     */
    var $_debug = false;

    /**
     * Default error mode for this object.
     *
     * @var     int
     * @access  private
     */
    var $_default_error_mode = null;

    /**
     * Default error options used for this object when error mode
     * is PEAR_ERROR_TRIGGER.
     *
     * @var     int
     * @access  private
     */
    var $_default_error_options = null;

    /**
     * Default error handler (callback) for this object, if error mode is
     * PEAR_ERROR_CALLBACK.
     *
     * @var     string
     * @access  private
     */
    var $_default_error_handler = '';

    /**
     * Which class to use for error objects.
     *
     * @var     string
     * @access  private
     */
    var $_error_class = 'PEAR_Error';

    /**
     * An array of expected errors.
     *
     * @var     array
     * @access  private
     */
    var $_expected_errors = array();

    // }}}

    // {{{ constructor

    /**
     * Constructor.  Registers this object in
     * $_PEAR_destructor_object_list for destructor emulation if a
     * destructor object exists.
     *
     * @param string $error_class  (optional) which class to use for
     *        error objects, defaults to PEAR_Error.
     * @access public
     * @return void
     */
    function PEAR($error_class = null)
    {
        $classname = strtolower(get_class($this));
        if ($this->_debug) {
            print "PEAR constructor called, class=$classname\n";
        }
        if ($error_class !== null) {
            $this->_error_class = $error_class;
        }
        while ($classname && strcasecmp($classname, "pear")) {
            $destructor = "_$classname";
            if (method_exists($this, $destructor)) {
                global $_PEAR_destructor_object_list;
                $_PEAR_destructor_object_list[] = &$this;
                if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
                    register_shutdown_function("_PEAR_call_destructors");
                    $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
                }
                break;
            } else {
                $classname = get_parent_class($classname);
            }
        }
    }

    // }}}
    // {{{ destructor

    /**
     * Destructor (the emulated type of...).  Does nothing right now,
     * but is included for forward compatibility, so subclass
     * destructors should always call it.
     *
     * See the note in the class desciption about output from
     * destructors.
     *
     * @access public
     * @return void
     */
    function _PEAR() {
        if ($this->_debug) {
            printf("PEAR destructor called, class=%s\n", strtolower(get_class($this)));
        }
    }

    // }}}
    // {{{ getStaticProperty()

    /**
    * If you have a class that's mostly/entirely static, and you need static
    * properties, you can use this method to simulate them. Eg. in your method(s)
    * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar');
    * You MUST use a reference, or they will not persist!
    *
    * @access public
    * @param  string $class  The calling classname, to prevent clashes
    * @param  string $var    The variable to retrieve.
    * @return mixed   A reference to the variable. If not set it will be
    *                 auto initialised to NULL.
    */
    function &getStaticProperty($class, $var)
    {
        static $properties;
        if (!isset($properties[$class])) {
            $properties[$class] = array();
        }
        if (!array_key_exists($var, $properties[$class])) {
            $properties[$class][$var] = null;
        }
        return $properties[$class][$var];
    }

    // }}}
    // {{{ registerShutdownFunc()

    /**
    * Use this function to register a shutdown method for static
    * classes.
    *
    * @access public
    * @param  mixed $func  The function name (or array of class/method) to call
    * @param  mixed $args  The arguments to pass to the function
    * @return void
    */
    function registerShutdownFunc($func, $args = array())
    {
        // if we are called statically, there is a potential
        // that no shutdown func is registered.  Bug #6445
        if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
            register_shutdown_function("_PEAR_call_destructors");
            $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
        }
        $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
    }

    // }}}
    // {{{ isError()

    /**
     * Tell whether a value is a PEAR error.
     *
     * @param   mixed $data   the value to test
     * @param   int   $code   if $data is an error object, return true
     *                        only if $code is a string and
     *                        $obj->getMessage() == $code or
     *                        $code is an integer and $obj->getCode() == $code
     * @access  public
     * @return  bool    true if parameter is an error
     */
    function isError($data, $code = null)
    {
        if (is_a($data, 'PEAR_Error')) {
            if (is_null($code)) {
                return true;
            } elseif (is_string($code)) {
                return $data->getMessage() == $code;
            } else {
                return $data->getCode() == $code;
            }
        }
        return false;
    }

    // }}}
    // {{{ setErrorHandling()

    /**
     * Sets how errors generated by this object should be handled.
     * Can be invoked both in objects and statically.  If called
     * statically, setErrorHandling sets the default behaviour for all
     * PEAR objects.  If called in an object, setErrorHandling sets
     * the default behaviour for that object.
     *
     * @param int $mode
     *        One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
     *        PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
     *        PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
     *
     * @param mixed $options
     *        When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
     *        of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
     *
     *        When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
     *        to be the callback function or method.  A callback
     *        function is a string with the name of the function, a
     *        callback method is an array of two elements: the element
     *        at index 0 is the object, and the element at index 1 is
     *        the name of the method to call in the object.
     *
     *        When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
     *        a printf format string used when printing the error
     *        message.
     *
     * @access public
     * @return void
     * @see PEAR_ERROR_RETURN
     * @see PEAR_ERROR_PRINT
     * @see PEAR_ERROR_TRIGGER
     * @see PEAR_ERROR_DIE
     * @see PEAR_ERROR_CALLBACK
     * @see PEAR_ERROR_EXCEPTION
     *
     * @since PHP 4.0.5
     */

    function setErrorHandling($mode = null, $options = null)
    {
        if (isset($this) && is_a($this, 'PEAR')) {
            $setmode     = &$this->_default_error_mode;
            $setoptions  = &$this->_default_error_options;
        } else {
            $setmode     = &$GLOBALS['_PEAR_default_error_mode'];
            $setoptions  = &$GLOBALS['_PEAR_default_error_options'];
        }

        switch ($mode) {
            case PEAR_ERROR_EXCEPTION:
            case PEAR_ERROR_RETURN:
            case PEAR_ERROR_PRINT:
            case PEAR_ERROR_TRIGGER:
            case PEAR_ERROR_DIE:
            case null:
                $setmode = $mode;
                $setoptions = $options;
                break;

            case PEAR_ERROR_CALLBACK:
                $setmode = $mode;
                // class/object method callback
                if (is_callable($options)) {
                    $setoptions = $options;
                } else {
                    trigger_error("invalid error callback", E_USER_WARNING);
                }
                break;

            default:
                trigger_error("invalid error mode", E_USER_WARNING);
                break;
        }
    }

    // }}}
    // {{{ expectError()

    /**
     * This method is used to tell which errors you expect to get.
     * Expected errors are always returned with error mode
     * PEAR_ERROR_RETURN.  Expected error codes are stored in a stack,
     * and this method pushes a new element onto it.  The list of
     * expected errors are in effect until they are popped off the
     * stack with the popExpect() method.
     *
     * Note that this method can not be called statically
     *
     * @param mixed $code a single error code or an array of error codes to expect
     *
     * @return int     the new depth of the "expected errors" stack
     * @access public
     */
    function expectError($code = '*')
    {
        if (is_array($code)) {
            array_push($this->_expected_errors, $code);
        } else {
            array_push($this->_expected_errors, array($code));
        }
        return sizeof($this->_expected_errors);
    }

    // }}}
    // {{{ popExpect()

    /**
     * This method pops one element off the expected error codes
     * stack.
     *
     * @return array   the list of error codes that were popped
     */
    function popExpect()
    {
        return array_pop($this->_expected_errors);
    }

    // }}}
    // {{{ _checkDelExpect()

    /**
     * This method checks unsets an error code if available
     *
     * @param mixed error code
     * @return bool true if the error code was unset, false otherwise
     * @access private
     * @since PHP 4.3.0
     */
    function _checkDelExpect($error_code)
    {
        $deleted = false;

        foreach ($this->_expected_errors AS $key => $error_array) {
            if (in_array($error_code, $error_array)) {
                unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
                $deleted = true;
            }

            // clean up empty arrays
            if (0 == count($this->_expected_errors[$key])) {
                unset($this->_expected_errors[$key]);
            }
        }
        return $deleted;
    }

    // }}}
    // {{{ delExpect()

    /**
     * This method deletes all occurences of the specified element from
     * the expected error codes stack.
     *
     * @param  mixed $error_code error code that should be deleted
     * @return mixed list of error codes that were deleted or error
     * @access public
     * @since PHP 4.3.0
     */
    function delExpect($error_code)
    {
        $deleted = false;

        if ((is_array($error_code) && (0 != count($error_code)))) {
            // $error_code is a non-empty array here;
            // we walk through it trying to unset all
            // values
            foreach($error_code as $key => $error) {
                if ($this->_checkDelExpect($error)) {
                    $deleted =  true;
                } else {
                    $deleted = false;
                }
            }
            return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
        } elseif (!empty($error_code)) {
            // $error_code comes alone, trying to unset it
            if ($this->_checkDelExpect($error_code)) {
                return true;
            } else {
                return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
            }
        } else {
            // $error_code is empty
            return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
        }
    }

    // }}}
    // {{{ raiseError()

    /**
     * This method is a wrapper that returns an instance of the
     * configured error class with this object's default error
     * handling applied.  If the $mode and $options parameters are not
     * specified, the object's defaults are used.
     *
     * @param mixed $message a text error message or a PEAR error object
     *
     * @param int $code      a numeric error code (it is up to your class
     *                  to define these if you want to use codes)
     *
     * @param int $mode      One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
     *                  PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
     *                  PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
     *
     * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
     *                  specifies the PHP-internal error level (one of
     *                  E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
     *                  If $mode is PEAR_ERROR_CALLBACK, this
     *                  parameter specifies the callback function or
     *                  method.  In other error modes this parameter
     *                  is ignored.
     *
     * @param string $userinfo If you need to pass along for example debug
     *                  information, this parameter is meant for that.
     *
     * @param string $error_class The returned error object will be
     *                  instantiated from this class, if specified.
     *
     * @param bool $skipmsg If true, raiseError will only pass error codes,
     *                  the error message parameter will be dropped.
     *
     * @access public
     * @return object   a PEAR error object
     * @see PEAR::setErrorHandling
     * @since PHP 4.0.5
     */
    function &raiseError($message = null,
                         $code = null,
                         $mode = null,
                         $options = null,
                         $userinfo = null,
                         $error_class = null,
                         $skipmsg = false)
    {
        // The error is yet a PEAR error object
        if (is_object($message)) {
            $code        = $message->getCode();
            $userinfo    = $message->getUserInfo();
            $error_class = $message->getType();
            $message->error_message_prefix = '';
            $message     = $message->getMessage();
        }

        if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) {
            if ($exp[0] == "*" ||
                (is_int(reset($exp)) && in_array($code, $exp)) ||
                (is_string(reset($exp)) && in_array($message, $exp))) {
                $mode = PEAR_ERROR_RETURN;
            }
        }
        // No mode given, try global ones
        if ($mode === null) {
            // Class error handler
            if (isset($this) && isset($this->_default_error_mode)) {
                $mode    = $this->_default_error_mode;
                $options = $this->_default_error_options;
            // Global error handler
            } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
                $mode    = $GLOBALS['_PEAR_default_error_mode'];
                $options = $GLOBALS['_PEAR_default_error_options'];
            }
        }

        if ($error_class !== null) {
            $ec = $error_class;
        } elseif (isset($this) && isset($this->_error_class)) {
            $ec = $this->_error_class;
        } else {
            $ec = 'PEAR_Error';
        }
        if (intval(PHP_VERSION) < 5) {
            // little non-eval hack to fix bug #12147
            include 'PEAR/FixPHP5PEARWarnings.php';
            return $a;
        }
        if ($skipmsg) {
            $a = new $ec($code, $mode, $options, $userinfo);
        } else {
            $a = new $ec($message, $code, $mode, $options, $userinfo);
        }
        return $a;
    }

    // }}}
    // {{{ throwError()

    /**
     * Simpler form of raiseError with fewer options.  In most cases
     * message, code and userinfo are enough.
     *
     * @param string $message
     *
     */
    function &throwError($message = null,
                         $code = null,
                         $userinfo = null)
    {
        if (isset($this) && is_a($this, 'PEAR')) {
            $a = &$this->raiseError($message, $code, null, null, $userinfo);
            return $a;
        } else {
            $a = &PEAR::raiseError($message, $code, null, null, $userinfo);
            return $a;
        }
    }

    // }}}
    function staticPushErrorHandling($mode, $options = null)
    {
        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
        $def_mode    = &$GLOBALS['_PEAR_default_error_mode'];
        $def_options = &$GLOBALS['_PEAR_default_error_options'];
        $stack[] = array($def_mode, $def_options);
        switch ($mode) {
            case PEAR_ERROR_EXCEPTION:
            case PEAR_ERROR_RETURN:
            case PEAR_ERROR_PRINT:
            case PEAR_ERROR_TRIGGER:
            case PEAR_ERROR_DIE:
            case null:
                $def_mode = $mode;
                $def_options = $options;
                break;

            case PEAR_ERROR_CALLBACK:
                $def_mode = $mode;
                // class/object method callback
                if (is_callable($options)) {
                    $def_options = $options;
                } else {
                    trigger_error("invalid error callback", E_USER_WARNING);
                }
                break;

            default:
                trigger_error("invalid error mode", E_USER_WARNING);
                break;
        }
        $stack[] = array($mode, $options);
        return true;
    }

    function staticPopErrorHandling()
    {
        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
        $setmode     = &$GLOBALS['_PEAR_default_error_mode'];
        $setoptions  = &$GLOBALS['_PEAR_default_error_options'];
        array_pop($stack);
        list($mode, $options) = $stack[sizeof($stack) - 1];
        array_pop($stack);
        switch ($mode) {
            case PEAR_ERROR_EXCEPTION:
            case PEAR_ERROR_RETURN:
            case PEAR_ERROR_PRINT:
            case PEAR_ERROR_TRIGGER:
            case PEAR_ERROR_DIE:
            case null:
                $setmode = $mode;
                $setoptions = $options;
                break;

            case PEAR_ERROR_CALLBACK:
                $setmode = $mode;
                // class/object method callback
                if (is_callable($options)) {
                    $setoptions = $options;
                } else {
                    trigger_error("invalid error callback", E_USER_WARNING);
                }
                break;

            default:
                trigger_error("invalid error mode", E_USER_WARNING);
                break;
        }
        return true;
    }

    // {{{ pushErrorHandling()

    /**
     * Push a new error handler on top of the error handler options stack. With this
     * you can easily override the actual error handler for some code and restore
     * it later with popErrorHandling.
     *
     * @param mixed $mode (same as setErrorHandling)
     * @param mixed $options (same as setErrorHandling)
     *
     * @return bool Always true
     *
     * @see PEAR::setErrorHandling
     */
    function pushErrorHandling($mode, $options = null)
    {
        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
        if (isset($this) && is_a($this, 'PEAR')) {
            $def_mode    = &$this->_default_error_mode;
            $def_options = &$this->_default_error_options;
        } else {
            $def_mode    = &$GLOBALS['_PEAR_default_error_mode'];
            $def_options = &$GLOBALS['_PEAR_default_error_options'];
        }
        $stack[] = array($def_mode, $def_options);

        if (isset($this) && is_a($this, 'PEAR')) {
            $this->setErrorHandling($mode, $options);
        } else {
            PEAR::setErrorHandling($mode, $options);
        }
        $stack[] = array($mode, $options);
        return true;
    }

    // }}}
    // {{{ popErrorHandling()

    /**
    * Pop the last error handler used
    *
    * @return bool Always true
    *
    * @see PEAR::pushErrorHandling
    */
    function popErrorHandling()
    {
        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
        array_pop($stack);
        list($mode, $options) = $stack[sizeof($stack) - 1];
        array_pop($stack);
        if (isset($this) && is_a($this, 'PEAR')) {
            $this->setErrorHandling($mode, $options);
        } else {
            PEAR::setErrorHandling($mode, $options);
        }
        return true;
    }

    // }}}
    // {{{ loadExtension()

    /**
    * OS independant PHP extension load. Remember to take care
    * on the correct extension name for case sensitive OSes.
    *
    * @param string $ext The extension name
    * @return bool Success or not on the dl() call
    */
    function loadExtension($ext)
    {
        if (!extension_loaded($ext)) {
            // if either returns true dl() will produce a FATAL error, stop that
            if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {
                return false;
            }
            if (OS_WINDOWS) {
                $suffix = '.dll';
            } elseif (PHP_OS == 'HP-UX') {
                $suffix = '.sl';
            } elseif (PHP_OS == 'AIX') {
                $suffix = '.a';
            } elseif (PHP_OS == 'OSX') {
                $suffix = '.bundle';
            } else {
                $suffix = '.so';
            }
            return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
        }
        return true;
    }

    // }}}
}

// {{{ _PEAR_call_destructors()

function _PEAR_call_destructors()
{
    global $_PEAR_destructor_object_list;
    if (is_array($_PEAR_destructor_object_list) &&
        sizeof($_PEAR_destructor_object_list))
    {
        reset($_PEAR_destructor_object_list);
        if (PEAR::getStaticProperty('PEAR', 'destructlifo')) {
            $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list);
        }
        while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
            $classname = get_class($objref);
            while ($classname) {
                $destructor = "_$classname";
                if (method_exists($objref, $destructor)) {
                    $objref->$destructor();
                    break;
                } else {
                    $classname = get_parent_class($classname);
                }
            }
        }
        // Empty the object list to ensure that destructors are
        // not called more than once.
        $_PEAR_destructor_object_list = array();
    }

    // Now call the shutdown functions
    if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) {
        foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
            call_user_func_array($value[0], $value[1]);
        }
    }
}

// }}}
/**
 * Standard PEAR error class for PHP 4
 *
 * This class is supserseded by {@link PEAR_Exception} in PHP 5
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V.V. Cox <cox@idecnet.com>
 * @author     Gregory Beaver <cellog@php.net>
 * @copyright  1997-2006 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/manual/en/core.pear.pear-error.php
 * @see        PEAR::raiseError(), PEAR::throwError()
 * @since      Class available since PHP 4.0.2
 */
class PEAR_Error
{
    // {{{ properties

    var $error_message_prefix = '';
    var $mode                 = PEAR_ERROR_RETURN;
    var $level                = E_USER_NOTICE;
    var $code                 = -1;
    var $message              = '';
    var $userinfo             = '';
    var $backtrace            = null;

    // }}}
    // {{{ constructor

    /**
     * PEAR_Error constructor
     *
     * @param string $message  message
     *
     * @param int $code     (optional) error code
     *
     * @param int $mode     (optional) error mode, one of: PEAR_ERROR_RETURN,
     * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
     * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
     *
     * @param mixed $options   (optional) error level, _OR_ in the case of
     * PEAR_ERROR_CALLBACK, the callback function or object/method
     * tuple.
     *
     * @param string $userinfo (optional) additional user/debug info
     *
     * @access public
     *
     */
    function PEAR_Error($message = 'unknown error', $code = null,
                        $mode = null, $options = null, $userinfo = null)
    {
        if ($mode === null) {
            $mode = PEAR_ERROR_RETURN;
        }
        $this->message   = $message;
        $this->code      = $code;
        $this->mode      = $mode;
        $this->userinfo  = $userinfo;
        if (!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) {
            $this->backtrace = debug_backtrace();
            if (isset($this->backtrace[0]) && isset($this->backtrace[0]['object'])) {
                unset($this->backtrace[0]['object']);
            }
        }
        if ($mode & PEAR_ERROR_CALLBACK) {
            $this->level = E_USER_NOTICE;
            $this->callback = $options;
        } else {
            if ($options === null) {
                $options = E_USER_NOTICE;
            }
            $this->level = $options;
            $this->callback = null;
        }
        if ($this->mode & PEAR_ERROR_PRINT) {
            if (is_null($options) || is_int($options)) {
                $format = "%s";
            } else {
                $format = $options;
            }
            printf($format, $this->getMessage());
        }
        if ($this->mode & PEAR_ERROR_TRIGGER) {
            trigger_error($this->getMessage(), $this->level);
        }
        if ($this->mode & PEAR_ERROR_DIE) {
            $msg = $this->getMessage();
            if (is_null($options) || is_int($options)) {
                $format = "%s";
                if (substr($msg, -1) != "\n") {
                    $msg .= "\n";
                }
            } else {
                $format = $options;
            }
            die(sprintf($format, $msg));
        }
        if ($this->mode & PEAR_ERROR_CALLBACK) {
            if (is_callable($this->callback)) {
                call_user_func($this->callback, $this);
            }
        }
        if ($this->mode & PEAR_ERROR_EXCEPTION) {
            trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING);
            eval('$e = new Exception($this->message, $this->code);throw($e);');
        }
    }

    // }}}
    // {{{ getMode()

    /**
     * Get the error mode from an error object.
     *
     * @return int error mode
     * @access public
     */
    function getMode() {
        return $this->mode;
    }

    // }}}
    // {{{ getCallback()

    /**
     * Get the callback function/method from an error object.
     *
     * @return mixed callback function or object/method array
     * @access public
     */
    function getCallback() {
        return $this->callback;
    }

    // }}}
    // {{{ getMessage()


    /**
     * Get the error message from an error object.
     *
     * @return  string  full error message
     * @access public
     */
    function getMessage()
    {
        return ($this->error_message_prefix . $this->message);
    }


    // }}}
    // {{{ getCode()

    /**
     * Get error code from an error object
     *
     * @return int error code
     * @access public
     */
     function getCode()
     {
        return $this->code;
     }

    // }}}
    // {{{ getType()

    /**
     * Get the name of this error/exception.
     *
     * @return string error/exception name (type)
     * @access public
     */
    function getType()
    {
        return get_class($this);
    }

    // }}}
    // {{{ getUserInfo()

    /**
     * Get additional user-supplied information.
     *
     * @return string user-supplied information
     * @access public
     */
    function getUserInfo()
    {
        return $this->userinfo;
    }

    // }}}
    // {{{ getDebugInfo()

    /**
     * Get additional debug information supplied by the application.
     *
     * @return string debug information
     * @access public
     */
    function getDebugInfo()
    {
        return $this->getUserInfo();
    }

    // }}}
    // {{{ getBacktrace()

    /**
     * Get the call backtrace from where the error was generated.
     * Supported with PHP 4.3.0 or newer.
     *
     * @param int $frame (optional) what frame to fetch
     * @return array Backtrace, or NULL if not available.
     * @access public
     */
    function getBacktrace($frame = null)
    {
        if (defined('PEAR_IGNORE_BACKTRACE')) {
            return null;
        }
        if ($frame === null) {
            return $this->backtrace;
        }
        return $this->backtrace[$frame];
    }

    // }}}
    // {{{ addUserInfo()

    function addUserInfo($info)
    {
        if (empty($this->userinfo)) {
            $this->userinfo = $info;
        } else {
            $this->userinfo .= " ** $info";
        }
    }

    // }}}
    // {{{ toString()
    function __toString()
    {
        return $this->getMessage();
    }
    // }}}
    // {{{ toString()

    /**
     * Make a string representation of this object.
     *
     * @return string a string with an object summary
     * @access public
     */
    function toString() {
        $modes = array();
        $levels = array(E_USER_NOTICE  => 'notice',
                        E_USER_WARNING => 'warning',
                        E_USER_ERROR   => 'error');
        if ($this->mode & PEAR_ERROR_CALLBACK) {
            if (is_array($this->callback)) {
                $callback = (is_object($this->callback[0]) ?
                    strtolower(get_class($this->callback[0])) :
                    $this->callback[0]) . '::' .
                    $this->callback[1];
            } else {
                $callback = $this->callback;
            }
            return sprintf('[%s: message="%s" code=%d mode=callback '.
                           'callback=%s prefix="%s" info="%s"]',
                           strtolower(get_class($this)), $this->message, $this->code,
                           $callback, $this->error_message_prefix,
                           $this->userinfo);
        }
        if ($this->mode & PEAR_ERROR_PRINT) {
            $modes[] = 'print';
        }
        if ($this->mode & PEAR_ERROR_TRIGGER) {
            $modes[] = 'trigger';
        }
        if ($this->mode & PEAR_ERROR_DIE) {
            $modes[] = 'die';
        }
        if ($this->mode & PEAR_ERROR_RETURN) {
            $modes[] = 'return';
        }
        return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
                       'prefix="%s" info="%s"]',
                       strtolower(get_class($this)), $this->message, $this->code,
                       implode("|", $modes), $levels[$this->level],
                       $this->error_message_prefix,
                       $this->userinfo);
    }

    // }}}
}

/*
 * Local Variables:
 * mode: php
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 */
?>
PEAR-1.7.2/README100644   1750   1750        2251 11014312742   6345 PEAR - The PEAR Installer
=========================

What is the PEAR Installer?  What is PEAR?

PEAR is the PHP Extension and Application Repository, found at
http://pear.php.net.  The PEAR Installer is this software, which
contains executable files and PHP code that is used to download
and install PEAR code from pear.php.net.

PEAR contains useful software libraries and applications such as
MDB2 (database abstraction), HTML_QuickForm (HTML forms management),
PhpDocumentor (auto-documentation generator), DB_DataObject
(Data Access Abstraction), and many hundreds more.  Browse all
available packages at http://pear.php.net, the list is constantly
growing and updating to reflect improvements in the PHP language.

DOCUMENTATION
=============

Documentation for PEAR can be found at http://pear.php.net/manual/.
Installation documentation can be found in the INSTALL file included
in this tarball.

WARNING: DO NOT RUN PEAR WITHOUT INSTALLING IT - if you downloaded this
tarball manually, you MUST install it.  Read the instructions in INSTALL
prior to use.


Happy PHPing, we hope PEAR will be a great tool for your development work!

$Id: README,v 1.11 2006/09/22 03:31:36 cellog Exp $PEAR-1.7.2/System.php100644   1750   1750       46735 11014312742   7521 <?php
/**
 * File/Directory manipulation
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    System
 * @author     Tomas V.V.Cox <cox@idecnet.com>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: System.php,v 1.62 2008/01/03 20:26:34 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * base class
 */
require_once 'PEAR.php';
require_once 'Console/Getopt.php';

$GLOBALS['_System_temp_files'] = array();

/**
* System offers cross plattform compatible system functions
*
* Static functions for different operations. Should work under
* Unix and Windows. The names and usage has been taken from its respectively
* GNU commands. The functions will return (bool) false on error and will
* trigger the error with the PHP trigger_error() function (you can silence
* the error by prefixing a '@' sign after the function call, but this
* is not recommended practice.  Instead use an error handler with
* {@link set_error_handler()}).
*
* Documentation on this class you can find in:
* http://pear.php.net/manual/
*
* Example usage:
* if (!@System::rm('-r file1 dir1')) {
*    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));
*
* @category   pear
* @package    System
* @author     Tomas V.V. Cox <cox@idecnet.com>
* @copyright  1997-2006 The PHP Group
* @license    http://www.php.net/license/3_0.txt  PHP License 3.0
* @version    Release: 1.7.2
* @link       http://pear.php.net/package/PEAR
* @since      Class available since Release 0.1
* @static 
*/
class System
{
    /**
    * returns the commandline arguments of a function
    *
    * @param    string  $argv           the commandline
    * @param    string  $short_options  the allowed option short-tags
    * @param    string  $long_options   the allowed option long-tags
    * @return   array   the given options and there values
    * @static 
    * @access private
    */
    function _parseArgs($argv, $short_options, $long_options = null)
    {
        if (!is_array($argv) && $argv !== null) {
            $argv = preg_split('/\s+/', $argv, -1, PREG_SPLIT_NO_EMPTY);
        }
        return Console_Getopt::getopt2($argv, $short_options);
    }

    /**
    * Output errors with PHP trigger_error(). You can silence the errors
    * with prefixing a "@" sign to the function call: @System::mkdir(..);
    *
    * @param mixed $error a PEAR error or a string with the error message
    * @return bool false
    * @static 
    * @access private
    */
    function raiseError($error)
    {
        if (PEAR::isError($error)) {
            $error = $error->getMessage();
        }
        trigger_error($error, E_USER_WARNING);
        return false;
    }

    /**
    * Creates a nested array representing the structure of a directory
    *
    * System::_dirToStruct('dir1', 0) =>
    *   Array
    *    (
    *    [dirs] => Array
    *        (
    *            [0] => dir1
    *        )
    *
    *    [files] => Array
    *        (
    *            [0] => dir1/file2
    *            [1] => dir1/file3
    *        )
    *    )
    * @param    string  $sPath      Name of the directory
    * @param    integer $maxinst    max. deep of the lookup
    * @param    integer $aktinst    starting deep of the lookup
    * @param    bool    $silent     if true, do not emit errors.
    * @return   array   the structure of the dir
    * @static 
    * @access   private
    */

    function _dirToStruct($sPath, $maxinst, $aktinst = 0, $silent = false)
    {
        $struct = array('dirs' => array(), 'files' => array());
        if (($dir = @opendir($sPath)) === false) {
            if (!$silent) {
                System::raiseError("Could not open dir $sPath");
            }
            return $struct; // XXX could not open error
        }
        $struct['dirs'][] = $sPath = realpath($sPath); // XXX don't add if '.' or '..' ?
        $list = array();
        while (false !== ($file = readdir($dir))) {
            if ($file != '.' && $file != '..') {
                $list[] = $file;
            }
        }
        closedir($dir);
        sort($list);
        if ($aktinst < $maxinst || $maxinst == 0) {
            foreach ($list as $val) {
                $path = $sPath . DIRECTORY_SEPARATOR . $val;
                if (is_dir($path) && !is_link($path)) {
                    $tmp = System::_dirToStruct($path, $maxinst, $aktinst+1, $silent);
                    $struct = array_merge_recursive($tmp, $struct);
                } else {
                    $struct['files'][] = $path;
                }
            }
        }
        return $struct;
    }

    /**
    * Creates a nested array representing the structure of a directory and files
    *
    * @param    array $files Array listing files and dirs
    * @return   array
    * @static 
    * @see System::_dirToStruct()
    */
    function _multipleToStruct($files)
    {
        $struct = array('dirs' => array(), 'files' => array());
        settype($files, 'array');
        foreach ($files as $file) {
            if (is_dir($file) && !is_link($file)) {
                $tmp = System::_dirToStruct($file, 0);
                $struct = array_merge_recursive($tmp, $struct);
            } else {
                $struct['files'][] = $file;
            }
        }
        return $struct;
    }

    /**
    * The rm command for removing files.
    * Supports multiple files and dirs and also recursive deletes
    *
    * @param    string  $args   the arguments for rm
    * @return   mixed   PEAR_Error or true for success
    * @static 
    * @access   public
    */
    function rm($args)
    {
        $opts = System::_parseArgs($args, 'rf'); // "f" does nothing but I like it :-)
        if (PEAR::isError($opts)) {
            return System::raiseError($opts);
        }
        foreach ($opts[0] as $opt) {
            if ($opt[0] == 'r') {
                $do_recursive = true;
            }
        }
        $ret = true;
        if (isset($do_recursive)) {
            $struct = System::_multipleToStruct($opts[1]);
            foreach ($struct['files'] as $file) {
                if (!@unlink($file)) {
                    $ret = false;
                }
            }
            foreach ($struct['dirs'] as $dir) {
                if (!@rmdir($dir)) {
                    $ret = false;
                }
            }
        } else {
            foreach ($opts[1] as $file) {
                $delete = (is_dir($file)) ? 'rmdir' : 'unlink';
                if (!@$delete($file)) {
                    $ret = false;
                }
            }
        }
        return $ret;
    }

    /**
    * Make directories.
    *
    * The -p option will create parent directories
    * @param    string  $args    the name of the director(y|ies) to create
    * @return   bool    True for success
    * @static 
    * @access   public
    */
    function mkDir($args)
    {
        $opts = System::_parseArgs($args, 'pm:');
        if (PEAR::isError($opts)) {
            return System::raiseError($opts);
        }
        $mode = 0777; // default mode
        foreach ($opts[0] as $opt) {
            if ($opt[0] == 'p') {
                $create_parents = true;
            } elseif ($opt[0] == 'm') {
                // if the mode is clearly an octal number (starts with 0)
                // convert it to decimal
                if (strlen($opt[1]) && $opt[1]{0} == '0') {
                    $opt[1] = octdec($opt[1]);
                } else {
                    // convert to int
                    $opt[1] += 0;
                }
                $mode = $opt[1];
            }
        }
        $ret = true;
        if (isset($create_parents)) {
            foreach ($opts[1] as $dir) {
                $dirstack = array();
                while ((!file_exists($dir) || !is_dir($dir)) &&
                        $dir != DIRECTORY_SEPARATOR) {
                    array_unshift($dirstack, $dir);
                    $dir = dirname($dir);
                }
                while ($newdir = array_shift($dirstack)) {
                    if (!is_writeable(dirname($newdir))) {
                        $ret = false;
                        break;
                    }
                    if (!mkdir($newdir, $mode)) {
                        $ret = false;
                    }
                }
            }
        } else {
            foreach($opts[1] as $dir) {
                if ((@file_exists($dir) || !is_dir($dir)) && !mkdir($dir, $mode)) {
                    $ret = false;
                }
            }
        }
        return $ret;
    }

    /**
    * Concatenate files
    *
    * Usage:
    * 1) $var = System::cat('sample.txt test.txt');
    * 2) System::cat('sample.txt test.txt > final.txt');
    * 3) System::cat('sample.txt test.txt >> final.txt');
    *
    * Note: as the class use fopen, urls should work also (test that)
    *
    * @param    string  $args   the arguments
    * @return   boolean true on success
    * @static 
    * @access   public
    */
    function &cat($args)
    {
        $ret = null;
        $files = array();
        if (!is_array($args)) {
            $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
        }

        $count_args = count($args);
        for ($i = 0; $i < $count_args; $i++) {
            if ($args[$i] == '>') {
                $mode = 'wb';
                $outputfile = $args[$i+1];
                break;
            } elseif ($args[$i] == '>>') {
                $mode = 'ab+';
                $outputfile = $args[$i+1];
                break;
            } else {
                $files[] = $args[$i];
            }
        }
        $outputfd = false;
        if (isset($mode)) {
            if (!$outputfd = fopen($outputfile, $mode)) {
                $err = System::raiseError("Could not open $outputfile");
                return $err;
            }
            $ret = true;
        }
        foreach ($files as $file) {
            if (!$fd = fopen($file, 'r')) {
                System::raiseError("Could not open $file");
                continue;
            }
            while ($cont = fread($fd, 2048)) {
                if (is_resource($outputfd)) {
                    fwrite($outputfd, $cont);
                } else {
                    $ret .= $cont;
                }
            }
            fclose($fd);
        }
        if (is_resource($outputfd)) {
            fclose($outputfd);
        }
        return $ret;
    }

    /**
    * Creates temporary files or directories. This function will remove
    * the created files when the scripts finish its execution.
    *
    * Usage:
    *   1) $tempfile = System::mktemp("prefix");
    *   2) $tempdir  = System::mktemp("-d prefix");
    *   3) $tempfile = System::mktemp();
    *   4) $tempfile = System::mktemp("-t /var/tmp prefix");
    *
    * prefix -> The string that will be prepended to the temp name
    *           (defaults to "tmp").
    * -d     -> A temporary dir will be created instead of a file.
    * -t     -> The target dir where the temporary (file|dir) will be created. If
    *           this param is missing by default the env vars TMP on Windows or
    *           TMPDIR in Unix will be used. If these vars are also missing
    *           c:\windows\temp or /tmp will be used.
    *
    * @param   string  $args  The arguments
    * @return  mixed   the full path of the created (file|dir) or false
    * @see System::tmpdir()
    * @static 
    * @access  public
    */
    function mktemp($args = null)
    {
        static $first_time = true;
        $opts = System::_parseArgs($args, 't:d');
        if (PEAR::isError($opts)) {
            return System::raiseError($opts);
        }
        foreach ($opts[0] as $opt) {
            if ($opt[0] == 'd') {
                $tmp_is_dir = true;
            } elseif ($opt[0] == 't') {
                $tmpdir = $opt[1];
            }
        }
        $prefix = (isset($opts[1][0])) ? $opts[1][0] : 'tmp';
        if (!isset($tmpdir)) {
            $tmpdir = System::tmpdir();
        }
        if (!System::mkDir(array('-p', $tmpdir))) {
            return false;
        }
        $tmp = tempnam($tmpdir, $prefix);
        if (isset($tmp_is_dir)) {
            unlink($tmp); // be careful possible race condition here
            if (!mkdir($tmp, 0700)) {
                return System::raiseError("Unable to create temporary directory $tmpdir");
            }
        }
        $GLOBALS['_System_temp_files'][] = $tmp;
        if ($first_time) {
            PEAR::registerShutdownFunc(array('System', '_removeTmpFiles'));
            $first_time = false;
        }
        return $tmp;
    }

    /**
    * Remove temporary files created my mkTemp. This function is executed
    * at script shutdown time
    *
    * @static 
    * @access private
    */
    function _removeTmpFiles()
    {
        if (count($GLOBALS['_System_temp_files'])) {
            $delete = $GLOBALS['_System_temp_files'];
            array_unshift($delete, '-r');
            System::rm($delete);
            $GLOBALS['_System_temp_files'] = array();
        }
    }

    /**
    * Get the path of the temporal directory set in the system
    * by looking in its environments variables.
    * Note: php.ini-recommended removes the "E" from the variables_order setting,
    * making unavaible the $_ENV array, that s why we do tests with _ENV
    *
    * @static 
    * @return string The temporary directory on the system
    */
    function tmpdir()
    {
        if (OS_WINDOWS) {
            if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) {
                return $var;
            }
            if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP')) {
                return $var;
            }
            if ($var = isset($_ENV['USERPROFILE']) ? $_ENV['USERPROFILE'] : getenv('USERPROFILE')) {
                return $var;
            }
            if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir')) {
                return $var;
            }
            return getenv('SystemRoot') . '\temp';
        }
        if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) {
            return $var;
        }
        return realpath('/tmp');
    }

    /**
    * The "which" command (show the full path of a command)
    *
    * @param string $program The command to search for
    * @param mixed  $fallback Value to return if $program is not found
    *
    * @return mixed A string with the full path or false if not found
    * @static 
    * @author Stig Bakken <ssb@php.net>
    */
    function which($program, $fallback = false)
    {
        // enforce API
        if (!is_string($program) || '' == $program) {
            return $fallback;
        }

        // full path given
        if (basename($program) != $program) {
            $path_elements[] = dirname($program);
            $program = basename($program);
        } else {
            // Honor safe mode
            if (!ini_get('safe_mode') || !$path = ini_get('safe_mode_exec_dir')) {
                $path = getenv('PATH');
                if (!$path) {
                    $path = getenv('Path'); // some OSes are just stupid enough to do this
                }
            }
            $path_elements = explode(PATH_SEPARATOR, $path);
        }

        if (OS_WINDOWS) {
            $exe_suffixes = getenv('PATHEXT')
                                ? explode(PATH_SEPARATOR, getenv('PATHEXT'))
                                : array('.exe','.bat','.cmd','.com');
            // allow passing a command.exe param
            if (strpos($program, '.') !== false) {
                array_unshift($exe_suffixes, '');
            }
            // is_executable() is not available on windows for PHP4
            $pear_is_executable = (function_exists('is_executable')) ? 'is_executable' : 'is_file';
        } else {
            $exe_suffixes = array('');
            $pear_is_executable = 'is_executable';
        }

        foreach ($exe_suffixes as $suff) {
            foreach ($path_elements as $dir) {
                $file = $dir . DIRECTORY_SEPARATOR . $program . $suff;
                if (@$pear_is_executable($file)) {
                    return $file;
                }
            }
        }
        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
    * @static 
    *
    */
    function find($args)
    {
        if (!is_array($args)) {
            $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
        }
        $dir = realpath(array_shift($args));
        if (!$dir) {
            return array();
        }
        $patterns = array();
        $depth = 0;
        $do_files = $do_dirs = true;
        $args_count = count($args);
        for ($i = 0; $i < $args_count; $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':
                    $name = preg_quote($args[$i+1], '#');
                    // our magic characters ? and * have just been escaped,
                    // so now we change the escaped versions to PCRE operators
                    $name = strtr($name, array('\?' => '.', '\*' => '.*'));
                    $patterns[] = '('.$name.')';
                    $i++;
                    break;
                case '-maxdepth':
                    $depth = $args[$i+1];
                    break;
            }
        }
        $path = System::_dirToStruct($dir, $depth, 0, true);
        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)) {
            $dsq = preg_quote(DIRECTORY_SEPARATOR, '#');
            $pattern = '#(^|'.$dsq.')'.implode('|', $patterns).'($|'.$dsq.')#';
            $ret = array();
            $files_count = count($files);
            for ($i = 0; $i < $files_count; $i++) {
                // only search in the part of the file below the current directory
                $filepart = basename($files[$i]);
                if (preg_match($pattern, $filepart)) {
                    $ret[] = $files[$i];
                }
            }
            return $ret;
        }
        return $files;
    }
}PEAR-1.7.2/template.spec100644   1750   1750        3725 11014312742  10163 Summary: PEAR: @summary@
Name: @rpm_package@
Version: @version@
Release: 1
License: @release_license@
Group: Development/Libraries
Source: http://@master_server@/get/@package@-%{version}.tgz
BuildRoot: %{_tmppath}/%{name}-root
URL: http://@master_server@/package/@package@
Prefix: %{_prefix}
BuildArchitectures: @arch@
@extra_headers@

%description
@description@

%prep
rm -rf %{buildroot}/*
%setup -c -T
# XXX Source files location is missing here in pear cmd
pear -v -c %{buildroot}/pearrc \
        -d php_dir=%{_libdir}/php/pear \
        -d doc_dir=/docs \
        -d bin_dir=%{_bindir} \
        -d data_dir=%{_libdir}/php/pear/data \
        -d test_dir=%{_libdir}/php/pear/tests \
        -d ext_dir=%{_libdir} \@extra_config@
        -s

%build
echo BuildRoot=%{buildroot}

%postun
# if refcount = 0 then package has been removed (not upgraded)
if [ "$1" -eq "0" ]; then
    pear uninstall --nodeps -r @possible_channel@@package@
    rm @rpm_xml_dir@/@package@.xml
fi


%post
# if refcount = 2 then package has been upgraded
if [ "$1" -ge "2" ]; then
    pear upgrade --nodeps -r @rpm_xml_dir@/@package@.xml
else
    pear install --nodeps -r @rpm_xml_dir@/@package@.xml
fi

%install
pear -c %{buildroot}/pearrc install --nodeps -R %{buildroot} \
        $RPM_SOURCE_DIR/@package@-%{version}.tgz
rm %{buildroot}/pearrc
rm %{buildroot}/%{_libdir}/php/pear/.filemap
rm %{buildroot}/%{_libdir}/php/pear/.lock
rm -rf %{buildroot}/%{_libdir}/php/pear/.registry
if [ "@doc_files@" != "" ]; then
     mv %{buildroot}/docs/@package@/* .
     rm -rf %{buildroot}/docs
fi
mkdir -p %{buildroot}@rpm_xml_dir@
tar -xzf $RPM_SOURCE_DIR/@package@-%{version}.tgz package@package2xml@.xml
cp -p package@package2xml@.xml %{buildroot}@rpm_xml_dir@/@package@.xml

#rm -rf %{buildroot}/*
#pear -q install -R %{buildroot} -n package@package2xml@.xml
#mkdir -p %{buildroot}@rpm_xml_dir@
#cp -p package@package2xml@.xml %{buildroot}@rpm_xml_dir@/@package@.xml

%files
    %defattr(-,root,root)
    %doc @doc_files@
    /
<?php
/**
 * PEAR, the PHP Extension and Application Repository
 *
 * PEAR class and PEAR_Error class
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Sterling Hughes <sterling@php.net>
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V.V.Cox <cox@idecnet.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: PEAR.php,v 1.104 2008/01/03 20:26:34 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**#@+
 * ERROR constants
 */
define('PEAR_ERROR_RETURN',     1);
define('PEAR_ERROR_PRINT',      2);
define('PEAR_ERROR_TRIGGER',    4);
define('PEAR_ERROR_DIE',        8);
define('PEAR_ERROR_CALLBACK',  16);
/**
 * WARNING: obsolete
 * @deprecated
 */
define('PEAR_ERROR_EXCEPTION', 32);
/**#@-*/
define('PEAR_ZE2', (function_exists('version_compare') &&
                    version_compare(zend_version(), "2-dev", "ge")));

if (substr(PHP_OS, 0, 3) == 'WIN') {
    define('OS_WINDOWS', true);
    define('OS_UNIX',    false);
    define('PEAR_OS',    'Windows');
} else {
    define('OS_WINDOWS', false);
    define('OS_UNIX',    true);
    define('PEAR_OS',    'Unix'); // blatant assumption
}

// instant backwards compatibility
if (!defined('PATH_SEPARATOR')) {
    if (OS_WINDOWS) {
        define('PATH_SEPARATOR', ';');
    } else {
        define('PATH_SEPARATOR', ':');
    }
}

$GLOBALS['_PEAR_default_error_mode']     = PEAR_ERROR_RETURN;
$GLOBALS['_PEAR_default_error_options']  = E_USER_NOTICE;
$GLOBALS['_PEAR_destructor_object_list'] = array();
$GLOBALS['_PEAR_shutdown_funcs']         = array();
$GLOBALS['_PEAR_error_handler_stack']    = array();

@ini_set('track_errors', true);

/**
 * Base class for other PEAR classes.  Provides rudimentary
 * emulation of destructors.
 *
 * If you want a destructor in your class, inherit PEAR and make a
 * destructor method called _yourclassname (same name as the
 * constructor, but with a "_" prefix).  Also, in your constructor you
 * have to call the PEAR constructor: $this->PEAR();.
 * The destructor method will be called without parameters.  Note that
 * at in some SAPI implementations (such as Apache), any output during
 * the request shutdown (in which destructors are called) seems to be
 * discarded.  If you need to get any debug information from your
 * destructor, use error_log(), syslog() or something similar.
 *
 * IMPORTANT! To use the emulated destructors you need to create the
 * objects by reference: $obj =& new PEAR_child;
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V.V. Cox <cox@idecnet.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2006 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @see        PEAR_Error
 * @since      Class available since PHP 4.0.2
 * @link        http://pear.php.net/manual/en/core.pear.php#core.pear.pear
 */
class PEAR
{
    // {{{ properties

    /**
     * Whether to enable internal debug messages.
     *
     * @var     bool
     * @access  private
     */
    var $_debug = false;

    /**
     * Default error mode for this object.
     *
     * @var     int
     * @access  private
     */
    var $_default_error_mode = null;

    /**
     * Default error options used for this object when error mode
     * is PEAR_ERROR_TRIGGER.
     *
     * @var     int
     * @access  private
     */
    var $_default_error_options = null;

    /**
     * Default error handler (callback) for this object, if error mode is
     * PEAR_ERROR_CALLBACK.
     *
     * @var     string
     * @access  private
     */
    var $_default_error_handler = '';

    /**
     * Which class to use for error objects.
     *
     * @var     string
     * @access  private
     */
    var $_error_class = 'PEAR_Error';

    /**
     * An array of expected errors.
     *
     * @var     array
     * @access  private
     */
    var $_expected_errors = array();

    // }}}

    // {{{ constructor

    /**
     * Constructor.  Registers this object in
     * $_PEAR_destructor_object_list for destructor emulation if a
     * destructor object exists.
     *
     * @param string $error_class  (optional) which class to use for
     *        error objects, defaults to PEAR_Error.
     * @access public
     * @return void
     */
    function PEAR($error_class = null)
    {
        $classname = strtolower(get_class($this));
        if ($this->_debug) {
            print "PEAR constructor called, class=$classname\n";
        }
        if ($error_class !== null) {
            $this->_error_class = $error_class;
        }
        while ($classname && strcasecmp($classname, "pear")) {
            $destructor = "_$classname";
            if (method_exists($this, $destructor)) {
                global $_PEAR_destructor_object_list;
                $_PEAR_destructor_object_list[] = &$this;
                if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
                    register_shutdown_function("_PEAR_call_destructors");
                    $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
                }
                break;
            } else {
                $classname = get_parent_class($classname);
            }
        }
    }

    // }}}
    // {{{ destructor

    /**
     * Destructor (the emulated type of...).  Does nothing right now,
     * but is included for forward compatibility, so subclass
     * destructors should always call it.
     *
     * See the note in the class desciption about output from
     * destructors.
     *
     * @access public
     * @return void
     */
    function _PEAR() {
        if ($this->_debug) {
            printf("PEAR destructor called, class=%s\n", strtolower(get_class($this)));
        }
    }

    // }}}
    // {{{ getStaticProperty()

    /**
    * If you have a class that's mostly/entirely static, and you need static
    * properties, you can use this method to simulate them. Eg. in your method(s)
    * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar');
    * You MUST use a reference, or they will not persist!
    *
    * @access public
    * @param  string $class  The calling classname, to prevent clashes
    * @param  string $var    The variable to retrieve.
    * @return mixed   A reference to the variable. If not set it will be
    *                 auto initialised to NULL.
    */
    function &getStaticProperty($class, $var)
    {
        static $properties;
        if (!isset($properties[$class])) {
            $properties[$class] = array();
        }
        if (!array_key_exists($var, $properties[$class])) {
            $properties[$class][$var] = null;
        }
        return $properties[$class][$var];
    }

    // }}}
    // {{{ registerShutdownFunc()

    /**
    * Use this function to register a shutdown method for static
    * classes.
    *
    * @access public
    * @param  mixed $func  The function name (or array of class/method) to call
    * @param  mixed $args  The arguments to pass to the function
    * @return void
    */
    function registerShutdownFunc($func, $args = array())
    {
        // if we are called statically, there is a potential
        // that no shutdown func is registered.  Bug #6445
        if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
            register_shutdown_function("_PEAR_call_destructors");
            $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
        }
        $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
    }

    // }}}
    // {{{ isError()

    /**
     * Tell whether a value is a PEAR error.
     *
     * @param   mixed $data   the value to test
     * @param   int   $code   if $data is an error object, return true
     *                        only if $code is a string and
     *                        $obj->getMessage() == $code or
     *                        $code is an integer and $obj->getCode() == $code
     * @access  public
     * @return  bool    true if parameter is an error
     */
    function isError($data, $code = null)
    {
        if (is_a($data, 'PEAR_Error')) {
            if (is_null($code)) {
                return true;
            } elseif (is_string($code)) {
                return $data->getMessage() == $code;
            } else {
                return $data->getCode() == $code;
            }
        }
        return false;
    }

    // }}}
    // {{{ setErrorHandling()

    /**
     * Sets how errors generated by this object should be handled.
     * Can be invoked both in objects and statically.  If called
     * statically, setErrorHandling sets the default behaviour for all
     * PEAR objects.  If called in an object, setErrorHandling sets
     * the default behaviour for that object.
     *
     * @param int $mode
     *        One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
     *        PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
     *        PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
     *
     * @param mixed $options
     *        When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
     *        of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
     *
     *        When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
     *        to be the callback function or method.  A callback
     *        function is a string with the name of the function, a
     *        callback method is an array of two elements: the element
     *        at index 0 is the object, and the element at index 1 is
     *        the name of the method to call in the object.
     *
     *        When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
     *        a printf format string used when printing the error
     *        message.
     *
     * @access public
     * @return void
     * @see PEAR_ERROR_RETURN
     * @see PEAR_ERROR_PRINT
     * @see PEAR_ERROR_TRIGGER
     * @see PEAR_ERROR_DIE
     * @see PEAR_ERROR_CALLBACK
     * @see PEAR_ERROR_EXCEPTION
     *
     * @since PHP 4.0.5
     */

    function setErrorHandling($mode = null, $options = null)
    {
        if (isset($this) && is_a($this, 'PEAR')) {
            $setmode     = &$this->_default_error_mode;
            $setoptions  = &$this->_default_error_options;
        } else {
            $setmode     = &$GLOBALS['_PEAR_default_error_mode'];
            $setoptions  = &$GLOBALS['_PEAR_default_error_options'];
        }

        switch ($mode) {
            case PEAR_ERROR_EXCEPTION:
            case PEAR_ERROR_RETURN:
            case PEAR_ERROR_PRINT:
            case PEAR_ERROR_TRIGGER:
            case PEAR_ERROR_DIE:
            case null:
                $setmode = $mode;
                $setoptions = $options;
                break;

            case PEAR_ERROR_CALLBACK:
                $setmode = $mode;
                // class/object method callback
                if (is_callable($options)) {
                    $setoptions = $options;
                } else {
                    trigger_error("invalid error callback", E_USER_WARNING);
                }
                break;

            default:
                trigger_error("invalid error mode", E_USER_WARNING);
                break;
        }
    }

    // }}}
    // {{{ expectError()

    /**
     * This method is used to tell which errors you expect to get.
     * Expected errors are always returned with error mode
     * PEAR_ERROR_RETURN.  Expected error codes are stored in a stack,
     * and this method pushes a new element onto it.  The list of
     * expected errors are in effect until they are popped off the
     * stack with the popExpect() method.
     *
     * Note that this method can not be called statically
     *
     * @param mixed $code a single error code or an array of error codes to expect
     *
     * @return int     the new depth of the "expected errors" stack
     * @access public
     */
    function expectError($code = '*')
    {
        if (is_array($code)) {
            array_push($this->_expected_errors, $code);
        } else {
            array_push($this->_expected_errors, array($code));
        }
        return sizeof($this->_expected_errors);
    }

    // }}}
    // {{{ popExpect()

    /**
     * This method pops one element off the expected error codes
     * stack.
     *
     * @return array   the list of error codes that were popped
     */
    function popExpect()
    {
        return array_pop($this->_expected_errors);
    }

    // }}}
    // {{{ _checkDelExpect()

    /**
     * This method checks unsets an error code if available
     *
     * @param mixed error code
     * @return bool true if the error code was unset, false otherwise
     * @access private
     * @since PHP 4.3.0
     */
    function _checkDelExpect($error_code)
    {
        $deleted = false;

        foreach ($this->_expected_errors AS $key => $error_array) {
            if (in_array($error_code, $error_array)) {
                unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
                $deleted = true;
            }

            // clean up empty arrays
            if (0 == count($this->_expected_errors[$key])) {
                unset($this->_expected_errors[$key]);
            }
        }
        return $deleted;
    }

    // }}}
    // {{{ delExpect()

    /**
     * This method deletes all occurences of the specified element from
     * the expected error codes stack.
     *
     * @param  mixed $error_code error code that should be deleted
     * @return mixed list of error codes that were deleted or error
     * @access public
     * @since PHP 4.3.0
     */
    function delExpect($error_code)
    {
        $deleted = false;

        if ((is_array($error_code) && (0 != count($error_code)))) {
            // $error_code is a non-empty array here;
            // we walk through it trying to unset all
            // values
            foreach($error_code as $key => $error) {
                if ($this->_checkDelExpect($error)) {
                    $deleted =  true;
                } else {
                    $deleted = false;
                }
            }
            return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
        } elseif (!empty($error_code)) {
            // $error_code comes alone, trying to unset it
            if ($this->_checkDelExpect($error_code)) {
                return true;
            } else {
                return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
            }
        } else {
            // $error_code is empty
            return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
        }
    }

    // }}}
    // {{{ raiseError()

    /**
     * This method is a wrapper that returns an instance of the
     * configured error class with this object's default error
     * handling applied.  If the $mode and $options parameters are not
     * specified, the object's defaults are used.
     *
     * @param mixed $message a text error message or a PEAR error object
     *
     * @param int $code      a numeric error code (it is up to your class
     *                  to define these if you want to use codes)
     *
     * @param int $mode      One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
     *                  PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
     *                  PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
     *
     * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
     *                  specifies the PHP-internal error level (one of
     *                  E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
     *                  If $mode is PEAR_ERROR_CALLBACK, this
     *                  parameter specifies the callback function or
     *                  method.  In other error modes this parameter
     *                  is ignored.
     *
     * @param string $userinfo If you need to pass along for example debug
     *                  information, this parameter is meant for that.
     *
     * @param string $error_class The returned error object will be
     *                  instantiated from this class, if specified.
     *
     * @param bool $skipmsg If true, raiseError will only pass error codes,
     *                  the error message parameter will be dropped.
     *
     * @access public
     * @return object   a PEAR error object
     * @see PEAR::setErrorHandling
     * @since PHP 4.0.5
     */
    function &raiseError($message = null,
                         $code = null,
                         $mode = null,
                         $options = null,
                         $userinfo = null,
                         $error_class = null,
                         $skipmsg = false)
    {
        // The error is yet a PEAR error object
        if (is_object($message)) {
            $code        = $message->getCode();
            $userinfo    = $message->getUserInfo();
            $error_class = $message->getType();
            $message->error_message_prefix = '';
            $message     = $message->getMessage();
        }

        if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) {
            if ($exp[0] == "*" ||
                (is_int(reset($exp)) && in_array($code, $exp)) ||
                (is_string(reset($exp)) && in_array($message, $exp))) {
                $mode = PEAR_ERROR_RETURN;
            }
        }
        // No mode given, try global ones
        if ($mode === null) {
            // Class error handler
            if (isset($this) && isset($this->_default_error_mode)) {
                $mode    = $this->_default_error_mode;
                $options = $this->_default_error_options;
            // Global error handler
            } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
                $mode    = $GLOBALS['_PEAR_default_error_mode'];
                $options = $GLOBALS['_PEAR_default_error_options'];
            }
        }

        if ($error_class !== null) {
            $ec = $error_class;
        } elseif (isset($this) && isset($this->_error_class)) {
            $ec = $this->_error_class;
        } else {
            $ec = 'PEAR_Error';
        }
        if (intval(PHP_VERSION) < 5) {
            // little non-eval hack to fix bug #12147
            include 'phar://install-pear-nozlib.phar/' . 'PEAR/FixPHP5PEARWarnings.php';
            return $a;
        }
        if ($skipmsg) {
            $a = new $ec($code, $mode, $options, $userinfo);
        } else {
            $a = new $ec($message, $code, $mode, $options, $userinfo);
        }
        return $a;
    }

    // }}}
    // {{{ throwError()

    /**
     * Simpler form of raiseError with fewer options.  In most cases
     * message, code and userinfo are enough.
     *
     * @param string $message
     *
     */
    function &throwError($message = null,
                         $code = null,
                         $userinfo = null)
    {
        if (isset($this) && is_a($this, 'PEAR')) {
            $a = &$this->raiseError($message, $code, null, null, $userinfo);
            return $a;
        } else {
            $a = &PEAR::raiseError($message, $code, null, null, $userinfo);
            return $a;
        }
    }

    // }}}
    function staticPushErrorHandling($mode, $options = null)
    {
        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
        $def_mode    = &$GLOBALS['_PEAR_default_error_mode'];
        $def_options = &$GLOBALS['_PEAR_default_error_options'];
        $stack[] = array($def_mode, $def_options);
        switch ($mode) {
            case PEAR_ERROR_EXCEPTION:
            case PEAR_ERROR_RETURN:
            case PEAR_ERROR_PRINT:
            case PEAR_ERROR_TRIGGER:
            case PEAR_ERROR_DIE:
            case null:
                $def_mode = $mode;
                $def_options = $options;
                break;

            case PEAR_ERROR_CALLBACK:
                $def_mode = $mode;
                // class/object method callback
                if (is_callable($options)) {
                    $def_options = $options;
                } else {
                    trigger_error("invalid error callback", E_USER_WARNING);
                }
                break;

            default:
                trigger_error("invalid error mode", E_USER_WARNING);
                break;
        }
        $stack[] = array($mode, $options);
        return true;
    }

    function staticPopErrorHandling()
    {
        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
        $setmode     = &$GLOBALS['_PEAR_default_error_mode'];
        $setoptions  = &$GLOBALS['_PEAR_default_error_options'];
        array_pop($stack);
        list($mode, $options) = $stack[sizeof($stack) - 1];
        array_pop($stack);
        switch ($mode) {
            case PEAR_ERROR_EXCEPTION:
            case PEAR_ERROR_RETURN:
            case PEAR_ERROR_PRINT:
            case PEAR_ERROR_TRIGGER:
            case PEAR_ERROR_DIE:
            case null:
                $setmode = $mode;
                $setoptions = $options;
                break;

            case PEAR_ERROR_CALLBACK:
                $setmode = $mode;
                // class/object method callback
                if (is_callable($options)) {
                    $setoptions = $options;
                } else {
                    trigger_error("invalid error callback", E_USER_WARNING);
                }
                break;

            default:
                trigger_error("invalid error mode", E_USER_WARNING);
                break;
        }
        return true;
    }

    // {{{ pushErrorHandling()

    /**
     * Push a new error handler on top of the error handler options stack. With this
     * you can easily override the actual error handler for some code and restore
     * it later with popErrorHandling.
     *
     * @param mixed $mode (same as setErrorHandling)
     * @param mixed $options (same as setErrorHandling)
     *
     * @return bool Always true
     *
     * @see PEAR::setErrorHandling
     */
    function pushErrorHandling($mode, $options = null)
    {
        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
        if (isset($this) && is_a($this, 'PEAR')) {
            $def_mode    = &$this->_default_error_mode;
            $def_options = &$this->_default_error_options;
        } else {
            $def_mode    = &$GLOBALS['_PEAR_default_error_mode'];
            $def_options = &$GLOBALS['_PEAR_default_error_options'];
        }
        $stack[] = array($def_mode, $def_options);

        if (isset($this) && is_a($this, 'PEAR')) {
            $this->setErrorHandling($mode, $options);
        } else {
            PEAR::setErrorHandling($mode, $options);
        }
        $stack[] = array($mode, $options);
        return true;
    }

    // }}}
    // {{{ popErrorHandling()

    /**
    * Pop the last error handler used
    *
    * @return bool Always true
    *
    * @see PEAR::pushErrorHandling
    */
    function popErrorHandling()
    {
        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
        array_pop($stack);
        list($mode, $options) = $stack[sizeof($stack) - 1];
        array_pop($stack);
        if (isset($this) && is_a($this, 'PEAR')) {
            $this->setErrorHandling($mode, $options);
        } else {
            PEAR::setErrorHandling($mode, $options);
        }
        return true;
    }

    // }}}
    // {{{ loadExtension()

    /**
    * OS independant PHP extension load. Remember to take care
    * on the correct extension name for case sensitive OSes.
    *
    * @param string $ext The extension name
    * @return bool Success or not on the dl() call
    */
    function loadExtension($ext)
    {
        if (!extension_loaded($ext)) {
            // if either returns true dl() will produce a FATAL error, stop that
            if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {
                return false;
            }
            if (OS_WINDOWS) {
                $suffix = '.dll';
            } elseif (PHP_OS == 'HP-UX') {
                $suffix = '.sl';
            } elseif (PHP_OS == 'AIX') {
                $suffix = '.a';
            } elseif (PHP_OS == 'OSX') {
                $suffix = '.bundle';
            } else {
                $suffix = '.so';
            }
            return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
        }
        return true;
    }

    // }}}
}

// {{{ _PEAR_call_destructors()

function _PEAR_call_destructors()
{
    global $_PEAR_destructor_object_list;
    if (is_array($_PEAR_destructor_object_list) &&
        sizeof($_PEAR_destructor_object_list))
    {
        reset($_PEAR_destructor_object_list);
        if (PEAR::getStaticProperty('PEAR', 'destructlifo')) {
            $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list);
        }
        while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
            $classname = get_class($objref);
            while ($classname) {
                $destructor = "_$classname";
                if (method_exists($objref, $destructor)) {
                    $objref->$destructor();
                    break;
                } else {
                    $classname = get_parent_class($classname);
                }
            }
        }
        // Empty the object list to ensure that destructors are
        // not called more than once.
        $_PEAR_destructor_object_list = array();
    }

    // Now call the shutdown functions
    if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) {
        foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
            call_user_func_array($value[0], $value[1]);
        }
    }
}

// }}}
/**
 * Standard PEAR error class for PHP 4
 *
 * This class is supserseded by {@link PEAR_Exception} in PHP 5
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V.V. Cox <cox@idecnet.com>
 * @author     Gregory Beaver <cellog@php.net>
 * @copyright  1997-2006 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/manual/en/core.pear.pear-error.php
 * @see        PEAR::raiseError(), PEAR::throwError()
 * @since      Class available since PHP 4.0.2
 */
class PEAR_Error
{
    // {{{ properties

    var $error_message_prefix = '';
    var $mode                 = PEAR_ERROR_RETURN;
    var $level                = E_USER_NOTICE;
    var $code                 = -1;
    var $message              = '';
    var $userinfo             = '';
    var $backtrace            = null;

    // }}}
    // {{{ constructor

    /**
     * PEAR_Error constructor
     *
     * @param string $message  message
     *
     * @param int $code     (optional) error code
     *
     * @param int $mode     (optional) error mode, one of: PEAR_ERROR_RETURN,
     * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
     * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
     *
     * @param mixed $options   (optional) error level, _OR_ in the case of
     * PEAR_ERROR_CALLBACK, the callback function or object/method
     * tuple.
     *
     * @param string $userinfo (optional) additional user/debug info
     *
     * @access public
     *
     */
    function PEAR_Error($message = 'unknown error', $code = null,
                        $mode = null, $options = null, $userinfo = null)
    {
        if ($mode === null) {
            $mode = PEAR_ERROR_RETURN;
        }
        $this->message   = $message;
        $this->code      = $code;
        $this->mode      = $mode;
        $this->userinfo  = $userinfo;
        if (!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) {
            $this->backtrace = debug_backtrace();
            if (isset($this->backtrace[0]) && isset($this->backtrace[0]['object'])) {
                unset($this->backtrace[0]['object']);
            }
        }
        if ($mode & PEAR_ERROR_CALLBACK) {
            $this->level = E_USER_NOTICE;
            $this->callback = $options;
        } else {
            if ($options === null) {
                $options = E_USER_NOTICE;
            }
            $this->level = $options;
            $this->callback = null;
        }
        if ($this->mode & PEAR_ERROR_PRINT) {
            if (is_null($options) || is_int($options)) {
                $format = "%s";
            } else {
                $format = $options;
            }
            printf($format, $this->getMessage());
        }
        if ($this->mode & PEAR_ERROR_TRIGGER) {
            trigger_error($this->getMessage(), $this->level);
        }
        if ($this->mode & PEAR_ERROR_DIE) {
            $msg = $this->getMessage();
            if (is_null($options) || is_int($options)) {
                $format = "%s";
                if (substr($msg, -1) != "\n") {
                    $msg .= "\n";
                }
            } else {
                $format = $options;
            }
            die(sprintf($format, $msg));
        }
        if ($this->mode & PEAR_ERROR_CALLBACK) {
            if (is_callable($this->callback)) {
                call_user_func($this->callback, $this);
            }
        }
        if ($this->mode & PEAR_ERROR_EXCEPTION) {
            trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING);
            eval('$e = new Exception($this->message, $this->code);throw($e);');
        }
    }

    // }}}
    // {{{ getMode()

    /**
     * Get the error mode from an error object.
     *
     * @return int error mode
     * @access public
     */
    function getMode() {
        return $this->mode;
    }

    // }}}
    // {{{ getCallback()

    /**
     * Get the callback function/method from an error object.
     *
     * @return mixed callback function or object/method array
     * @access public
     */
    function getCallback() {
        return $this->callback;
    }

    // }}}
    // {{{ getMessage()


    /**
     * Get the error message from an error object.
     *
     * @return  string  full error message
     * @access public
     */
    function getMessage()
    {
        return ($this->error_message_prefix . $this->message);
    }


    // }}}
    // {{{ getCode()

    /**
     * Get error code from an error object
     *
     * @return int error code
     * @access public
     */
     function getCode()
     {
        return $this->code;
     }

    // }}}
    // {{{ getType()

    /**
     * Get the name of this error/exception.
     *
     * @return string error/exception name (type)
     * @access public
     */
    function getType()
    {
        return get_class($this);
    }

    // }}}
    // {{{ getUserInfo()

    /**
     * Get additional user-supplied information.
     *
     * @return string user-supplied information
     * @access public
     */
    function getUserInfo()
    {
        return $this->userinfo;
    }

    // }}}
    // {{{ getDebugInfo()

    /**
     * Get additional debug information supplied by the application.
     *
     * @return string debug information
     * @access public
     */
    function getDebugInfo()
    {
        return $this->getUserInfo();
    }

    // }}}
    // {{{ getBacktrace()

    /**
     * Get the call backtrace from where the error was generated.
     * Supported with PHP 4.3.0 or newer.
     *
     * @param int $frame (optional) what frame to fetch
     * @return array Backtrace, or NULL if not available.
     * @access public
     */
    function getBacktrace($frame = null)
    {
        if (defined('PEAR_IGNORE_BACKTRACE')) {
            return null;
        }
        if ($frame === null) {
            return $this->backtrace;
        }
        return $this->backtrace[$frame];
    }

    // }}}
    // {{{ addUserInfo()

    function addUserInfo($info)
    {
        if (empty($this->userinfo)) {
            $this->userinfo = $info;
        } else {
            $this->userinfo .= " ** $info";
        }
    }

    // }}}
    // {{{ toString()
    function __toString()
    {
        return $this->getMessage();
    }
    // }}}
    // {{{ toString()

    /**
     * Make a string representation of this object.
     *
     * @return string a string with an object summary
     * @access public
     */
    function toString() {
        $modes = array();
        $levels = array(E_USER_NOTICE  => 'notice',
                        E_USER_WARNING => 'warning',
                        E_USER_ERROR   => 'error');
        if ($this->mode & PEAR_ERROR_CALLBACK) {
            if (is_array($this->callback)) {
                $callback = (is_object($this->callback[0]) ?
                    strtolower(get_class($this->callback[0])) :
                    $this->callback[0]) . '::' .
                    $this->callback[1];
            } else {
                $callback = $this->callback;
            }
            return sprintf('[%s: message="%s" code=%d mode=callback '.
                           'callback=%s prefix="%s" info="%s"]',
                           strtolower(get_class($this)), $this->message, $this->code,
                           $callback, $this->error_message_prefix,
                           $this->userinfo);
        }
        if ($this->mode & PEAR_ERROR_PRINT) {
            $modes[] = 'print';
        }
        if ($this->mode & PEAR_ERROR_TRIGGER) {
            $modes[] = 'trigger';
        }
        if ($this->mode & PEAR_ERROR_DIE) {
            $modes[] = 'die';
        }
        if ($this->mode & PEAR_ERROR_RETURN) {
            $modes[] = 'return';
        }
        return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
                       'prefix="%s" info="%s"]',
                       strtolower(get_class($this)), $this->message, $this->code,
                       implode("|", $modes), $levels[$this->level],
                       $this->error_message_prefix,
                       $this->userinfo);
    }

    // }}}
}

/*
 * Local Variables:
 * mode: php
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 */
?>
<?php
/**
 * PEAR_ChannelFile, the channel handling class
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: ChannelFile.php,v 1.80 2008/01/03 20:26:34 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * Needed for error handling
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ErrorStack.php';
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/XMLParser.php';
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Common.php';

/**
 * Error code if the channel.xml <channel> tag does not contain a valid version
 */
define('PEAR_CHANNELFILE_ERROR_NO_VERSION', 1);
/**
 * Error code if the channel.xml <channel> tag version is not supported (version 1.0 is the only supported version,
 * currently
 */
define('PEAR_CHANNELFILE_ERROR_INVALID_VERSION', 2);

/**
 * Error code if parsing is attempted with no xml extension
 */
define('PEAR_CHANNELFILE_ERROR_NO_XML_EXT', 3);

/**
 * Error code if creating the xml parser resource fails
 */
define('PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER', 4);

/**
 * Error code used for all sax xml parsing errors
 */
define('PEAR_CHANNELFILE_ERROR_PARSER_ERROR', 5);

/**#@+
 * Validation errors
 */
/**
 * Error code when channel name is missing
 */
define('PEAR_CHANNELFILE_ERROR_NO_NAME', 6);
/**
 * Error code when channel name is invalid
 */
define('PEAR_CHANNELFILE_ERROR_INVALID_NAME', 7);
/**
 * Error code when channel summary is missing
 */
define('PEAR_CHANNELFILE_ERROR_NO_SUMMARY', 8);
/**
 * Error code when channel summary is multi-line
 */
define('PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY', 9);
/**
 * Error code when channel server is missing for xmlrpc or soap protocol
 */
define('PEAR_CHANNELFILE_ERROR_NO_HOST', 10);
/**
 * Error code when channel server is invalid for xmlrpc or soap protocol
 */
define('PEAR_CHANNELFILE_ERROR_INVALID_HOST', 11);
/**
 * Error code when a mirror name is invalid
 */
define('PEAR_CHANNELFILE_ERROR_INVALID_MIRROR', 21);
/**
 * Error code when a mirror type is invalid
 */
define('PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE', 22);
/**
 * Error code when an attempt is made to generate xml, but the parsed content is invalid
 */
define('PEAR_CHANNELFILE_ERROR_INVALID', 23);
/**
 * Error code when an empty package name validate regex is passed in
 */
define('PEAR_CHANNELFILE_ERROR_EMPTY_REGEX', 24);
/**
 * Error code when a <function> tag has no version
 */
define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION', 25);
/**
 * Error code when a <function> tag has no name
 */
define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME', 26);
/**
 * Error code when a <validatepackage> tag has no name
 */
define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME', 27);
/**
 * Error code when a <validatepackage> tag has no version attribute
 */
define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION', 28);
/**
 * Error code when a mirror does not exist but is called for in one of the set*
 * methods.
 */
define('PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND', 32);
/**
 * Error code when a server port is not numeric
 */
define('PEAR_CHANNELFILE_ERROR_INVALID_PORT', 33);
/**
 * Error code when <static> contains no version attribute
 */
define('PEAR_CHANNELFILE_ERROR_NO_STATICVERSION', 34);
/**
 * Error code when <baseurl> contains no type attribute in a <rest> protocol definition
 */
define('PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE', 35);
/** 
 * Error code when a mirror is defined and the channel.xml represents the __uri pseudo-channel
 */
define('PEAR_CHANNELFILE_URI_CANT_MIRROR', 36);
/** 
 * Error code when ssl attribute is present and is not "yes"
 */
define('PEAR_CHANNELFILE_ERROR_INVALID_SSL', 37);
/**#@-*/

/**
 * Mirror types allowed.  Currently only internet servers are recognized.
 */
$GLOBALS['_PEAR_CHANNELS_MIRROR_TYPES'] =  array('server');


/**
 * The Channel handling class
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_ChannelFile {
    /**
     * @access private
     * @var PEAR_ErrorStack
     * @access private
     */
    var $_stack;
    
    /**
     * Supported channel.xml versions, for parsing
     * @var array
     * @access private
     */
    var $_supportedVersions = array('1.0');

    /**
     * Parsed channel information
     * @var array
     * @access private
     */
    var $_channelInfo;

    /**
     * index into the subchannels array, used for parsing xml
     * @var int
     * @access private
     */
    var $_subchannelIndex;

    /**
     * index into the mirrors array, used for parsing xml
     * @var int
     * @access private
     */
    var $_mirrorIndex;
    
    /**
     * Flag used to determine the validity of parsed content
     * @var boolean
     * @access private
     */
    var $_isValid = false;

    function PEAR_ChannelFile()
    {
        $this->_stack = &new PEAR_ErrorStack('PEAR_ChannelFile');
        $this->_stack->setErrorMessageTemplate($this->_getErrorMessage());
        $this->_isValid = false;
    }
    
    /**
     * @return array
     * @access protected
     */
    function _getErrorMessage()
    {
        return
            array(
                PEAR_CHANNELFILE_ERROR_INVALID_VERSION =>
                    'While parsing channel.xml, an invalid version number "%version% was passed in, expecting one of %versions%',
                PEAR_CHANNELFILE_ERROR_NO_VERSION =>
                    'No version number found in <channel> tag',
                PEAR_CHANNELFILE_ERROR_NO_XML_EXT =>
                    '%error%',
                PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER =>
                    'Unable to create XML parser',
                PEAR_CHANNELFILE_ERROR_PARSER_ERROR =>
                    '%error%',
                PEAR_CHANNELFILE_ERROR_NO_NAME =>
                    'Missing channel name',
                PEAR_CHANNELFILE_ERROR_INVALID_NAME =>
                    'Invalid channel %tag% "%name%"',
                PEAR_CHANNELFILE_ERROR_NO_SUMMARY =>
                    'Missing channel summary',
                PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY =>
                    'Channel summary should be on one line, but is multi-line',
                PEAR_CHANNELFILE_ERROR_NO_HOST =>
                    'Missing channel server for %type% server',
                PEAR_CHANNELFILE_ERROR_INVALID_HOST =>
                    'Server name "%server%" is invalid for %type% server',
                PEAR_CHANNELFILE_ERROR_INVALID_MIRROR =>
                    'Invalid mirror name "%name%", mirror type %type%',
                PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE =>
                    'Invalid mirror type "%type%"',
                PEAR_CHANNELFILE_ERROR_INVALID =>
                    'Cannot generate xml, contents are invalid',
                PEAR_CHANNELFILE_ERROR_EMPTY_REGEX =>
                    'packagenameregex cannot be empty',
                PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION =>
                    '%parent% %protocol% function has no version',
                PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME =>
                    '%parent% %protocol% function has no name',
                PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE =>
                    '%parent% rest baseurl has no type',
                PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME =>
                    'Validation package has no name in <validatepackage> tag',
                PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION =>
                    'Validation package "%package%" has no version',
                PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND =>
                    'Mirror "%mirror%" does not exist',
                PEAR_CHANNELFILE_ERROR_INVALID_PORT =>
                    'Port "%port%" must be numeric',
                PEAR_CHANNELFILE_ERROR_NO_STATICVERSION =>
                    '<static> tag must contain version attribute',
                PEAR_CHANNELFILE_URI_CANT_MIRROR =>
                    'The __uri pseudo-channel cannot have mirrors',
                PEAR_CHANNELFILE_ERROR_INVALID_SSL =>
                    '%server% has invalid ssl attribute "%ssl%" can only be yes or not present',
            );
    }

    /**
     * @param string contents of package.xml file
     * @return bool success of parsing
     */
    function fromXmlString($data)
    {
        if (preg_match('/<channel\s+version="([0-9]+\.[0-9]+)"/', $data, $channelversion)) {
            if (!in_array($channelversion[1], $this->_supportedVersions)) {
                $this->_stack->push(PEAR_CHANNELFILE_ERROR_INVALID_VERSION, 'error',
                    array('version' => $channelversion[1]));
                return false;
            }
            $parser = new PEAR_XMLParser;
            $result = $parser->parse($data);
            if ($result !== true) {
                if ($result->getCode() == 1) {
                    $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_XML_EXT, 'error',
                        array('error' => $result->getMessage()));
                } else {
                    $this->_stack->push(PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER, 'error');
                }
                return false;
            }
            $this->_channelInfo = $parser->getData();
            return true;
        } else {
            $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_VERSION, 'error', array('xml' => $data));
            return false;
        }
    }
    
    /**
     * @return array
     */
    function toArray()
    {
        if (!$this->_isValid && !$this->validate()) {
            return false;
        }
        return $this->_channelInfo;
    }
    
    /**
     * @param array
     * @static
     * @return PEAR_ChannelFile|false false if invalid
     */
    function &fromArray($data, $compatibility = false, $stackClass = 'PEAR_ErrorStack')
    {
        $a = new PEAR_ChannelFile($compatibility, $stackClass);
        $a->_fromArray($data);
        if (!$a->validate()) {
            $a = false;
            return $a;
        }
        return $a;
    }

    /**
     * Unlike {@link fromArray()} this does not do any validation
     * @param array
     * @static
     * @return PEAR_ChannelFile
     */
    function &fromArrayWithErrors($data, $compatibility = false,
                                  $stackClass = 'PEAR_ErrorStack')
    {
        $a = new PEAR_ChannelFile($compatibility, $stackClass);
        $a->_fromArray($data);
        return $a;
    }
    
    /**
     * @param array
     * @access private
     */
    function _fromArray($data)
    {
        $this->_channelInfo = $data;
    }
    
    /**
     * Wrapper to {@link PEAR_ErrorStack::getErrors()}
     * @param boolean determines whether to purge the error stack after retrieving
     * @return array
     */
    function getErrors($purge = false)
    {
        return $this->_stack->getErrors($purge);
    }

    /**
     * Unindent given string (?)
     *
     * @param string $str The string that has to be unindented.
     * @return string
     * @access private
     */
    function _unIndent($str)
    {
        // remove leading newlines
        $str = preg_replace('/^[\r\n]+/', '', $str);
        // find whitespace at the beginning of the first line
        $indent_len = strspn($str, " \t");
        $indent = substr($str, 0, $indent_len);
        $data = '';
        // remove the same amount of whitespace from following lines
        foreach (explode("\n", $str) as $line) {
            if (substr($line, 0, $indent_len) == $indent) {
                $data .= substr($line, $indent_len) . "\n";
            }
        }
        return $data;
    }

    /**
     * Parse a channel.xml file.  Expects the name of
     * a channel xml file as input.
     *
     * @param string  $descfile  name of channel xml file
     * @return bool success of parsing
     */
    function fromXmlFile($descfile)
    {
        if (!file_exists($descfile) || !is_file($descfile) || !is_readable($descfile) ||
             (!$fp = fopen($descfile, 'r'))) {
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
            return PEAR::raiseError("Unable to open $descfile");
        }

        // read the whole thing so we only get one cdata callback
        // for each block of cdata
        fclose($fp);
        $data = file_get_contents($descfile);
        return $this->fromXmlString($data);
    }

    /**
     * Parse channel information from different sources
     *
     * This method is able to extract information about a channel
     * from an .xml file or a string
     *
     * @access public
     * @param  string Filename of the source or the source itself
     * @return bool
     */
    function fromAny($info)
    {
        if (is_string($info) && file_exists($info) && strlen($info) < 255) {
            $tmp = substr($info, -4);
            if ($tmp == '.xml') {
                $info = $this->fromXmlFile($info);
            } else {
                $fp = fopen($info, "r");
                $test = fread($fp, 5);
                fclose($fp);
                if ($test == "<?xml") {
                    $info = $this->fromXmlFile($info);
                }
            }
            if (PEAR::isError($info)) {
                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
                return PEAR::raiseError($info);
            }
        }
        if (is_string($info)) {
            $info = $this->fromXmlString($info);
        }
        return $info;
    }

    /**
     * Return an XML document based on previous parsing and modifications
     *
     * @return string XML data
     *
     * @access public
     */
    function toXml()
    {
        if (!$this->_isValid && !$this->validate()) {
            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID);
            return false;
        }
        if (!isset($this->_channelInfo['attribs']['version'])) {
            $this->_channelInfo['attribs']['version'] = '1.0';
        }
        $channelInfo = $this->_channelInfo;
        $ret = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
        $ret .= "<channel version=\"" .
            $channelInfo['attribs']['version'] . "\" xmlns=\"http://pear.php.net/channel-1.0\"
  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
  xsi:schemaLocation=\"http://pear.php.net/dtd/channel-"
            . $channelInfo['attribs']['version'] . " http://pear.php.net/dtd/channel-" .
            $channelInfo['attribs']['version'] . ".xsd\">
 <name>$channelInfo[name]</name>
 <summary>" . htmlspecialchars($channelInfo['summary'])."</summary>
";
        if (isset($channelInfo['suggestedalias'])) {
            $ret .= ' <suggestedalias>' . $channelInfo['suggestedalias'] . "</suggestedalias>\n";
        }
        if (isset($channelInfo['validatepackage'])) {
            $ret .= ' <validatepackage version="' .
                $channelInfo['validatepackage']['attribs']['version']. '">' .
                htmlspecialchars($channelInfo['validatepackage']['_content']) .
                "</validatepackage>\n";
        }
        $ret .= " <servers>\n";
        $ret .= '  <primary';
        if (isset($channelInfo['servers']['primary']['attribs']['ssl'])) {
            $ret .= ' ssl="' . $channelInfo['servers']['primary']['attribs']['ssl'] . '"';
        }
        if (isset($channelInfo['servers']['primary']['attribs']['port'])) {
            $ret .= ' port="' . $channelInfo['servers']['primary']['attribs']['port'] . '"';
        }
        $ret .= ">\n";
        if (isset($channelInfo['servers']['primary']['xmlrpc'])) {
            $ret .= $this->_makeXmlrpcXml($channelInfo['servers']['primary']['xmlrpc'], '   ');
        }
        if (isset($channelInfo['servers']['primary']['rest'])) {
            $ret .= $this->_makeRestXml($channelInfo['servers']['primary']['rest'], '   ');
        }
        if (isset($channelInfo['servers']['primary']['soap'])) {
            $ret .= $this->_makeSoapXml($channelInfo['servers']['primary']['soap'], '   ');
        }
        $ret .= "  </primary>\n";
        if (isset($channelInfo['servers']['mirror'])) {
            $ret .= $this->_makeMirrorsXml($channelInfo);
        }
        $ret .= " </servers>\n";
        $ret .= "</channel>";
        return str_replace("\r", "\n", str_replace("\r\n", "\n", $ret));
    }

    /**
     * Generate the <xmlrpc> tag
     * @access private
     */
    function _makeXmlrpcXml($info, $indent)
    {
        $ret = $indent . "<xmlrpc";
        if (isset($info['attribs']['path'])) {
            $ret .= ' path="' . htmlspecialchars($info['attribs']['path']) . '"';
        }
        $ret .= ">\n";
        $ret .= $this->_makeFunctionsXml($info['function'], "$indent ");
        $ret .= $indent . "</xmlrpc>\n";
        return $ret;
    }

    /**
     * Generate the <soap> tag
     * @access private
     */
    function _makeSoapXml($info, $indent)
    {
        $ret = $indent . "<soap";
        if (isset($info['attribs']['path'])) {
            $ret .= ' path="' . htmlspecialchars($info['attribs']['path']) . '"';
        }
        $ret .= ">\n";
        $ret .= $this->_makeFunctionsXml($info['function'], "$indent ");
        $ret .= $indent . "</soap>\n";
        return $ret;
    }

    /**
     * Generate the <rest> tag
     * @access private
     */
    function _makeRestXml($info, $indent)
    {
        $ret = $indent . "<rest>\n";
        if (!isset($info['baseurl'][0])) {
            $info['baseurl'] = array($info['baseurl']);
        }
        foreach ($info['baseurl'] as $url) {
            $ret .= "$indent <baseurl type=\"" . $url['attribs']['type'] . "\"";
            $ret .= ">" . $url['_content'] . "</baseurl>\n";
        }
        $ret .= $indent . "</rest>\n";
        return $ret;
    }

    /**
     * Generate the <mirrors> tag
     * @access private
     */
    function _makeMirrorsXml($channelInfo)
    {
        $ret = "";
        if (!isset($channelInfo['servers']['mirror'][0])) {
            $channelInfo['servers']['mirror'] = array($channelInfo['servers']['mirror']);
        }
        foreach ($channelInfo['servers']['mirror'] as $mirror) {
            $ret .= '  <mirror host="' . $mirror['attribs']['host'] . '"';
            if (isset($mirror['attribs']['port'])) {
                $ret .= ' port="' . $mirror['attribs']['port'] . '"';
            }
            if (isset($mirror['attribs']['ssl'])) {
                $ret .= ' ssl="' . $mirror['attribs']['ssl'] . '"';
            }
            $ret .= ">\n";
            if (isset($mirror['xmlrpc']) || isset($mirror['soap'])) {
                if (isset($mirror['xmlrpc'])) {
                    $ret .= $this->_makeXmlrpcXml($mirror['xmlrpc'], '   ');
                }
                if (isset($mirror['rest'])) {
                    $ret .= $this->_makeRestXml($mirror['rest'], '   ');
                }
                if (isset($mirror['soap'])) {
                    $ret .= $this->_makeSoapXml($mirror['soap'], '   ');
                }
                $ret .= "  </mirror>\n";
            } else {
                $ret .= "/>\n";
            }
        }
        return $ret;
    }

    /**
     * Generate the <functions> tag
     * @access private
     */
    function _makeFunctionsXml($functions, $indent, $rest = false)
    {
        $ret = '';
        if (!isset($functions[0])) {
            $functions = array($functions);
        }
        foreach ($functions as $function) {
            $ret .= "$indent<function version=\"" . $function['attribs']['version'] . "\"";
            if ($rest) {
                $ret .= ' uri="' . $function['attribs']['uri'] . '"';
            }
            $ret .= ">" . $function['_content'] . "</function>\n";
        }
        return $ret;
    }

    /**
     * Validation error.  Also marks the object contents as invalid
     * @param error code
     * @param array error information
     * @access private
     */
    function _validateError($code, $params = array())
    {
        $this->_stack->push($code, 'error', $params);
        $this->_isValid = false;
    }

    /**
     * Validation warning.  Does not mark the object contents invalid.
     * @param error code
     * @param array error information
     * @access private
     */
    function _validateWarning($code, $params = array())
    {
        $this->_stack->push($code, 'warning', $params);
    }

    /**
     * Validate parsed file.
     *
     * @access public
     * @return boolean
     */
    function validate()
    {
        $this->_isValid = true;
        $info = $this->_channelInfo;
        if (empty($info['name'])) {
            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_NAME);
        } elseif (!$this->validChannelServer($info['name'])) {
            if ($info['name'] != '__uri') {
                $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, array('tag' => 'name',
                    'name' => $info['name']));
            }
        }
        if (empty($info['summary'])) {
            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY);
        } elseif (strpos(trim($info['summary']), "\n") !== false) {
            $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY,
                array('summary' => $info['summary']));
        }
        if (isset($info['suggestedalias'])) {
            if (!$this->validChannelServer($info['suggestedalias'])) {
                $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
                    array('tag' => 'suggestedalias', 'name' =>$info['suggestedalias']));
            }
        }
        if (isset($info['localalias'])) {
            if (!$this->validChannelServer($info['localalias'])) {
                $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
                    array('tag' => 'localalias', 'name' =>$info['localalias']));
            }
        }
        if (isset($info['validatepackage'])) {
            if (!isset($info['validatepackage']['_content'])) {
                $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME);
            }
            if (!isset($info['validatepackage']['attribs']['version'])) {
                $content = isset($info['validatepackage']['_content']) ?
                    $info['validatepackage']['_content'] :
                    null;
                $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION,
                    array('package' => $content));
            }
        }
        if (isset($info['servers']['primary']['attribs']['port']) &&
              !is_numeric($info['servers']['primary']['attribs']['port'])) {
            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_PORT,
                array('port' => $info['servers']['primary']['attribs']['port']));
        }
        if (isset($info['servers']['primary']['attribs']['ssl']) &&
              $info['servers']['primary']['attribs']['ssl'] != 'yes') {
            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL,
                array('ssl' => $info['servers']['primary']['attribs']['ssl'],
                    'server' => $info['name']));
        }

        if (isset($info['servers']['primary']['xmlrpc']) &&
              isset($info['servers']['primary']['xmlrpc']['function'])) {
            $this->_validateFunctions('xmlrpc', $info['servers']['primary']['xmlrpc']['function']);
        }
        if (isset($info['servers']['primary']['soap']) &&
              isset($info['servers']['primary']['soap']['function'])) {
            $this->_validateFunctions('soap', $info['servers']['primary']['soap']['function']);
        }
        if (isset($info['servers']['primary']['rest']) &&
              isset($info['servers']['primary']['rest']['baseurl'])) {
            $this->_validateFunctions('rest', $info['servers']['primary']['rest']['baseurl']);
        }
        if (isset($info['servers']['mirror'])) {
            if ($this->_channelInfo['name'] == '__uri') {
                $this->_validateError(PEAR_CHANNELFILE_URI_CANT_MIRROR);
            }
            if (!isset($info['servers']['mirror'][0])) {
                $info['servers']['mirror'] = array($info['servers']['mirror']);
            }
            foreach ($info['servers']['mirror'] as $mirror) {
                if (!isset($mirror['attribs']['host'])) {
                    $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_HOST,
                      array('type' => 'mirror'));
                } elseif (!$this->validChannelServer($mirror['attribs']['host'])) {
                    $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_HOST,
                        array('server' => $mirror['attribs']['host'], 'type' => 'mirror'));
                }
                if (isset($mirror['attribs']['ssl']) && $mirror['attribs']['ssl'] != 'yes') {
                    $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL,
                        array('ssl' => $info['ssl'], 'server' => $mirror['attribs']['host']));
                }
                if (isset($mirror['xmlrpc'])) {
                    $this->_validateFunctions('xmlrpc',
                        $mirror['xmlrpc']['function'], $mirror['attribs']['host']);
                }
                if (isset($mirror['soap'])) {
                    $this->_validateFunctions('soap', $mirror['soap']['function'],
                        $mirror['attribs']['host']);
                }
                if (isset($mirror['rest'])) {
                    $this->_validateFunctions('rest', $mirror['rest']['baseurl'],
                        $mirror['attribs']['host']);
                }
            }
        }
        return $this->_isValid;
    }

    /**
     * @param string xmlrpc or soap - protocol name this function applies to
     * @param array the functions
     * @param string the name of the parent element (mirror name, for instance)
     */
    function _validateFunctions($protocol, $functions, $parent = '')
    {
        if (!isset($functions[0])) {
            $functions = array($functions);
        }
        foreach ($functions as $function) {
            if (!isset($function['_content']) || empty($function['_content'])) {
                $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME,
                    array('parent' => $parent, 'protocol' => $protocol));
            }
            if ($protocol == 'rest') {
                if (!isset($function['attribs']['type']) ||
                      empty($function['attribs']['type'])) {
                    $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_BASEURLTYPE,
                        array('parent' => $parent, 'protocol' => $protocol));
                }
            } else {
                if (!isset($function['attribs']['version']) ||
                      empty($function['attribs']['version'])) {
                    $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION,
                        array('parent' => $parent, 'protocol' => $protocol));
                }
            }
        }
    }

    /**
     * Test whether a string contains a valid channel server.
     * @param string $ver the package version to test
     * @return bool
     */
    function validChannelServer($server)
    {
        if ($server == '__uri') {
            return true;
        }
        return (bool) preg_match(PEAR_CHANNELS_SERVER_PREG, $server);
    }

    /**
     * @return string|false
     */
    function getName()
    {
        if (isset($this->_channelInfo['name'])) {
            return $this->_channelInfo['name'];
        } else {
            return false;
        }
    }

    /**
     * @return string|false
     */
    function getServer()
    {
        if (isset($this->_channelInfo['name'])) {
            return $this->_channelInfo['name'];
        } else {
            return false;
        }
    }

    /**
     * @return int|80 port number to connect to
     */
    function getPort($mirror = false)
    {
        if ($mirror) {
            if ($mir = $this->getMirror($mirror)) {
                if (isset($mir['attribs']['port'])) {
                    return $mir['attribs']['port'];
                } else {
                    if ($this->getSSL($mirror)) {
                        return 443;
                    }
                    return 80;
                }
            }
            return false;
        }
        if (isset($this->_channelInfo['servers']['primary']['attribs']['port'])) {
            return $this->_channelInfo['servers']['primary']['attribs']['port'];
        }
        if ($this->getSSL()) {
            return 443;
        }
        return 80;
    }

    /**
     * @return bool Determines whether secure sockets layer (SSL) is used to connect to this channel
     */
    function getSSL($mirror = false)
    {
        if ($mirror) {
            if ($mir = $this->getMirror($mirror)) {
                if (isset($mir['attribs']['ssl'])) {
                    return true;
                } else {
                    return false;
                }
            }
            return false;
        }
        if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) {
            return true;
        }
        return false;
    }

    /**
     * @return string|false
     */
    function getSummary()
    {
        if (isset($this->_channelInfo['summary'])) {
            return $this->_channelInfo['summary'];
        } else {
            return false;
        }
    }

    /**
     * @param string xmlrpc or soap
     * @param string|false mirror name or false for primary server
     */
    function getPath($protocol, $mirror = false)
    {   
        if (!in_array($protocol, array('xmlrpc', 'soap'))) {
            return false;
        }
        if ($mirror) {
            if (!($mir = $this->getMirror($mirror))) {
                return false;
            }
            if (isset($mir[$protocol]['attribs']['path'])) {
                return $mir[$protocol]['attribs']['path'];
            } else {
                return $protocol . '.php';
            }
        } elseif (isset($this->_channelInfo['servers']['primary'][$protocol]['attribs']['path'])) {
            return $this->_channelInfo['servers']['primary'][$protocol]['attribs']['path'];
        }
        return $protocol . '.php';
    }

    /**
     * @param string protocol type (xmlrpc, soap)
     * @param string Mirror name
     * @return array|false
     */
    function getFunctions($protocol, $mirror = false)
    {
        if ($this->getName() == '__uri') {
            return false;
        }
        if ($protocol == 'rest') {
            $function = 'baseurl';
        } else {
            $function = 'function';
        }
        if ($mirror) {
            if ($mir = $this->getMirror($mirror)) {
                if (isset($mir[$protocol][$function])) {
                    return $mir[$protocol][$function];
                }
            }
            return false;
        }
        if (isset($this->_channelInfo['servers']['primary'][$protocol][$function])) {
            return $this->_channelInfo['servers']['primary'][$protocol][$function];
        } else {
            return false;
        }
    }

    /**
     * @param string Protocol type
     * @param string Function name (null to return the
     *               first protocol of the type requested)
     * @param string Mirror name, if any
     * @return array
     */
     function getFunction($type, $name = null, $mirror = false)
     {
        $protocols = $this->getFunctions($type, $mirror);
        if (!$protocols) {
            return false;
        }
        foreach ($protocols as $protocol) {
            if ($name === null) {
                return $protocol;
            }
            if ($protocol['_content'] != $name) {
                continue;
            }
            return $protocol;
        }
        return false;
     }

    /**
     * @param string protocol type
     * @param string protocol name
     * @param string version
     * @param string mirror name
     * @return boolean
     */
    function supports($type, $name = null, $mirror = false, $version = '1.0')
    {
        $protocols = $this->getFunctions($type, $mirror);
        if (!$protocols) {
            return false;
        }
        foreach ($protocols as $protocol) {
            if ($protocol['attribs']['version'] != $version) {
                continue;
            }
            if ($name === null) {
                return true;
            }
            if ($protocol['_content'] != $name) {
                continue;
            }
            return true;
        }
        return false;
    }

    /**
     * Determines whether a channel supports Representational State Transfer (REST) protocols
     * for retrieving channel information
     * @param string
     * @return bool
     */
    function supportsREST($mirror = false)
    {
        if ($mirror == $this->_channelInfo['name']) {
            $mirror = false;
        }
        if ($mirror) {
            if ($mir = $this->getMirror($mirror)) {
                return isset($mir['rest']);
            }
            return false;
        }
        return isset($this->_channelInfo['servers']['primary']['rest']);
    }

    /**
     * Get the URL to access a base resource.
     *
     * Hyperlinks in the returned xml will be used to retrieve the proper information
     * needed.  This allows extreme extensibility and flexibility in implementation
     * @param string Resource Type to retrieve
     */
    function getBaseURL($resourceType, $mirror = false)
    {
        if ($mirror == $this->_channelInfo['name']) {
            $mirror = false;
        }
        if ($mirror) {
            if ($mir = $this->getMirror($mirror)) {
                $rest = $mir['rest'];
            } else {
                return false;
            }
        } else {
            $rest = $this->_channelInfo['servers']['primary']['rest'];
        }
        if (!isset($rest['baseurl'][0])) {
            $rest['baseurl'] = array($rest['baseurl']);
        }
        foreach ($rest['baseurl'] as $baseurl) {
            if (strtolower($baseurl['attribs']['type']) == strtolower($resourceType)) {
                return $baseurl['_content'];
            }
        }
        return false;
    }

    /**
     * Since REST does not implement RPC, provide this as a logical wrapper around
     * resetFunctions for REST
     * @param string|false mirror name, if any
     */
    function resetREST($mirror = false)
    {
        return $this->resetFunctions('rest', $mirror);
    }

    /**
     * Empty all protocol definitions
     * @param string protocol type (xmlrpc, soap)
     * @param string|false mirror name, if any
     */
    function resetFunctions($type, $mirror = false)
    {
        if ($mirror) {
            if (isset($this->_channelInfo['servers']['mirror'])) {
                $mirrors = $this->_channelInfo['servers']['mirror'];
                if (!isset($mirrors[0])) {
                    $mirrors = array($mirrors);
                }
                foreach ($mirrors as $i => $mir) {
                    if ($mir['attribs']['host'] == $mirror) {
                        if (isset($this->_channelInfo['servers']['mirror'][$i][$type])) {
                            unset($this->_channelInfo['servers']['mirror'][$i][$type]);
                        }
                        return true;
                    }
                }
                return false;
            } else {
                return false;
            }
        } else {
            if (isset($this->_channelInfo['servers']['primary'][$type])) {
                unset($this->_channelInfo['servers']['primary'][$type]);
            }
            return true;
        }
    }

    /**
     * Set a channel's protocols to the protocols supported by pearweb
     */
    function setDefaultPEARProtocols($version = '1.0', $mirror = false)
    {
        switch ($version) {
            case '1.0' :
                $this->resetFunctions('xmlrpc', $mirror);
                $this->resetFunctions('soap', $mirror);
                $this->resetREST($mirror);
                $this->addFunction('xmlrpc', '1.0', 'logintest', $mirror);
                $this->addFunction('xmlrpc', '1.0', 'package.listLatestReleases', $mirror);
                $this->addFunction('xmlrpc', '1.0', 'package.listAll', $mirror);
                $this->addFunction('xmlrpc', '1.0', 'package.info', $mirror);
                $this->addFunction('xmlrpc', '1.0', 'package.getDownloadURL', $mirror);
                $this->addFunction('xmlrpc', '1.1', 'package.getDownloadURL', $mirror);
                $this->addFunction('xmlrpc', '1.0', 'package.getDepDownloadURL', $mirror);
                $this->addFunction('xmlrpc', '1.1', 'package.getDepDownloadURL', $mirror);
                $this->addFunction('xmlrpc', '1.0', 'package.search', $mirror);
                $this->addFunction('xmlrpc', '1.0', 'channel.listAll', $mirror);
                return true;
            break;
            default :
                return false;
            break;
        }
    }
    
    /**
     * @return array
     */
    function getMirrors()
    {
        if (isset($this->_channelInfo['servers']['mirror'])) {
            $mirrors = $this->_channelInfo['servers']['mirror'];
            if (!isset($mirrors[0])) {
                $mirrors = array($mirrors);
            }
            return $mirrors;
        } else {
            return array();
        }
    }

    /**
     * Get the unserialized XML representing a mirror
     * @return array|false
     */
    function getMirror($server)
    {
        foreach ($this->getMirrors() as $mirror) {
            if ($mirror['attribs']['host'] == $server) {
                return $mirror;
            }
        }
        return false;
    }

    /**
     * @param string
     * @return string|false
     * @error PEAR_CHANNELFILE_ERROR_NO_NAME
     * @error PEAR_CHANNELFILE_ERROR_INVALID_NAME
     */
    function setName($name)
    {
        return $this->setServer($name);
    }

    /**
     * Set the socket number (port) that is used to connect to this channel
     * @param integer
     * @param string|false name of the mirror server, or false for the primary
     */
    function setPort($port, $mirror = false)
    {
        if ($mirror) {
            if (!isset($this->_channelInfo['servers']['mirror'])) {
                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
                    array('mirror' => $mirror));
                return false;
            }
            if (isset($this->_channelInfo['servers']['mirror'][0])) {
                foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
                    if ($mirror == $mir['attribs']['host']) {
                        $this->_channelInfo['servers']['mirror'][$i]['attribs']['port'] = $port;
                        return true;
                    }
                }
                return false;
            } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
                $this->_channelInfo['servers']['mirror']['attribs']['port'] = $port;
                $this->_isValid = false;
                return true;
            }
        }
        $this->_channelInfo['servers']['primary']['attribs']['port'] = $port;
        $this->_isValid = false;
        return true;
    }

    /**
     * Set the socket number (port) that is used to connect to this channel
     * @param bool Determines whether to turn on SSL support or turn it off
     * @param string|false name of the mirror server, or false for the primary
     */
    function setSSL($ssl = true, $mirror = false)
    {
        if ($mirror) {
            if (!isset($this->_channelInfo['servers']['mirror'])) {
                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
                    array('mirror' => $mirror));
                return false;
            }
            if (isset($this->_channelInfo['servers']['mirror'][0])) {
                foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
                    if ($mirror == $mir['attribs']['host']) {
                        if (!$ssl) {
                            if (isset($this->_channelInfo['servers']['mirror'][$i]
                                  ['attribs']['ssl'])) {
                                unset($this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl']);
                            }
                        } else {
                            $this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl'] = 'yes';
                        }
                        return true;
                    }
                }
                return false;
            } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
                if (!$ssl) {
                    if (isset($this->_channelInfo['servers']['mirror']['attribs']['ssl'])) {
                        unset($this->_channelInfo['servers']['mirror']['attribs']['ssl']);
                    }
                } else {
                    $this->_channelInfo['servers']['mirror']['attribs']['ssl'] = 'yes';
                }
                $this->_isValid = false;
                return true;
            }
        }
        if ($ssl) {
            $this->_channelInfo['servers']['primary']['attribs']['ssl'] = 'yes';
        } else {
            if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) {
                unset($this->_channelInfo['servers']['primary']['attribs']['ssl']);
            }
        }
        $this->_isValid = false;
        return true;
    }

    /**
     * Set the socket number (port) that is used to connect to this channel
     * @param integer
     * @param string|false name of the mirror server, or false for the primary
     */
    function setPath($protocol, $path, $mirror = false)
    {
        if (!in_array($protocol, array('xmlrpc', 'soap'))) {
            return false;
        }
        if ($mirror) {
            if (!isset($this->_channelInfo['servers']['mirror'])) {
                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
                    array('mirror' => $mirror));
                return false;
            }
            if (isset($this->_channelInfo['servers']['mirror'][0])) {
                foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
                    if ($mirror == $mir['attribs']['host']) {
                        $this->_channelInfo['servers']['mirror'][$i][$protocol]['attribs']['path'] =
                            $path;
                        return true;
                    }
                }
                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
                    array('mirror' => $mirror));
                return false;
            } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
                $this->_channelInfo['servers']['mirror'][$protocol]['attribs']['path'] = $path;
                $this->_isValid = false;
                return true;
            }
        }
        $this->_channelInfo['servers']['primary'][$protocol]['attribs']['path'] = $path;
        $this->_isValid = false;
        return true;
    }

    /**
     * @param string
     * @return string|false
     * @error PEAR_CHANNELFILE_ERROR_NO_SERVER
     * @error PEAR_CHANNELFILE_ERROR_INVALID_SERVER
     */
    function setServer($server, $mirror = false)
    {
        if (empty($server)) {
            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SERVER);
            return false;
        } elseif (!$this->validChannelServer($server)) {
            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
                array('tag' => 'name', 'name' => $server));
            return false;
        }
        if ($mirror) {
            $found = false;
            foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
                if ($mirror == $mir['attribs']['host']) {
                    $found = true;
                    break;
                }
            }
            if (!$found) {
                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
                    array('mirror' => $mirror));
                return false;
            }
            $this->_channelInfo['mirror'][$i]['attribs']['host'] = $server;
            return true;
        }
        $this->_channelInfo['name'] = $server;
        return true;
    }

    /**
     * @param string
     * @return boolean success
     * @error PEAR_CHANNELFILE_ERROR_NO_SUMMARY
     * @warning PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY
     */
    function setSummary($summary)
    {
        if (empty($summary)) {
            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY);
            return false;
        } elseif (strpos(trim($summary), "\n") !== false) {
            $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY,
                array('summary' => $summary));
        }
        $this->_channelInfo['summary'] = $summary;
        return true;
    }

    /**
     * @param string
     * @param boolean determines whether the alias is in channel.xml or local
     * @return boolean success
     */
    function setAlias($alias, $local = false)
    {
        if (!$this->validChannelServer($alias)) {
            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
                array('tag' => 'suggestedalias', 'name' => $alias));
            return false;
        }
        if ($local) {
            $this->_channelInfo['localalias'] = $alias;
        } else {
            $this->_channelInfo['suggestedalias'] = $alias;
        }
        return true;
    }

    /**
     * @return string
     */
    function getAlias()
    {
        if (isset($this->_channelInfo['localalias'])) {
            return $this->_channelInfo['localalias'];
        }
        if (isset($this->_channelInfo['suggestedalias'])) {
            return $this->_channelInfo['suggestedalias'];
        }
        if (isset($this->_channelInfo['name'])) {
            return $this->_channelInfo['name'];
        }
        return '';
    }

    /**
     * Set the package validation object if it differs from PEAR's default
     * The class must be includeable via changing _ in the classname to path separator,
     * but no checking of this is made.
     * @param string|false pass in false to reset to the default packagename regex
     * @return boolean success
     */
    function setValidationPackage($validateclass, $version)
    {
        if (empty($validateclass)) {
            unset($this->_channelInfo['validatepackage']);
        }
        $this->_channelInfo['validatepackage'] = array('_content' => $validateclass);
        $this->_channelInfo['validatepackage']['attribs'] = array('version' => $version);
    }

    /**
     * Add a protocol to the provides section
     * @param string protocol type
     * @param string protocol version
     * @param string protocol name, if any
     * @param string mirror name, if this is a mirror's protocol
     * @return bool
     */
    function addFunction($type, $version, $name = '', $mirror = false)
    {
        if ($mirror) {
            return $this->addMirrorFunction($mirror, $type, $version, $name);
        }
        $set = array('attribs' => array('version' => $version), '_content' => $name);
        if (!isset($this->_channelInfo['servers']['primary'][$type]['function'])) {
            if (!isset($this->_channelInfo['servers'])) {
                $this->_channelInfo['servers'] = array('primary' =>
                    array($type => array()));
            } elseif (!isset($this->_channelInfo['servers']['primary'])) {
                $this->_channelInfo['servers']['primary'] = array($type => array());
            }
            $this->_channelInfo['servers']['primary'][$type]['function'] = $set;
            $this->_isValid = false;
            return true;
        } elseif (!isset($this->_channelInfo['servers']['primary'][$type]['function'][0])) {
            $this->_channelInfo['servers']['primary'][$type]['function'] = array(
                $this->_channelInfo['servers']['primary'][$type]['function']);
        }
        $this->_channelInfo['servers']['primary'][$type]['function'][] = $set;
        return true;
    }
    /**
     * Add a protocol to a mirror's provides section
     * @param string mirror name (server)
     * @param string protocol type
     * @param string protocol version
     * @param string protocol name, if any
     */
    function addMirrorFunction($mirror, $type, $version, $name = '')
    {
        if (!isset($this->_channelInfo['servers']['mirror'])) {
            $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
                array('mirror' => $mirror));
            return false;
        }
        $setmirror = false;
        if (isset($this->_channelInfo['servers']['mirror'][0])) {
            foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
                if ($mirror == $mir['attribs']['host']) {
                    $setmirror = &$this->_channelInfo['servers']['mirror'][$i];
                    break;
                }
            }
        } else {
            if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
                $setmirror = &$this->_channelInfo['servers']['mirror'];
            }
        }
        if (!$setmirror) {
            $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
                array('mirror' => $mirror));
            return false;
        }
        $set = array('attribs' => array('version' => $version), '_content' => $name);
        if (!isset($setmirror[$type]['function'])) {
            $setmirror[$type]['function'] = $set;
            $this->_isValid = false;
            return true;
        } elseif (!isset($setmirror[$type]['function'][0])) {
            $setmirror[$type]['function'] = array($setmirror[$type]['function']);
        }
        $setmirror[$type]['function'][] = $set;
        $this->_isValid = false;
        return true;
    }

    /**
     * @param string Resource Type this url links to
     * @param string URL
     * @param string|false mirror name, if this is not a primary server REST base URL
     */
    function setBaseURL($resourceType, $url, $mirror = false)
    {
        if ($mirror) {
            if (!isset($this->_channelInfo['servers']['mirror'])) {
                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
                    array('mirror' => $mirror));
                return false;
            }
            $setmirror = false;
            if (isset($this->_channelInfo['servers']['mirror'][0])) {
                foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
                    if ($mirror == $mir['attribs']['host']) {
                        $setmirror = &$this->_channelInfo['servers']['mirror'][$i];
                        break;
                    }
                }
            } else {
                if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
                    $setmirror = &$this->_channelInfo['servers']['mirror'];
                }
            }
        } else {
            $setmirror = &$this->_channelInfo['servers']['primary'];
        }
        $set = array('attribs' => array('type' => $resourceType), '_content' => $url);
        if (!isset($setmirror['rest'])) {
            $setmirror['rest'] = array();
        }
        if (!isset($setmirror['rest']['baseurl'])) {
            $setmirror['rest']['baseurl'] = $set;
            $this->_isValid = false;
            return true;
        } elseif (!isset($setmirror['rest']['baseurl'][0])) {
            $setmirror['rest']['baseurl'] = array($setmirror['rest']['baseurl']);
        }
        foreach ($setmirror['rest']['baseurl'] as $i => $url) {
            if ($url['attribs']['type'] == $resourceType) {
                $this->_isValid = false;
                $setmirror['rest']['baseurl'][$i] = $set;
                return true;
            }
        }
        $setmirror['rest']['baseurl'][] = $set;
        $this->_isValid = false;
        return true;
    }

    /**
     * @param string mirror server
     * @param int mirror http port
     * @return boolean
     */
    function addMirror($server, $port = null)
    {
        if ($this->_channelInfo['name'] == '__uri') {
            return false; // the __uri channel cannot have mirrors by definition
        }
        $set = array('attribs' => array('host' => $server));
        if (is_numeric($port)) {
            $set['attribs']['port'] = $port;
        }
        if (!isset($this->_channelInfo['servers']['mirror'])) {
            $this->_channelInfo['servers']['mirror'] = $set;
            return true;
        } else {
            if (!isset($this->_channelInfo['servers']['mirror'][0])) {
                $this->_channelInfo['servers']['mirror'] =
                    array($this->_channelInfo['servers']['mirror']);
            }
        }
        $this->_channelInfo['servers']['mirror'][] = $set;
        return true;
    }

    /**
     * Retrieve the name of the validation package for this channel
     * @return string|false
     */
    function getValidationPackage()
    {
        if (!$this->_isValid && !$this->validate()) {
            return false;
        }
        if (!isset($this->_channelInfo['validatepackage'])) {
            return array('attribs' => array('version' => 'default'),
                '_content' => 'PEAR_Validate');
        }
        return $this->_channelInfo['validatepackage'];
    }

    /**
     * Retrieve the object that can be used for custom validation
     * @param string|false the name of the package to validate.  If the package is
     *                     the channel validation package, PEAR_Validate is returned
     * @return PEAR_Validate|false false is returned if the validation package
     *         cannot be located
     */
    function &getValidationObject($package = false)
    {
        if (!class_exists('PEAR_Validate')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Validate.php';
        }
        if (!$this->_isValid) {
            if (!$this->validate()) {
                $a = false;
                return $a;
            }
        }
        if (isset($this->_channelInfo['validatepackage'])) {
            if ($package == $this->_channelInfo['validatepackage']) {
                // channel validation packages are always validated by PEAR_Validate
                $val = &new PEAR_Validate;
                return $val;
            }
            if (!class_exists(str_replace('.', '_',
                  $this->_channelInfo['validatepackage']['_content']))) {
                if ($this->isIncludeable(str_replace('_', '/',
                      $this->_channelInfo['validatepackage']['_content']) . '.php')) {
                    include_once 'phar://install-pear-nozlib.phar/' . str_replace('_', '/',
                        $this->_channelInfo['validatepackage']['_content']) . '.php';
                    $vclass = str_replace('.', '_',
                        $this->_channelInfo['validatepackage']['_content']);
                    $val = &new $vclass;
                } else {
                    $a = false;
                    return $a;
                }
            } else {
                $vclass = str_replace('.', '_',
                    $this->_channelInfo['validatepackage']['_content']);
                $val = &new $vclass;
            }
        } else {
            $val = &new PEAR_Validate;
        }
        return $val;
    }

    function isIncludeable($path)
    {
        $possibilities = explode(PATH_SEPARATOR, ini_get('include_path'));
        foreach ($possibilities as $dir) {
            if (file_exists($dir . DIRECTORY_SEPARATOR . $path)
                  && is_readable($dir . DIRECTORY_SEPARATOR . $path)) {
                return true;
            }
        }
        return false;
    }

    /**
     * This function is used by the channel updater and retrieves a value set by
     * the registry, or the current time if it has not been set
     * @return string
     */
    function lastModified()
    {
        if (isset($this->_channelInfo['_lastmodified'])) {
            return $this->_channelInfo['_lastmodified'];
        }
        return time();
    }
}
?>
<?php
/**
 * PEAR_ChannelFile_Parser for parsing channel.xml
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Parser.php,v 1.5 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * base xml parser class
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/XMLParser.php';
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
/**
 * Parser for channel.xml
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_ChannelFile_Parser extends PEAR_XMLParser
{
    var $_config;
    var $_logger;
    var $_registry;

    function setConfig(&$c)
    {
        $this->_config = &$c;
        $this->_registry = &$c->getRegistry();
    }

    function setLogger(&$l)
    {
        $this->_logger = &$l;
    }

    function parse($data, $file)
    {
        if (PEAR::isError($err = parent::parse($data, $file))) {
            return $err;
        }
        $ret = new PEAR_ChannelFile;
        $ret->setConfig($this->_config);
        if (isset($this->_logger)) {
            $ret->setLogger($this->_logger);
        }
        $ret->fromArray($this->_unserializedData);
        // make sure the filelist is in the easy to read format needed
        $ret->flattenFilelist();
        $ret->setPackagefile($file, $archive);
        return $ret;
    }
}
?><?php
/**
 * PEAR_Command, command pattern class
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Command.php,v 1.39 2008/01/03 20:26:34 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * Needed for error handling
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Frontend.php';
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/XMLParser.php';

/**
 * List of commands and what classes they are implemented in.
 * @var array command => implementing class
 */
$GLOBALS['_PEAR_Command_commandlist'] = array();

/**
 * List of commands and their descriptions
 * @var array command => description
 */
$GLOBALS['_PEAR_Command_commanddesc'] = array();

/**
 * List of shortcuts to common commands.
 * @var array shortcut => command
 */
$GLOBALS['_PEAR_Command_shortcuts'] = array();

/**
 * Array of command objects
 * @var array class => object
 */
$GLOBALS['_PEAR_Command_objects'] = array();

/**
 * PEAR command class, a simple factory class for administrative
 * commands.
 *
 * How to implement command classes:
 *
 * - The class must be called PEAR_Command_Nnn, installed in the
 *   "PEAR/Common" subdir, with a method called getCommands() that
 *   returns an array of the commands implemented by the class (see
 *   PEAR/Command/Install.php for an example).
 *
 * - The class must implement a run() function that is called with three
 *   params:
 *
 *    (string) command name
 *    (array)  assoc array with options, freely defined by each
 *             command, for example:
 *             array('force' => true)
 *    (array)  list of the other parameters
 *
 *   The run() function returns a PEAR_CommandResponse object.  Use
 *   these methods to get information:
 *
 *    int getStatus()   Returns PEAR_COMMAND_(SUCCESS|FAILURE|PARTIAL)
 *                      *_PARTIAL means that you need to issue at least
 *                      one more command to complete the operation
 *                      (used for example for validation steps).
 *
 *    string getMessage()  Returns a message for the user.  Remember,
 *                         no HTML or other interface-specific markup.
 *
 *   If something unexpected happens, run() returns a PEAR error.
 *
 * - DON'T OUTPUT ANYTHING! Return text for output instead.
 *
 * - DON'T USE HTML! The text you return will be used from both Gtk,
 *   web and command-line interfaces, so for now, keep everything to
 *   plain text.
 *
 * - DON'T USE EXIT OR DIE! Always use pear errors.  From static
 *   classes do PEAR::raiseError(), from other classes do
 *   $this->raiseError().
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */
class PEAR_Command
{
    // {{{ factory()

    /**
     * Get the right object for executing a command.
     *
     * @param string $command The name of the command
     * @param object $config  Instance of PEAR_Config object
     *
     * @return object the command object or a PEAR error
     *
     * @access public
     * @static
     */
    function &factory($command, &$config)
    {
        if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
            PEAR_Command::registerCommands();
        }
        if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
            $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
        }
        if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
            $a = PEAR::raiseError("unknown command `$command'");
            return $a;
        }
        $class = $GLOBALS['_PEAR_Command_commandlist'][$command];
        if (!class_exists($class)) {
            require_once $GLOBALS['_PEAR_Command_objects'][$class];
        }
        if (!class_exists($class)) {
            $a = PEAR::raiseError("unknown command `$command'");
            return $a;
        }
        $ui =& PEAR_Command::getFrontendObject();
        $obj = &new $class($ui, $config);
        return $obj;
    }

    // }}}
    // {{{ & getObject()
    function &getObject($command)
    {
        $class = $GLOBALS['_PEAR_Command_commandlist'][$command];
        if (!class_exists($class)) {
            require_once $GLOBALS['_PEAR_Command_objects'][$class];
        }
        if (!class_exists($class)) {
            return PEAR::raiseError("unknown command `$command'");
        }
        $ui =& PEAR_Command::getFrontendObject();
        $config = &PEAR_Config::singleton();
        $obj = &new $class($ui, $config);
        return $obj;
    }

    // }}}
    // {{{ & getFrontendObject()

    /**
     * Get instance of frontend object.
     *
     * @return object|PEAR_Error
     * @static
     */
    function &getFrontendObject()
    {
        $a = &PEAR_Frontend::singleton();
        return $a;
    }

    // }}}
    // {{{ & setFrontendClass()

    /**
     * Load current frontend class.
     *
     * @param string $uiclass Name of class implementing the frontend
     *
     * @return object the frontend object, or a PEAR error
     * @static
     */
    function &setFrontendClass($uiclass)
    {
        $a = &PEAR_Frontend::setFrontendClass($uiclass);
        return $a;
    }

    // }}}
    // {{{ setFrontendType()

    /**
     * Set current frontend.
     *
     * @param string $uitype Name of the frontend type (for example "CLI")
     *
     * @return object the frontend object, or a PEAR error
     * @static
     */
    function setFrontendType($uitype)
    {
        $uiclass = 'PEAR_Frontend_' . $uitype;
        return PEAR_Command::setFrontendClass($uiclass);
    }

    // }}}
    // {{{ registerCommands()

    /**
     * Scan through the Command directory looking for classes
     * and see what commands they implement.
     *
     * @param bool   (optional) if FALSE (default), the new list of
     *               commands should replace the current one.  If TRUE,
     *               new entries will be merged with old.
     *
     * @param string (optional) where (what directory) to look for
     *               classes, defaults to the Command subdirectory of
     *               the directory from where this file (__FILE__) is
     *               included.
     *
     * @return bool TRUE on success, a PEAR error on failure
     *
     * @access public
     * @static
     */
    function registerCommands($merge = false, $dir = null)
    {
        $parser = new PEAR_XMLParser;
        if ($dir === null) {
            $dir = dirname(__FILE__) . '/Command';
        }
        if (!is_dir($dir)) {
            return PEAR::raiseError("registerCommands: opendir($dir) '$dir' does not exist or is not a directory");
        }
        $dp = @opendir($dir);
        if (empty($dp)) {
            return PEAR::raiseError("registerCommands: opendir($dir) failed");
        }
        if (!$merge) {
            $GLOBALS['_PEAR_Command_commandlist'] = array();
        }
        while ($entry = readdir($dp)) {
            if ($entry{0} == '.' || substr($entry, -4) != '.xml') {
                continue;
            }
            $class = "PEAR_Command_".substr($entry, 0, -4);
            $file = "$dir/$entry";
            $parser->parse(file_get_contents($file));
            $implements = $parser->getData();
            // List of commands
            if (empty($GLOBALS['_PEAR_Command_objects'][$class])) {
                $GLOBALS['_PEAR_Command_objects'][$class] = "$dir/" . substr($entry, 0, -4) .
                    '.php';
            }
            foreach ($implements as $command => $desc) {
                if ($command == 'attribs') {
                    continue;
                }
                if (isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
                    return PEAR::raiseError('Command "' . $command . '" already registered in ' .
                        'class "' . $GLOBALS['_PEAR_Command_commandlist'][$command] . '"');
                }
                $GLOBALS['_PEAR_Command_commandlist'][$command] = $class;
                $GLOBALS['_PEAR_Command_commanddesc'][$command] = $desc['summary'];
                if (isset($desc['shortcut'])) {
                    $shortcut = $desc['shortcut'];
                    if (isset($GLOBALS['_PEAR_Command_shortcuts'][$shortcut])) {
                        return PEAR::raiseError('Command shortcut "' . $shortcut . '" already ' .
                            'registered to command "' . $command . '" in class "' .
                            $GLOBALS['_PEAR_Command_commandlist'][$command] . '"');
                    }
                    $GLOBALS['_PEAR_Command_shortcuts'][$shortcut] = $command;
                }
                if (isset($desc['options']) && $desc['options']) {
                    foreach ($desc['options'] as $oname => $option) {
                        if (isset($option['shortopt']) && strlen($option['shortopt']) > 1) {
                            return PEAR::raiseError('Option "' . $oname . '" short option "' .
                                $option['shortopt'] . '" must be ' .
                                'only 1 character in Command "' . $command . '" in class "' .
                                $class . '"');
                        }
                    }
                }
            }
        }
        ksort($GLOBALS['_PEAR_Command_shortcuts']);
        ksort($GLOBALS['_PEAR_Command_commandlist']);
        @closedir($dp);
        return true;
    }

    // }}}
    // {{{ getCommands()

    /**
     * Get the list of currently supported commands, and what
     * classes implement them.
     *
     * @return array command => implementing class
     *
     * @access public
     * @static
     */
    function getCommands()
    {
        if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
            PEAR_Command::registerCommands();
        }
        return $GLOBALS['_PEAR_Command_commandlist'];
    }

    // }}}
    // {{{ getShortcuts()

    /**
     * Get the list of command shortcuts.
     *
     * @return array shortcut => command
     *
     * @access public
     * @static
     */
    function getShortcuts()
    {
        if (empty($GLOBALS['_PEAR_Command_shortcuts'])) {
            PEAR_Command::registerCommands();
        }
        return $GLOBALS['_PEAR_Command_shortcuts'];
    }

    // }}}
    // {{{ getGetoptArgs()

    /**
     * Compiles arguments for getopt.
     *
     * @param string $command     command to get optstring for
     * @param string $short_args  (reference) short getopt format
     * @param array  $long_args   (reference) long getopt format
     *
     * @return void
     *
     * @access public
     * @static
     */
    function getGetoptArgs($command, &$short_args, &$long_args)
    {
        if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
            PEAR_Command::registerCommands();
        }
        if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
            $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
        }
        if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
            return null;
        }
        $obj = &PEAR_Command::getObject($command);
        return $obj->getGetoptArgs($command, $short_args, $long_args);
    }

    // }}}
    // {{{ getDescription()

    /**
     * Get description for a command.
     *
     * @param  string $command Name of the command
     *
     * @return string command description
     *
     * @access public
     * @static
     */
    function getDescription($command)
    {
        if (!isset($GLOBALS['_PEAR_Command_commanddesc'][$command])) {
            return null;
        }
        return $GLOBALS['_PEAR_Command_commanddesc'][$command];
    }

    // }}}
    // {{{ getHelp()

    /**
     * Get help for command.
     *
     * @param string $command Name of the command to return help for
     *
     * @access public
     * @static
     */
    function getHelp($command)
    {
        $cmds = PEAR_Command::getCommands();
        if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
            $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
        }
        if (isset($cmds[$command])) {
            $obj = &PEAR_Command::getObject($command);
            return $obj->getHelp($command);
        }
        return false;
    }
    // }}}
}

?>
<?php
/**
 * PEAR_Command_Common base class
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Common.php,v 1.36 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * base class
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';

/**
 * PEAR commands base class
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */
class PEAR_Command_Common extends PEAR
{
    // {{{ properties

    /**
     * PEAR_Config object used to pass user system and configuration
     * on when executing commands
     *
     * @var PEAR_Config
     */
    var $config;
    /**
     * @var PEAR_Registry
     * @access protected
     */
    var $_registry;

    /**
     * User Interface object, for all interaction with the user.
     * @var object
     */
    var $ui;

    var $_deps_rel_trans = array(
                                 'lt' => '<',
                                 'le' => '<=',
                                 'eq' => '=',
                                 'ne' => '!=',
                                 'gt' => '>',
                                 'ge' => '>=',
                                 'has' => '=='
                                 );

    var $_deps_type_trans = array(
                                  'pkg' => 'package',
                                  'ext' => 'extension',
                                  'php' => 'PHP',
                                  'prog' => 'external program',
                                  'ldlib' => 'external library for linking',
                                  'rtlib' => 'external runtime library',
                                  'os' => 'operating system',
                                  'websrv' => 'web server',
                                  'sapi' => 'SAPI backend'
                                  );

    // }}}
    // {{{ constructor

    /**
     * PEAR_Command_Common constructor.
     *
     * @access public
     */
    function PEAR_Command_Common(&$ui, &$config)
    {
        parent::PEAR();
        $this->config = &$config;
        $this->ui = &$ui;
    }

    // }}}

    // {{{ getCommands()

    /**
     * Return a list of all the commands defined by this class.
     * @return array list of commands
     * @access public
     */
    function getCommands()
    {
        $ret = array();
        foreach (array_keys($this->commands) as $command) {
            $ret[$command] = $this->commands[$command]['summary'];
        }
        return $ret;
    }

    // }}}
    // {{{ getShortcuts()

    /**
     * Return a list of all the command shortcuts defined by this class.
     * @return array shortcut => command
     * @access public
     */
    function getShortcuts()
    {
        $ret = array();
        foreach (array_keys($this->commands) as $command) {
            if (isset($this->commands[$command]['shortcut'])) {
                $ret[$this->commands[$command]['shortcut']] = $command;
            }
        }
        return $ret;
    }

    // }}}
    // {{{ getOptions()

    function getOptions($command)
    {
        $shortcuts = $this->getShortcuts();
        if (isset($shortcuts[$command])) {
            $command = $shortcuts[$command];
        }
        if (isset($this->commands[$command]) &&
              isset($this->commands[$command]['options'])) {
            return $this->commands[$command]['options'];
        } else {
            return null;
        }
    }

    // }}}
    // {{{ getGetoptArgs()

    function getGetoptArgs($command, &$short_args, &$long_args)
    {
        $short_args = "";
        $long_args = array();
        if (empty($this->commands[$command]) || empty($this->commands[$command]['options'])) {
            return;
        }
        reset($this->commands[$command]['options']);
        while (list($option, $info) = each($this->commands[$command]['options'])) {
            $larg = $sarg = '';
            if (isset($info['arg'])) {
                if ($info['arg']{0} == '(') {
                    $larg = '==';
                    $sarg = '::';
                    $arg = substr($info['arg'], 1, -1);
                } else {
                    $larg = '=';
                    $sarg = ':';
                    $arg = $info['arg'];
                }
            }
            if (isset($info['shortopt'])) {
                $short_args .= $info['shortopt'] . $sarg;
            }
            $long_args[] = $option . $larg;
        }
    }

    // }}}
    // {{{ getHelp()
    /**
    * Returns the help message for the given command
    *
    * @param string $command The command
    * @return mixed A fail string if the command does not have help or
    *               a two elements array containing [0]=>help string,
    *               [1]=> help string for the accepted cmd args
    */
    function getHelp($command)
    {
        $config = &PEAR_Config::singleton();
        if (!isset($this->commands[$command])) {
            return "No such command \"$command\"";
        }
        $help = null;
        if (isset($this->commands[$command]['doc'])) {
            $help = $this->commands[$command]['doc'];
        }
        if (empty($help)) {
            // XXX (cox) Fallback to summary if there is no doc (show both?)
            if (!isset($this->commands[$command]['summary'])) {
                return "No help for command \"$command\"";
            }
            $help = $this->commands[$command]['summary'];
        }
        if (preg_match_all('/{config\s+([^\}]+)}/e', $help, $matches)) {
            foreach($matches[0] as $k => $v) {
                $help = preg_replace("/$v/", $config->get($matches[1][$k]), $help);
            }
        }
        return array($help, $this->getHelpArgs($command));
    }

    // }}}
    // {{{ getHelpArgs()
    /**
    * Returns the help for the accepted arguments of a command
    *
    * @param  string $command
    * @return string The help string
    */
    function getHelpArgs($command)
    {
        if (isset($this->commands[$command]['options']) &&
            count($this->commands[$command]['options']))
        {
            $help = "Options:\n";
            foreach ($this->commands[$command]['options'] as $k => $v) {
                if (isset($v['arg'])) {
                    if ($v['arg'][0] == '(') {
                        $arg = substr($v['arg'], 1, -1);
                        $sapp = " [$arg]";
                        $lapp = "[=$arg]";
                    } else {
                        $sapp = " $v[arg]";
                        $lapp = "=$v[arg]";
                    }
                } else {
                    $sapp = $lapp = "";
                }
                if (isset($v['shortopt'])) {
                    $s = $v['shortopt'];
                    $help .= "  -$s$sapp, --$k$lapp\n";
                } else {
                    $help .= "  --$k$lapp\n";
                }
                $p = "        ";
                $doc = rtrim(str_replace("\n", "\n$p", $v['doc']));
                $help .= "        $doc\n";
            }
            return $help;
        }
        return null;
    }

    // }}}
    // {{{ run()

    function run($command, $options, $params)
    {
        if (empty($this->commands[$command]['function'])) {
            // look for shortcuts
            foreach (array_keys($this->commands) as $cmd) {
                if (isset($this->commands[$cmd]['shortcut']) && $this->commands[$cmd]['shortcut'] == $command) {
                    if (empty($this->commands[$cmd]['function'])) {
                        return $this->raiseError("unknown command `$command'");
                    } else {
                        $func = $this->commands[$cmd]['function'];
                    }
                    $command = $cmd;
                    break;
                }
            }
        } else {
            $func = $this->commands[$command]['function'];
        }
        return $this->$func($command, $options, $params);
    }

    // }}}
}

?>
<?php
/**
 * PEAR_Command_Install (install, upgrade, upgrade-all, uninstall, bundle, run-scripts commands)
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Install.php,v 1.141 2008/05/13 18:32:29 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * base class
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Command/Common.php';

/**
 * PEAR commands for installation or deinstallation/upgrading of
 * packages.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */
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',
                    ),
                'loose' => array(
                    'shortopt' => 'l',
                    'doc' => 'do not check for recommended dependency version',
                    ),
                '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), use packagingroot for RPM',
                    ),
                'packagingroot' => array(
                    'shortopt' => 'P',
                    'arg' => 'DIR',
                    'doc' => 'root directory used when packaging files, like RPM packaging',
                    ),
                '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',
                    ),
                'offline' => array(
                    'shortopt' => 'O',
                    'doc' => 'do not attempt to download any urls or contact channels',
                    ),
                'pretend' => array(
                    'shortopt' => 'p',
                    'doc' => 'Only list the packages that would be downloaded',
                    ),
                ),
            'doc' => '[channel/]<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[-version/state][.tar]" : queries your default channel\'s server
({config master_server}) and downloads the newest package with
the preferred quality/state ({config preferred_state}).

To retrieve Package version 1.1, use "Package-1.1," to retrieve
Package state beta, use "Package-beta."  To retrieve an uncompressed
file, append .tar (make sure there is no file by the same name first)

To download a package from another channel, prefix with the channel name like
"channel/Package"

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',
                    ),
                'loose' => array(
                    'shortopt' => 'l',
                    'doc' => 'do not check for recommended dependency version',
                    ),
                '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',
                    ),
                'offline' => array(
                    'shortopt' => 'O',
                    'doc' => 'do not attempt to download any urls or contact channels',
                    ),
                'pretend' => array(
                    'shortopt' => 'p',
                    'doc' => 'Only list the packages that would be downloaded',
                    ),
                ),
            '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' => 'doUpgradeAll',
            '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), use packagingroot for RPM',
                    ),
                'ignore-errors' => array(
                    'doc' => 'force install even if there were errors',
                    ),
                'loose' => array(
                    'doc' => 'do not check for recommended dependency version',
                    ),
                ),
            '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',
                    ),
                'offline' => array(
                    'shortopt' => 'O',
                    'doc' => 'do not attempt to uninstall remotely',
                    ),
                ),
            'doc' => '[channel/]<package> ...
Uninstalls one or more PEAR packages.  More than one package may be
specified at once.  Prefix with channel name to uninstall from a
channel not in your default channel ({config default_channel})
'),
        '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.
'),
        'run-scripts' => array(
            'summary' => 'Run Post-Install Scripts bundled with a package',
            'function' => 'doRunScripts',
            'shortcut' => 'rs',
            'options' => array(
            ),
            'doc' => '<package>
Run post-installation scripts in package <package>, if any exist.
'),
    );

    // }}}
    // {{{ constructor

    /**
     * PEAR_Command_Install constructor.
     *
     * @access public
     */
    function PEAR_Command_Install(&$ui, &$config)
    {
        parent::PEAR_Command_Common($ui, $config);
    }

    // }}}

    /**
     * For unit testing purposes
     */
    function &getDownloader(&$ui, $options, &$config)
    {
        if (!class_exists('PEAR_Downloader')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Downloader.php';
        }
        $a = &new PEAR_Downloader($ui, $options, $config);
        return $a;
    }

    /**
     * For unit testing purposes
     */
    function &getInstaller(&$ui)
    {
        if (!class_exists('PEAR_Installer')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Installer.php';
        }
        $a = &new PEAR_Installer($ui);
        return $a;
    }

    function enableExtension($binaries, $type)
    {
        if (!($phpini = $this->config->get('php_ini', null, 'pear.php.net'))) {
            return PEAR::raiseError('configuration option "php_ini" is not set to php.ini location');
        }
        $ini = $this->_parseIni($phpini);
        if (PEAR::isError($ini)) {
            return $ini;
        }
        $line = 0;
        if ($type == 'extsrc' || $type == 'extbin') {
            $search = 'extensions';
            $enable = 'extension';
        } else {
            $search = 'zend_extensions';
            ob_start();
            phpinfo(INFO_GENERAL);
            $info = ob_get_contents();
            ob_end_clean();
            $debug = function_exists('leak') ? '_debug' : '';
            $ts = preg_match('Thread Safety.+enabled', $info) ? '_ts' : '';
            $enable = 'zend_extension' . $debug . $ts;
        }
        foreach ($ini[$search] as $line => $extension) {
            if (in_array($extension, $binaries, true) || in_array(
                  $ini['extension_dir'] . DIRECTORY_SEPARATOR . $extension, $binaries, true)) {
                // already enabled - assume if one is, all are
                return true;
            }
        }
        if ($line) {
            $newini = array_slice($ini['all'], 0, $line);
        } else {
            $newini = array();
        }
        foreach ($binaries as $binary) {
            if ($ini['extension_dir']) {
                $binary = basename($binary);
            }
            $newini[] = $enable . '="' . $binary . '"' . (OS_UNIX ? "\n" : "\r\n");
        }
        $newini = array_merge($newini, array_slice($ini['all'], $line));
        $fp = @fopen($phpini, 'wb');
        if (!$fp) {
            return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing');
        }
        foreach ($newini as $line) {
            fwrite($fp, $line);
        }
        fclose($fp);
        return true;
    }

    function disableExtension($binaries, $type)
    {
        if (!($phpini = $this->config->get('php_ini', null, 'pear.php.net'))) {
            return PEAR::raiseError('configuration option "php_ini" is not set to php.ini location');
        }
        $ini = $this->_parseIni($phpini);
        if (PEAR::isError($ini)) {
            return $ini;
        }
        $line = 0;
        if ($type == 'extsrc' || $type == 'extbin') {
            $search = 'extensions';
            $enable = 'extension';
        } else {
            $search = 'zend_extensions';
            ob_start();
            phpinfo(INFO_GENERAL);
            $info = ob_get_contents();
            ob_end_clean();
            $debug = function_exists('leak') ? '_debug' : '';
            $ts = preg_match('Thread Safety.+enabled', $info) ? '_ts' : '';
            $enable = 'zend_extension' . $debug . $ts;
        }
        $found = false;
        foreach ($ini[$search] as $line => $extension) {
            if (in_array($extension, $binaries, true) || in_array(
                  $ini['extension_dir'] . DIRECTORY_SEPARATOR . $extension, $binaries, true)) {
                $found = true;
                break;
            }
        }
        if (!$found) {
            // not enabled
            return true;
        }
        $fp = @fopen($phpini, 'wb');
        if (!$fp) {
            return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing');
        }
        if ($line) {
            $newini = array_slice($ini['all'], 0, $line);
            // delete the enable line
            $newini = array_merge($newini, array_slice($ini['all'], $line + 1));
        } else {
            $newini = array_slice($ini['all'], 1);
        }
        foreach ($newini as $line) {
            fwrite($fp, $line);
        }
        fclose($fp);
        return true;
    }

    function _parseIni($filename)
    {
        if (file_exists($filename)) {
            if (filesize($filename) > 300000) {
                return PEAR::raiseError('php.ini "' . $filename . '" is too large, aborting');
            }
            ob_start();
            phpinfo(INFO_GENERAL);
            $info = ob_get_contents();
            ob_end_clean();
            $debug = function_exists('leak') ? '_debug' : '';
            $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
            $zend_extension_line = 'zend_extension' . $debug . $ts;
            $all = @file($filename);
            if (!$all) {
                return PEAR::raiseError('php.ini "' . $filename .'" could not be read');
            }
            $zend_extensions = $extensions = array();
            // assume this is right, but pull from the php.ini if it is found
            $extension_dir = ini_get('extension_dir');
            foreach ($all as $linenum => $line) {
                $line = trim($line);
                if (!$line) {
                    continue;
                }
                if ($line[0] == ';') {
                    continue;
                }
                if (strtolower(substr($line, 0, 13)) == 'extension_dir') {
                    $line = trim(substr($line, 13));
                    if ($line[0] == '=') {
                        $x = trim(substr($line, 1));
                        $x = explode(';', $x);
                        $extension_dir = str_replace('"', '', array_shift($x));
                        continue;
                    }
                }
                if (strtolower(substr($line, 0, 9)) == 'extension') {
                    $line = trim(substr($line, 9));
                    if ($line[0] == '=') {
                        $x = trim(substr($line, 1));
                        $x = explode(';', $x);
                        $extensions[$linenum] = str_replace('"', '', array_shift($x));
                        continue;
                    }
                }
                if (strtolower(substr($line, 0, strlen($zend_extension_line))) ==
                      $zend_extension_line) {
                    $line = trim(substr($line, strlen($zend_extension_line)));
                    if ($line[0] == '=') {
                        $x = trim(substr($line, 1));
                        $x = explode(';', $x);
                        $zend_extensions[$linenum] = str_replace('"', '', array_shift($x));
                        continue;
                    }
                }
            }
            return array(
                'extensions' => $extensions,
                'zend_extensions' => $zend_extensions,
                'extension_dir' => $extension_dir,
                'all' => $all,
            );
        } else {
            return PEAR::raiseError('php.ini "' . $filename . '" does not exist');
        }
    }

    // {{{ doInstall()

    function doInstall($command, $options, $params)
    {
        if (!class_exists('PEAR_PackageFile')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile.php';
        }
        if (empty($this->installer)) {
            $this->installer = &$this->getInstaller($this->ui);
        }
        if ($command == 'upgrade' || $command == 'upgrade-all') {
            $options['upgrade'] = true;
        } else {
            $packages = $params;
        }
        if (isset($options['installroot']) && isset($options['packagingroot'])) {
            return $this->raiseError('ERROR: cannot use both --installroot and --packagingroot');
        }
        $reg = &$this->config->getRegistry();
        $instreg = &$reg; // instreg used to check if package is installed
        if (isset($options['packagingroot']) && !isset($options['upgrade'])) {
            $packrootphp_dir = $this->installer->_prependPath(
                $this->config->get('php_dir', null, 'pear.php.net'),
                $options['packagingroot']);
            $instreg = new PEAR_Registry($packrootphp_dir); // other instreg!

            if ($this->config->get('verbose') > 2) {
                $this->ui->outputData('using package root: ' . $options['packagingroot']);
            }
        }
        $abstractpackages = array();
        $otherpackages = array();
        // parse params
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        foreach($params as $param) {
            if (strpos($param, 'http://') === 0) {
                $otherpackages[] = $param;
                continue;
            }
            if (strpos($param, 'channel://') === false && @file_exists($param)) {
                if (isset($options['force'])) {
                    $otherpackages[] = $param;
                    continue;
                }
                $pkg = new PEAR_PackageFile($this->config);
                $pf = $pkg->fromAnyFile($param, PEAR_VALIDATE_DOWNLOADING);
                if (PEAR::isError($pf)) {
                    $otherpackages[] = $param;
                    continue;
                }
                if ($reg->packageExists($pf->getPackage(), $pf->getChannel()) &&
                      version_compare($pf->getVersion(),
                      $reg->packageInfo($pf->getPackage(), 'version', $pf->getChannel()),
                      '<=')) {
                    if ($this->config->get('verbose')) {
                        $this->ui->outputData('Ignoring installed package ' .
                            $reg->parsedPackageNameToString(
                            array('package' => $pf->getPackage(),
                                  'channel' => $pf->getChannel()), true));
                    }
                    continue;
                }
                $otherpackages[] = $param;
                continue;
            }
            $e = $reg->parsePackageName($param, $this->config->get('default_channel'));
            if (PEAR::isError($e)) {
                $otherpackages[] = $param;
            } else {
                $abstractpackages[] = $e;
            }
        }
        PEAR::staticPopErrorHandling();

        // if there are any local package .tgz or remote static url, we can't
        // filter.  The filter only works for abstract packages
        if (count($abstractpackages) && !isset($options['force'])) {
            // when not being forced, only do necessary upgrades/installs
            if (isset($options['upgrade'])) {
                $abstractpackages = $this->_filterUptodatePackages($abstractpackages,
                    $command);
            } else {
                foreach ($abstractpackages as $i => $package) {
                    if (isset($package['group'])) {
                        // do not filter out install groups
                        continue;
                    }
                    if ($instreg->packageExists($package['package'], $package['channel'])) {
                        if ($this->config->get('verbose')) {
                            $this->ui->outputData('Ignoring installed package ' .
                                $reg->parsedPackageNameToString($package, true));
                        }
                        unset($abstractpackages[$i]);
                    }
                }
            }
            $abstractpackages =
                array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages);
        } elseif (count($abstractpackages)) {
            $abstractpackages =
                array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages);
        }


        $packages = array_merge($abstractpackages, $otherpackages);
        if (!count($packages)) {
            $this->ui->outputData('Nothing to ' . $command);
            return true;
        }

        $this->downloader = &$this->getDownloader($this->ui, $options, $this->config);
        $errors = array();
        $binaries = array();
        $downloaded = array();
        $downloaded = &$this->downloader->download($packages);
        if (PEAR::isError($downloaded)) {
            return $this->raiseError($downloaded);
        }
        $errors = $this->downloader->getErrorMsgs();
        if (count($errors)) {
            $err = array();
            $err['data'] = array();
            foreach ($errors as $error) {
                $err['data'][] = array($error);
            }
            $err['headline'] = 'Install Errors';
            $this->ui->outputData($err);
            if (!count($downloaded)) {
                return $this->raiseError("$command failed");
            }
        }
        $data = array(
            'headline' => 'Packages that would be Installed'
        );
        if (isset($options['pretend'])) {
            foreach ($downloaded as $package) {
                $data['data'][] = array($reg->parsedPackageNameToString($package->getParsedPackage()));
            }
            $this->ui->outputData($data, 'pretend');
            return true;
        }
        $this->installer->setOptions($options);
        $this->installer->sortPackagesForInstall($downloaded);
        if (PEAR::isError($err = $this->installer->setDownloadedPackages($downloaded))) {
            $this->raiseError($err->getMessage());
            return true;
        }
        $extrainfo = array();
        $binaries = array();
        foreach ($downloaded as $param) {
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            $info = $this->installer->install($param, $options);
            PEAR::staticPopErrorHandling();
            if (PEAR::isError($info)) {
                $oldinfo = $info;
                $pkg = &$param->getPackageFile();
                if ($info->getCode() != PEAR_INSTALLER_NOBINARY) {
                    if (!($info = $pkg->installBinary($this->installer))) {
                        $this->ui->outputData('ERROR: ' .$oldinfo->getMessage());
                        continue;
                    }
                    // we just installed a different package than requested,
                    // let's change the param and info so that the rest of this works
                    $param = $info[0];
                    $info = $info[1];
                }
            }
            if (is_array($info)) {
                if ($param->getPackageType() == 'extsrc' ||
                      $param->getPackageType() == 'extbin' ||
                      $param->getPackageType() == 'zendextsrc' ||
                      $param->getPackageType() == 'zendextbin') {
                    $pkg = &$param->getPackageFile();
                    if ($instbin = $pkg->getInstalledBinary()) {
                        $instpkg = &$instreg->getPackage($instbin, $pkg->getChannel());
                    } else {
                        $instpkg = &$instreg->getPackage($pkg->getPackage(), $pkg->getChannel());
                    }

                    foreach ($instpkg->getFilelist() as $name => $atts) {
                        $pinfo = pathinfo($atts['installed_as']);
                        if (!isset($pinfo['extension']) ||
                              in_array($pinfo['extension'], array('c', 'h'))) {
                            continue; // make sure we don't match php_blah.h
                        }
                        if ((strpos($pinfo['basename'], 'php_') === 0 &&
                              $pinfo['extension'] == 'dll') ||
                              // most unices
                              $pinfo['extension'] == 'so' ||
                              // hp-ux
                              $pinfo['extension'] == 'sl') {
                            $binaries[] = array($atts['installed_as'], $pinfo);
                            break;
                        }
                    }
                    if (count($binaries)) {
                        foreach ($binaries as $pinfo) {
                            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
                            $ret = $this->enableExtension(array($pinfo[0]), $param->getPackageType());
                            PEAR::staticPopErrorHandling();
                            if (PEAR::isError($ret)) {
                                $extrainfo[] = $ret->getMessage();
                                if ($param->getPackageType() == 'extsrc' ||
                                      $param->getPackageType() == 'extbin') {
                                    $exttype = 'extension';
                                } else {
                                    ob_start();
                                    phpinfo(INFO_GENERAL);
                                    $info = ob_get_contents();
                                    ob_end_clean();
                                    $debug = function_exists('leak') ? '_debug' : '';
                                    $ts = preg_match('Thread Safety.+enabled', $info) ? '_ts' : '';
                                    $exttype = 'zend_extension' . $debug . $ts;
                                }
                                $extrainfo[] = 'You should add "' . $exttype . '=' .
                                    $pinfo[1]['basename'] . '" to php.ini';
                            } else {
                                $extrainfo[] = 'Extension ' . $instpkg->getProvidesExtension() .
                                    ' enabled in php.ini';
                            }
                        }
                    }
                }
                if ($this->config->get('verbose') > 0) {
                    $channel = $param->getChannel();
                    $label = $reg->parsedPackageNameToString(
                        array(
                            'channel' => $channel,
                            'package' => $param->getPackage(),
                            'version' => $param->getVersion(),
                        ));
                    $out = array('data' => "$command ok: $label");
                    if (isset($info['release_warnings'])) {
                        $out['release_warnings'] = $info['release_warnings'];
                    }
                    $this->ui->outputData($out, $command);
                    if (!isset($options['register-only']) && !isset($options['offline'])) {
                        if ($this->config->isDefinedLayer('ftp')) {
                            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
                            $info = $this->installer->ftpInstall($param);
                            PEAR::staticPopErrorHandling();
                            if (PEAR::isError($info)) {
                                $this->ui->outputData($info->getMessage());
                                $this->ui->outputData("remote install failed: $label");
                            } else {
                                $this->ui->outputData("remote install ok: $label");
                            }
                        }
                    }
                }
                $deps = $param->getDeps();
                if ($deps) {
                    if (isset($deps['group'])) {
                        $groups = $deps['group'];
                        if (!isset($groups[0])) {
                            $groups = array($groups);
                        }
                        foreach ($groups as $group) {
                            if ($group['attribs']['name'] == 'default') {
                                // default group is always installed, unless the user
                                // explicitly chooses to install another group
                                continue;
                            }
                            $extrainfo[] = $param->getPackage() . ': Optional feature ' .
                                $group['attribs']['name'] . ' available (' .
                                $group['attribs']['hint'] . ')';
                        }
                        $extrainfo[] = $param->getPackage() .
                            ': To install optional features use "pear install ' .
                            $reg->parsedPackageNameToString(
                                array('package' => $param->getPackage(),
                                      'channel' => $param->getChannel()), true) .
                                  '#featurename"';
                    }
                }
                $pkg = &$instreg->getPackage($param->getPackage(), $param->getChannel());
                // $pkg may be NULL if install is a 'fake' install via --packagingroot
                if (is_object($pkg)) {
                    $pkg->setConfig($this->config);
                    if ($list = $pkg->listPostinstallScripts()) {
                        $pn = $reg->parsedPackageNameToString(array('channel' =>
                           $param->getChannel(), 'package' => $param->getPackage()), true);
                        $extrainfo[] = $pn . ' has post-install scripts:';
                        foreach ($list as $file) {
                            $extrainfo[] = $file;
                        }
                        $extrainfo[] = $param->getPackage() .
                            ': Use "pear run-scripts ' . $pn . '" to finish setup.';
                        $extrainfo[] = 'DO NOT RUN SCRIPTS FROM UNTRUSTED SOURCES';
                    }
                }
            } else {
                return $this->raiseError("$command failed");
            }
        }
        if (count($extrainfo)) {
            foreach ($extrainfo as $info) {
                $this->ui->outputData($info);
            }
        }
        return true;
    }

    // }}}
    // {{{ doUpgradeAll()

    function doUpgradeAll($command, $options, $params)
    {
        $reg = &$this->config->getRegistry();
        $toUpgrade = array();
        foreach ($reg->listChannels() as $channel) {
            if ($channel == '__uri') {
                continue;
            }

            // parse name with channel
            foreach ($reg->listPackages($channel) as $name) {
                $toUpgrade[] = $reg->parsedPackageNameToString(array(
                        'channel' => $channel,
                        'package' => $name
                    ));
            }
        }

        $err = $this->doInstall('upgrade-all', $options, $toUpgrade);
        if (PEAR::isError($err)) {
            $this->ui->outputData($err->getMessage(), $command);
        }
   }

    // }}}
    // {{{ doUninstall()

    function doUninstall($command, $options, $params)
    {
        if (empty($this->installer)) {
            $this->installer = &$this->getInstaller($this->ui);
        }
        if (isset($options['remoteconfig'])) {
            $e = $this->config->readFTPConfigFile($options['remoteconfig']);
            if (!PEAR::isError($e)) {
                $this->installer->setConfig($this->config);
            }
        }
        if (sizeof($params) < 1) {
            return $this->raiseError("Please supply the package(s) you want to uninstall");
        }
        $reg = &$this->config->getRegistry();
        $newparams = array();
        $binaries = array();
        $badparams = array();
        foreach ($params as $pkg) {
            $channel = $this->config->get('default_channel');
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            $parsed = $reg->parsePackageName($pkg, $channel);
            PEAR::staticPopErrorHandling();
            if (!$parsed || PEAR::isError($parsed)) {
                $badparams[] = $pkg;
                continue;
            }
            $package = $parsed['package'];
            $channel = $parsed['channel'];
            $info = &$reg->getPackage($package, $channel);
            if ($info === null &&
                 ($channel == 'pear.php.net' || $channel == 'pecl.php.net')) {
                // make sure this isn't a package that has flipped from pear to pecl but
                // used a package.xml 1.0
                $testc = ($channel == 'pear.php.net') ? 'pecl.php.net' : 'pear.php.net';
                $info = &$reg->getPackage($package, $testc);
                if ($info !== null) {
                    $channel = $testc;
                }
            }
            if ($info === null) {
                $badparams[] = $pkg;
            } else {
                $newparams[] = &$info;
                // check for binary packages (this is an alias for those packages if so)
                if ($installedbinary = $info->getInstalledBinary()) {
                    $this->ui->log('adding binary package ' .
                        $reg->parsedPackageNameToString(array('channel' => $channel,
                            'package' => $installedbinary), true));
                    $newparams[] = &$reg->getPackage($installedbinary, $channel);
                }
                // add the contents of a dependency group to the list of installed packages
                if (isset($parsed['group'])) {
                    $group = $info->getDependencyGroup($parsed['group']);
                    if ($group) {
                        $installed = $reg->getInstalledGroup($group);
                        if ($installed) {
                            foreach ($installed as $i => $p) {
                                $newparams[] = &$installed[$i];
                            }
                        }
                    }
                }
            }
        }
        $err = $this->installer->sortPackagesForUninstall($newparams);
        if (PEAR::isError($err)) {
            $this->ui->outputData($err->getMessage(), $command);
            return true;
        }
        $params = $newparams;
        // twist this to use it to check on whether dependent packages are also being uninstalled
        // for circular dependencies like subpackages
        $this->installer->setUninstallPackages($newparams);
        $params = array_merge($params, $badparams);
        $binaries = array();
        foreach ($params as $pkg) {
            $this->installer->pushErrorHandling(PEAR_ERROR_RETURN);
            if ($err = $this->installer->uninstall($pkg, $options)) {
                $this->installer->popErrorHandling();
                if (PEAR::isError($err)) {
                    $this->ui->outputData($err->getMessage(), $command);
                    continue;
                }
                if ($pkg->getPackageType() == 'extsrc' ||
                      $pkg->getPackageType() == 'extbin' ||
                      $pkg->getPackageType() == 'zendextsrc' ||
                      $pkg->getPackageType() == 'zendextbin') {
                    if ($instbin = $pkg->getInstalledBinary()) {
                        continue; // this will be uninstalled later
                    }

                    foreach ($pkg->getFilelist() as $name => $atts) {
                        $pinfo = pathinfo($atts['installed_as']);
                        if (!isset($pinfo['extension']) ||
                              in_array($pinfo['extension'], array('c', 'h'))) {
                            continue; // make sure we don't match php_blah.h
                        }
                        if ((strpos($pinfo['basename'], 'php_') === 0 &&
                              $pinfo['extension'] == 'dll') ||
                              // most unices
                              $pinfo['extension'] == 'so' ||
                              // hp-ux
                              $pinfo['extension'] == 'sl') {
                            $binaries[] = array($atts['installed_as'], $pinfo);
                            break;
                        }
                    }
                    if (count($binaries)) {
                        foreach ($binaries as $pinfo) {
                            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
                            $ret = $this->disableExtension(array($pinfo[0]), $pkg->getPackageType());
                            PEAR::staticPopErrorHandling();
                            if (PEAR::isError($ret)) {
                                $extrainfo[] = $ret->getMessage();
                                if ($pkg->getPackageType() == 'extsrc' ||
                                      $pkg->getPackageType() == 'extbin') {
                                    $exttype = 'extension';
                                } else {
                                    ob_start();
                                    phpinfo(INFO_GENERAL);
                                    $info = ob_get_contents();
                                    ob_end_clean();
                                    $debug = function_exists('leak') ? '_debug' : '';
                                    $ts = preg_match('Thread Safety.+enabled', $info) ? '_ts' : '';
                                    $exttype = 'zend_extension' . $debug . $ts;
                                }
                                $this->ui->outputData('Unable to remove "' . $exttype . '=' .
                                    $pinfo[1]['basename'] . '" from php.ini', $command);
                            } else {
                                $this->ui->outputData('Extension ' . $pkg->getProvidesExtension() .
                                    ' disabled in php.ini', $command);
                            }
                        }
                    }
                }
                $savepkg = $pkg;
                if ($this->config->get('verbose') > 0) {
                    if (is_object($pkg)) {
                        $pkg = $reg->parsedPackageNameToString($pkg);
                    }
                    $this->ui->outputData("uninstall ok: $pkg", $command);
                }
                if (!isset($options['offline']) && is_object($savepkg) &&
                      defined('PEAR_REMOTEINSTALL_OK')) {
                    if ($this->config->isDefinedLayer('ftp')) {
                        $this->installer->pushErrorHandling(PEAR_ERROR_RETURN);
                        $info = $this->installer->ftpUninstall($savepkg);
                        $this->installer->popErrorHandling();
                        if (PEAR::isError($info)) {
                            $this->ui->outputData($info->getMessage());
                            $this->ui->outputData("remote uninstall failed: $pkg");
                        } else {
                            $this->ui->outputData("remote uninstall ok: $pkg");
                        }
                    }
                }
            } else {
                $this->installer->popErrorHandling();
                if (is_object($pkg)) {
                    $pkg = $reg->parsedPackageNameToString($pkg);
                }
                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)
    {
        $downloader = &$this->getDownloader($this->ui, array('force' => true, 'nodeps' => true,
            'soft' => true, 'downloadonly' => true), $this->config);
        $reg = &$this->config->getRegistry();
        if (sizeof($params) < 1) {
            return $this->raiseError("Please supply the package you want to bundle");
        }

        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;
            }
        }
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        $err = $downloader->setDownloadDir($dest);
        PEAR::staticPopErrorHandling();
        if (PEAR::isError($err)) {
            return PEAR::raiseError('download directory "' . $dest .
                '" is not writeable.');
        }
        $result = &$downloader->download(array($params[0]));
        if (PEAR::isError($result)) {
            return $result;
        }
        if (!isset($result[0])) {
            return $this->raiseError('unable to unpack ' . $params[0]);
        }
        $pkgfile = &$result[0]->getPackageFile();
        $pkgname = $pkgfile->getName();
        $pkgversion = $pkgfile->getVersion();

        // Unpacking -------------------------------------------------
        $dest .= DIRECTORY_SEPARATOR . $pkgname;
        $orig = $pkgname . '-' . $pkgversion;

        $tar = &new Archive_Tar($pkgfile->getArchiveFile());
        if (!$tar->extractModify($dest, $orig)) {
            return $this->raiseError('unable to unpack ' . $pkgfile->getArchiveFile());
        }
        $this->ui->outputData("Package ready at '$dest'");
    // }}}
    }

    // }}}

    function doRunScripts($command, $options, $params)
    {
        if (!isset($params[0])) {
            return $this->raiseError('run-scripts expects 1 parameter: a package name');
        }
        $reg = &$this->config->getRegistry();
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
        PEAR::staticPopErrorHandling();
        if (PEAR::isError($parsed)) {
            return $this->raiseError($parsed);
        }
        $package = &$reg->getPackage($parsed['package'], $parsed['channel']);
        if (is_object($package)) {
            $package->setConfig($this->config);
            $package->runPostinstallScripts();
        } else {
            return $this->raiseError('Could not retrieve package "' . $params[0] . '" from registry');
        }
        $this->ui->outputData('Install scripts complete', $command);
        return true;
    }

    /**
     * Given a list of packages, filter out those ones that are already up to date
     *
     * @param $packages: packages, in parsed array format !
     * @return list of packages that can be upgraded
     */
    function _filterUptodatePackages($packages, $command)
    {
        $reg = &$this->config->getRegistry();
        $latestReleases = array();

        $ret = array();
        foreach($packages as $package) {
            if (isset($package['group'])) {
                $ret[] = $package;
                continue;
            }
            $channel = $package['channel'];
            $name = $package['package'];

            if (!$reg->packageExists($name, $channel)) {
                $ret[] = $package;
                continue;
            }
            if (!isset($latestReleases[$channel])) {
                // fill in cache for this channel
                $chan = &$reg->getChannel($channel);
                if (PEAR::isError($chan)) {
                    return $this->raiseError($chan);
                }
                if ($chan->supportsREST($this->config->get('preferred_mirror',
                                                           null, $channel)) &&
                      $base = $chan->getBaseURL('REST1.0',
                                                $this->config->get('preferred_mirror',
                                                                   null, $channel)))
                {
                    $dorest = true;
                } else {
                    $dorest = false;
                    $remote = &$this->config->getRemote($this->config);
                }
                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
                if ($dorest) {
                    $rest = &$this->config->getREST('1.0', array());
                    $installed = array_flip($reg->listPackages($channel));
                    $latest = $rest->listLatestUpgrades($base,
                        $this->config->get('preferred_state', null, $channel), $installed,
                        $channel, $reg);
                } else {
                    $latest = $remote->call("package.listLatestReleases",
                        $this->config->get('preferred_state', null, $channel));
                    unset($remote);
                }
                PEAR::staticPopErrorHandling();
                if (PEAR::isError($latest)) {
                    $this->ui->outputData('Error getting channel info from ' . $channel .
                        ': ' . $latest->getMessage());
                    continue;
                }

                $latestReleases[$channel] = array_change_key_case($latest);
            }

            // check package for latest release
            if (isset($latestReleases[$channel][strtolower($name)])) {
                // if not set, up to date
                $inst_version = $reg->packageInfo($name, 'version', $channel);
                $channel_version = $latestReleases[$channel][strtolower($name)]['version'];
                if (version_compare($channel_version, $inst_version, "le")) {
                    // installed version is up-to-date
                    continue;
                }
                // maintain BC
                if ($command == 'upgrade-all') {
                    $this->ui->outputData(array('data' => 'Will upgrade ' .
                        $reg->parsedPackageNameToString($package)), $command);
                }
                $ret[] = $package;
            }
        }

        return $ret;
    }

}
?>
<commands version="1.0">
 <install>
  <summary>Install Package</summary>
  <function>doInstall</function>
  <shortcut>i</shortcut>
  <options>
   <force>
    <shortopt>f</shortopt>
    <doc>will overwrite newer installed packages</doc>
   </force>
   <loose>
    <shortopt>l</shortopt>
    <doc>do not check for recommended dependency version</doc>
   </loose>
   <nodeps>
    <shortopt>n</shortopt>
    <doc>ignore dependencies, install anyway</doc>
   </nodeps>
   <register-only>
    <shortopt>r</shortopt>
    <doc>do not install files, only register the package as installed</doc>
   </register-only>
   <soft>
    <shortopt>s</shortopt>
    <doc>soft install, fail silently, or upgrade if already installed</doc>
   </soft>
   <nobuild>
    <shortopt>B</shortopt>
    <doc>don&apos;t build C extensions</doc>
   </nobuild>
   <nocompress>
    <shortopt>Z</shortopt>
    <doc>request uncompressed files when downloading</doc>
   </nocompress>
   <installroot>
    <shortopt>R</shortopt>
    <arg>DIR</arg>
    <doc>root directory used when installing files (ala PHP&apos;s INSTALL_ROOT), use packagingroot for RPM</doc>
   </installroot>
   <packagingroot>
    <shortopt>P</shortopt>
    <arg>DIR</arg>
    <doc>root directory used when packaging files, like RPM packaging</doc>
   </packagingroot>
   <ignore-errors>
    <doc>force install even if there were errors</doc>
   </ignore-errors>
   <alldeps>
    <shortopt>a</shortopt>
    <doc>install all required and optional dependencies</doc>
   </alldeps>
   <onlyreqdeps>
    <shortopt>o</shortopt>
    <doc>install all required dependencies</doc>
   </onlyreqdeps>
   <offline>
    <shortopt>O</shortopt>
    <doc>do not attempt to download any urls or contact channels</doc>
   </offline>
   <pretend>
    <shortopt>p</shortopt>
    <doc>Only list the packages that would be downloaded</doc>
   </pretend>
  </options>
  <doc>[channel/]&lt;package&gt; ...
Installs one or more PEAR packages.  You can specify a package to
install in four ways:

&quot;Package-1.0.tgz&quot; : installs from a local file

&quot;http://example.com/Package-1.0.tgz&quot; : installs from
anywhere on the net.

&quot;package.xml&quot; : installs the package described in
package.xml.  Useful for testing, or for wrapping a PEAR package in
another package manager such as RPM.

&quot;Package[-version/state][.tar]&quot; : queries your default channel&apos;s server
({config master_server}) and downloads the newest package with
the preferred quality/state ({config preferred_state}).

To retrieve Package version 1.1, use &quot;Package-1.1,&quot; to retrieve
Package state beta, use &quot;Package-beta.&quot;  To retrieve an uncompressed
file, append .tar (make sure there is no file by the same name first)

To download a package from another channel, prefix with the channel name like
&quot;channel/Package&quot;

More than one package may be specified at once.  It is ok to mix these
four ways of specifying packages.
</doc>
 </install>
 <upgrade>
  <summary>Upgrade Package</summary>
  <function>doInstall</function>
  <shortcut>up</shortcut>
  <options>
   <force>
    <shortopt>f</shortopt>
    <doc>overwrite newer installed packages</doc>
   </force>
   <loose>
    <shortopt>l</shortopt>
    <doc>do not check for recommended dependency version</doc>
   </loose>
   <nodeps>
    <shortopt>n</shortopt>
    <doc>ignore dependencies, upgrade anyway</doc>
   </nodeps>
   <register-only>
    <shortopt>r</shortopt>
    <doc>do not install files, only register the package as upgraded</doc>
   </register-only>
   <nobuild>
    <shortopt>B</shortopt>
    <doc>don&apos;t build C extensions</doc>
   </nobuild>
   <nocompress>
    <shortopt>Z</shortopt>
    <doc>request uncompressed files when downloading</doc>
   </nocompress>
   <installroot>
    <shortopt>R</shortopt>
    <arg>DIR</arg>
    <doc>root directory used when installing files (ala PHP&apos;s INSTALL_ROOT)</doc>
   </installroot>
   <ignore-errors>
    <doc>force install even if there were errors</doc>
   </ignore-errors>
   <alldeps>
    <shortopt>a</shortopt>
    <doc>install all required and optional dependencies</doc>
   </alldeps>
   <onlyreqdeps>
    <shortopt>o</shortopt>
    <doc>install all required dependencies</doc>
   </onlyreqdeps>
   <offline>
    <shortopt>O</shortopt>
    <doc>do not attempt to download any urls or contact channels</doc>
   </offline>
   <pretend>
    <shortopt>p</shortopt>
    <doc>Only list the packages that would be downloaded</doc>
   </pretend>
  </options>
  <doc>&lt;package&gt; ...
Upgrades one or more PEAR packages.  See documentation for the
&quot;install&quot; 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.
</doc>
 </upgrade>
 <upgrade-all>
  <summary>Upgrade All Packages</summary>
  <function>doInstall</function>
  <shortcut>ua</shortcut>
  <options>
   <nodeps>
    <shortopt>n</shortopt>
    <doc>ignore dependencies, upgrade anyway</doc>
   </nodeps>
   <register-only>
    <shortopt>r</shortopt>
    <doc>do not install files, only register the package as upgraded</doc>
   </register-only>
   <nobuild>
    <shortopt>B</shortopt>
    <doc>don&apos;t build C extensions</doc>
   </nobuild>
   <nocompress>
    <shortopt>Z</shortopt>
    <doc>request uncompressed files when downloading</doc>
   </nocompress>
   <installroot>
    <shortopt>R</shortopt>
    <arg>DIR</arg>
    <doc>root directory used when installing files (ala PHP&apos;s INSTALL_ROOT)</doc>
   </installroot>
   <ignore-errors>
    <doc>force install even if there were errors</doc>
   </ignore-errors>
   <loose>
    <doc>do not check for recommended dependency version</doc>
   </loose>
  </options>
  <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
&quot;preferred_state&quot; (currently {config preferred_state}), or a state considered
more stable.
</doc>
 </upgrade-all>
 <uninstall>
  <summary>Un-install Package</summary>
  <function>doUninstall</function>
  <shortcut>un</shortcut>
  <options>
   <nodeps>
    <shortopt>n</shortopt>
    <doc>ignore dependencies, uninstall anyway</doc>
   </nodeps>
   <register-only>
    <shortopt>r</shortopt>
    <doc>do not remove files, only register the packages as not installed</doc>
   </register-only>
   <installroot>
    <shortopt>R</shortopt>
    <arg>DIR</arg>
    <doc>root directory used when installing files (ala PHP&apos;s INSTALL_ROOT)</doc>
   </installroot>
   <ignore-errors>
    <doc>force install even if there were errors</doc>
   </ignore-errors>
   <offline>
    <shortopt>O</shortopt>
    <doc>do not attempt to uninstall remotely</doc>
   </offline>
  </options>
  <doc>[channel/]&lt;package&gt; ...
Uninstalls one or more PEAR packages.  More than one package may be
specified at once.  Prefix with channel name to uninstall from a
channel not in your default channel ({config default_channel})
</doc>
 </uninstall>
 <bundle>
  <summary>Unpacks a Pecl Package</summary>
  <function>doBundle</function>
  <shortcut>bun</shortcut>
  <options>
   <destination>
    <shortopt>d</shortopt>
    <arg>DIR</arg>
    <doc>Optional destination directory for unpacking (defaults to current path or &quot;ext&quot; if exists)</doc>
   </destination>
   <force>
    <shortopt>f</shortopt>
    <doc>Force the unpacking even if there were errors in the package</doc>
   </force>
  </options>
  <doc>&lt;package&gt;
Unpacks a Pecl Package into the selected location. It will download the
package if needed.
</doc>
 </bundle>
 <run-scripts>
  <summary>Run Post-Install Scripts bundled with a package</summary>
  <function>doRunScripts</function>
  <shortcut>rs</shortcut>
  <options />
  <doc>&lt;package&gt;
Run post-installation scripts in package &lt;package&gt;, if any exist.
</doc>
 </run-scripts>
</commands><?php
/**
 * PEAR_Common, the base class for the PEAR Installer
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V. V. Cox <cox@idecnet.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Common.php,v 1.160 2008/01/03 20:26:34 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1.0
 * @deprecated File deprecated since Release 1.4.0a1
 */

/**
 * Include error handling
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';

// {{{ constants and globals

/**
 * PEAR_Common error when an invalid PHP file is passed to PEAR_Common::analyzeSourceCode()
 */
define('PEAR_COMMON_ERROR_INVALIDPHP', 1);
define('_PEAR_COMMON_PACKAGE_NAME_PREG', '[A-Za-z][a-zA-Z0-9_]+');
define('PEAR_COMMON_PACKAGE_NAME_PREG', '/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '\\z/');

// this should allow: 1, 1.0, 1.0RC1, 1.0dev, 1.0dev123234234234, 1.0a1, 1.0b1, 1.0pl1
define('_PEAR_COMMON_PACKAGE_VERSION_PREG', '\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?');
define('PEAR_COMMON_PACKAGE_VERSION_PREG', '/^' . _PEAR_COMMON_PACKAGE_VERSION_PREG . '\\z/i');

// XXX far from perfect :-)
define('_PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '(' . _PEAR_COMMON_PACKAGE_NAME_PREG .
    ')(-([.0-9a-zA-Z]+))?');
define('PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_PACKAGE_DOWNLOAD_PREG .
    '\\z/');

define('_PEAR_CHANNELS_NAME_PREG', '[A-Za-z][a-zA-Z0-9\.]+');
define('PEAR_CHANNELS_NAME_PREG', '/^' . _PEAR_CHANNELS_NAME_PREG . '\\z/');

// this should allow any dns or IP address, plus a path - NO UNDERSCORES ALLOWED
define('_PEAR_CHANNELS_SERVER_PREG', '[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*(\/[a-zA-Z0-9\-]+)*');
define('PEAR_CHANNELS_SERVER_PREG', '/^' . _PEAR_CHANNELS_SERVER_PREG . '\\z/i');

define('_PEAR_CHANNELS_PACKAGE_PREG',  '(' ._PEAR_CHANNELS_SERVER_PREG . ')\/('
         . _PEAR_COMMON_PACKAGE_NAME_PREG . ')');
define('PEAR_CHANNELS_PACKAGE_PREG', '/^' . _PEAR_CHANNELS_PACKAGE_PREG . '\\z/i');

define('_PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '(' . _PEAR_CHANNELS_NAME_PREG . ')::('
    . _PEAR_COMMON_PACKAGE_NAME_PREG . ')(-([.0-9a-zA-Z]+))?');
define('PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_CHANNEL_DOWNLOAD_PREG . '\\z/');

/**
 * List of temporary files and directories registered by
 * PEAR_Common::addTempFile().
 * @var array
 */
$GLOBALS['_PEAR_Common_tempfiles'] = array();

/**
 * Valid maintainer roles
 * @var array
 */
$GLOBALS['_PEAR_Common_maintainer_roles'] = array('lead','developer','contributor','helper');

/**
 * Valid release states
 * @var array
 */
$GLOBALS['_PEAR_Common_release_states'] = array('alpha','beta','stable','snapshot','devel');

/**
 * Valid dependency types
 * @var array
 */
$GLOBALS['_PEAR_Common_dependency_types'] = array('pkg','ext','php','prog','ldlib','rtlib','os','websrv','sapi');

/**
 * Valid dependency relations
 * @var array
 */
$GLOBALS['_PEAR_Common_dependency_relations'] = array('has','eq','lt','le','gt','ge','not', 'ne');

/**
 * Valid file roles
 * @var array
 */
$GLOBALS['_PEAR_Common_file_roles'] = array('php','ext','test','doc','data','src','script');

/**
 * Valid replacement types
 * @var array
 */
$GLOBALS['_PEAR_Common_replacement_types'] = array('php-const', 'pear-config', 'package-info');

/**
 * Valid "provide" types
 * @var array
 */
$GLOBALS['_PEAR_Common_provide_types'] = array('ext', 'prog', 'class', 'function', 'feature', 'api');

/**
 * Valid "provide" types
 * @var array
 */
$GLOBALS['_PEAR_Common_script_phases'] = array('pre-install', 'post-install', 'pre-uninstall', 'post-uninstall', 'pre-build', 'post-build', 'pre-configure', 'post-configure', 'pre-setup', 'post-setup');

// }}}

/**
 * Class providing common functionality for PEAR administration classes.
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V. V. Cox <cox@idecnet.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 * @deprecated This class will disappear, and its components will be spread
 *             into smaller classes, like the AT&T breakup, as of Release 1.4.0a1
 */
class PEAR_Common extends PEAR
{
    // {{{ properties

    /** stack of elements, gives some sort of XML context */
    var $element_stack = array();

    /** name of currently parsed XML element */
    var $current_element;

    /** array of attributes of the currently parsed XML element */
    var $current_attributes = array();

    /** assoc with information about a package */
    var $pkginfo = array();

    /**
     * User Interface object (PEAR_Frontend_* class).  If null,
     * the log() method uses print.
     * @var object
     */
    var $ui = null;

    /**
     * Configuration object (PEAR_Config).
     * @var PEAR_Config
     */
    var $config = null;

    var $current_path = null;

    /**
     * PEAR_SourceAnalyzer instance
     * @var object
     */
    var $source_analyzer = null;
    /**
     * Flag variable used to mark a valid package file
     * @var boolean
     * @access private
     */
    var $_validPackageFile;

    // }}}

    // {{{ constructor

    /**
     * PEAR_Common constructor
     *
     * @access public
     */
    function PEAR_Common()
    {
        parent::PEAR();
        $this->config = &PEAR_Config::singleton();
        $this->debug = $this->config->get('verbose');
    }

    // }}}
    // {{{ destructor

    /**
     * PEAR_Common destructor
     *
     * @access private
     */
    function _PEAR_Common()
    {
        // doesn't work due to bug #14744
        //$tempfiles = $this->_tempfiles;
        $tempfiles =& $GLOBALS['_PEAR_Common_tempfiles'];
        while ($file = array_shift($tempfiles)) {
            if (@is_dir($file)) {
                if (!class_exists('System')) {
                    require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
                }
                System::rm(array('-rf', $file));
            } elseif (file_exists($file)) {
                unlink($file);
            }
        }
    }

    // }}}
    // {{{ addTempFile()

    /**
     * Register a temporary file or directory.  When the destructor is
     * executed, all registered temporary files and directories are
     * removed.
     *
     * @param string  $file  name of file or directory
     *
     * @return void
     *
     * @access public
     */
    function addTempFile($file)
    {
        if (!class_exists('PEAR_Frontend')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Frontend.php';
        }
        PEAR_Frontend::addTempFile($file);
    }

    // }}}
    // {{{ mkDirHier()

    /**
     * Wrapper to System::mkDir(), creates a directory as well as
     * any necessary parent directories.
     *
     * @param string  $dir  directory name
     *
     * @return bool TRUE on success, or a PEAR error
     *
     * @access public
     */
    function mkDirHier($dir)
    {
        $this->log(2, "+ create dir $dir");
        if (!class_exists('System')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
        }
        return System::mkDir(array('-p', $dir));
    }

    // }}}
    // {{{ log()

    /**
     * Logging method.
     *
     * @param int    $level  log level (0 is quiet, higher is noisier)
     * @param string $msg    message to write to the log
     *
     * @return void
     *
     * @access public
     * @static
     */
    function log($level, $msg, $append_crlf = true)
    {
        if ($this->debug >= $level) {
            if (!class_exists('PEAR_Frontend')) {
                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Frontend.php';
            }
            $ui = &PEAR_Frontend::singleton();
            if (is_a($ui, 'PEAR_Frontend')) {
                $ui->log($msg, $append_crlf);
            } else {
                print "$msg\n";
            }
        }
    }

    // }}}
    // {{{ mkTempDir()

    /**
     * Create and register a temporary directory.
     *
     * @param string $tmpdir (optional) Directory to use as tmpdir.
     *                       Will use system defaults (for example
     *                       /tmp or c:\windows\temp) if not specified
     *
     * @return string name of created directory
     *
     * @access public
     */
    function mkTempDir($tmpdir = '')
    {
        if ($tmpdir) {
            $topt = array('-t', $tmpdir);
        } else {
            $topt = array();
        }
        $topt = array_merge($topt, array('-d', 'pear'));
        if (!class_exists('System')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
        }
        if (!$tmpdir = System::mktemp($topt)) {
            return false;
        }
        $this->addTempFile($tmpdir);
        return $tmpdir;
    }

    // }}}
    // {{{ setFrontendObject()

    /**
     * Set object that represents the frontend to be used.
     *
     * @param  object Reference of the frontend object
     * @return void
     * @access public
     */
    function setFrontendObject(&$ui)
    {
        $this->ui = &$ui;
    }

    // }}}

    // {{{ infoFromTgzFile()

    /**
     * Returns information about a package file.  Expects the name of
     * a gzipped tar file as input.
     *
     * @param string  $file  name of .tgz file
     *
     * @return array  array with package information
     *
     * @access public
     * @deprecated use PEAR_PackageFile->fromTgzFile() instead
     *
     */
    function infoFromTgzFile($file)
    {
        $packagefile = &new PEAR_PackageFile($this->config);
        $pf = &$packagefile->fromTgzFile($file, PEAR_VALIDATE_NORMAL);
        if (PEAR::isError($pf)) {
            $errs = $pf->getUserinfo();
            if (is_array($errs)) {
                foreach ($errs as $error) {
                    $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
                }
            }
            return $pf;
        }
        return $this->_postProcessValidPackagexml($pf);
    }

    // }}}
    // {{{ infoFromDescriptionFile()

    /**
     * Returns information about a package file.  Expects the name of
     * a package xml file as input.
     *
     * @param string  $descfile  name of package xml file
     *
     * @return array  array with package information
     *
     * @access public
     * @deprecated use PEAR_PackageFile->fromPackageFile() instead
     *
     */
    function infoFromDescriptionFile($descfile)
    {
        $packagefile = &new PEAR_PackageFile($this->config);
        $pf = &$packagefile->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
        if (PEAR::isError($pf)) {
            $errs = $pf->getUserinfo();
            if (is_array($errs)) {
                foreach ($errs as $error) {
                    $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
                }
            }
            return $pf;
        }
        return $this->_postProcessValidPackagexml($pf);
    }

    // }}}
    // {{{ infoFromString()

    /**
     * Returns information about a package file.  Expects the contents
     * of a package xml file as input.
     *
     * @param string  $data  contents of package.xml file
     *
     * @return array   array with package information
     *
     * @access public
     * @deprecated use PEAR_PackageFile->fromXmlstring() instead
     *
     */
    function infoFromString($data)
    {
        $packagefile = &new PEAR_PackageFile($this->config);
        $pf = &$packagefile->fromXmlString($data, PEAR_VALIDATE_NORMAL, false);
        if (PEAR::isError($pf)) {
            $errs = $pf->getUserinfo();
            if (is_array($errs)) {
                foreach ($errs as $error) {
                    $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
                }
            }
            return $pf;
        }
        return $this->_postProcessValidPackagexml($pf);
    }
    // }}}

    /**
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @return array
     */
    function _postProcessValidPackagexml(&$pf)
    {
        if (is_a($pf, 'PEAR_PackageFile_v2')) {
            // sort of make this into a package.xml 1.0-style array
            // changelog is not converted to old format.
            $arr = $pf->toArray(true);
            $arr = array_merge($arr, $arr['old']);
            unset($arr['old']);
            unset($arr['xsdversion']);
            unset($arr['contents']);
            unset($arr['compatible']);
            unset($arr['channel']);
            unset($arr['uri']);
            unset($arr['dependencies']);
            unset($arr['phprelease']);
            unset($arr['extsrcrelease']);
            unset($arr['zendextsrcrelease']);
            unset($arr['extbinrelease']);
            unset($arr['zendextbinrelease']);
            unset($arr['bundle']);
            unset($arr['lead']);
            unset($arr['developer']);
            unset($arr['helper']);
            unset($arr['contributor']);
            $arr['filelist'] = $pf->getFilelist();
            $this->pkginfo = $arr;
            return $arr;
        } else {
            $this->pkginfo = $pf->toArray();
            return $this->pkginfo;
        }
    }
    // {{{ infoFromAny()

    /**
     * Returns package information from different sources
     *
     * This method is able to extract information about a package
     * from a .tgz archive or from a XML package definition file.
     *
     * @access public
     * @param  string Filename of the source ('package.xml', '<package>.tgz')
     * @return string
     * @deprecated use PEAR_PackageFile->fromAnyFile() instead
     */
    function infoFromAny($info)
    {
        if (is_string($info) && file_exists($info)) {
            $packagefile = &new PEAR_PackageFile($this->config);
            $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL);
            if (PEAR::isError($pf)) {
                $errs = $pf->getUserinfo();
                if (is_array($errs)) {
                    foreach ($errs as $error) {
                        $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
                    }
                }
                return $pf;
            }
            return $this->_postProcessValidPackagexml($pf);
        }
        return $info;
    }

    // }}}
    // {{{ xmlFromInfo()

    /**
     * Return an XML document based on the package info (as returned
     * by the PEAR_Common::infoFrom* methods).
     *
     * @param array  $pkginfo  package info
     *
     * @return string XML data
     *
     * @access public
     * @deprecated use a PEAR_PackageFile_v* object's generator instead
     */
    function xmlFromInfo($pkginfo)
    {
        $config = &PEAR_Config::singleton();
        $packagefile = &new PEAR_PackageFile($config);
        $pf = &$packagefile->fromArray($pkginfo);
        $gen = &$pf->getDefaultGenerator();
        return $gen->toXml(PEAR_VALIDATE_PACKAGING);
    }

    // }}}
    // {{{ validatePackageInfo()

    /**
     * Validate XML package definition file.
     *
     * @param  string $info Filename of the package archive or of the
     *                package definition file
     * @param  array $errors Array that will contain the errors
     * @param  array $warnings Array that will contain the warnings
     * @param  string $dir_prefix (optional) directory where source files
     *                may be found, or empty if they are not available
     * @access public
     * @return boolean
     * @deprecated use the validation of PEAR_PackageFile objects
     */
    function validatePackageInfo($info, &$errors, &$warnings, $dir_prefix = '')
    {
        $config = &PEAR_Config::singleton();
        $packagefile = &new PEAR_PackageFile($config);
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        if (strpos($info, '<?xml') !== false) {
            $pf = &$packagefile->fromXmlString($info, PEAR_VALIDATE_NORMAL, '');
        } else {
            $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL);
        }
        PEAR::staticPopErrorHandling();
        if (PEAR::isError($pf)) {
            $errs = $pf->getUserinfo();
            if (is_array($errs)) {
                foreach ($errs as $error) {
                    if ($error['level'] == 'error') {
                        $errors[] = $error['message'];
                    } else {
                        $warnings[] = $error['message'];
                    }
                }
            }
            return false;
        }
        return true;
    }

    // }}}
    // {{{ buildProvidesArray()

    /**
     * Build a "provides" array from data returned by
     * analyzeSourceCode().  The format of the built array is like
     * this:
     *
     *  array(
     *    'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
     *    ...
     *  )
     *
     *
     * @param array $srcinfo array with information about a source file
     * as returned by the analyzeSourceCode() method.
     *
     * @return void
     *
     * @access public
     *
     */
    function buildProvidesArray($srcinfo)
    {
        $file = basename($srcinfo['source_file']);
        $pn = '';
        if (isset($this->_packageName)) {
            $pn = $this->_packageName;
        }
        $pnl = strlen($pn);
        foreach ($srcinfo['declared_classes'] as $class) {
            $key = "class;$class";
            if (isset($this->pkginfo['provides'][$key])) {
                continue;
            }
            $this->pkginfo['provides'][$key] =
                array('file'=> $file, 'type' => 'class', 'name' => $class);
            if (isset($srcinfo['inheritance'][$class])) {
                $this->pkginfo['provides'][$key]['extends'] =
                    $srcinfo['inheritance'][$class];
            }
        }
        foreach ($srcinfo['declared_methods'] as $class => $methods) {
            foreach ($methods as $method) {
                $function = "$class::$method";
                $key = "function;$function";
                if ($method{0} == '_' || !strcasecmp($method, $class) ||
                    isset($this->pkginfo['provides'][$key])) {
                    continue;
                }
                $this->pkginfo['provides'][$key] =
                    array('file'=> $file, 'type' => 'function', 'name' => $function);
            }
        }

        foreach ($srcinfo['declared_functions'] as $function) {
            $key = "function;$function";
            if ($function{0} == '_' || isset($this->pkginfo['provides'][$key])) {
                continue;
            }
            if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
                $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
            }
            $this->pkginfo['provides'][$key] =
                array('file'=> $file, 'type' => 'function', 'name' => $function);
        }
    }

    // }}}
    // {{{ analyzeSourceCode()

    /**
     * Analyze the source code of the given PHP file
     *
     * @param  string Filename of the PHP file
     * @return mixed
     * @access public
     */
    function analyzeSourceCode($file)
    {
        if (!function_exists("token_get_all")) {
            return false;
        }
        if (!defined('T_DOC_COMMENT')) {
            define('T_DOC_COMMENT', T_COMMENT);
        }
        if (!defined('T_INTERFACE')) {
            define('T_INTERFACE', -1);
        }
        if (!defined('T_IMPLEMENTS')) {
            define('T_IMPLEMENTS', -1);
        }
        if (!$fp = @fopen($file, "r")) {
            return false;
        }
        fclose($fp);
        $contents = file_get_contents($file);
        $tokens = token_get_all($contents);
/*
        for ($i = 0; $i < sizeof($tokens); $i++) {
            @list($token, $data) = $tokens[$i];
            if (is_string($token)) {
                var_dump($token);
            } else {
                print token_name($token) . ' ';
                var_dump(rtrim($data));
            }
        }
*/
        $look_for = 0;
        $paren_level = 0;
        $bracket_level = 0;
        $brace_level = 0;
        $lastphpdoc = '';
        $current_class = '';
        $current_interface = '';
        $current_class_level = -1;
        $current_function = '';
        $current_function_level = -1;
        $declared_classes = array();
        $declared_interfaces = array();
        $declared_functions = array();
        $declared_methods = array();
        $used_classes = array();
        $used_functions = array();
        $extends = array();
        $implements = array();
        $nodeps = array();
        $inquote = false;
        $interface = false;
        for ($i = 0; $i < sizeof($tokens); $i++) {
            if (is_array($tokens[$i])) {
                list($token, $data) = $tokens[$i];
            } else {
                $token = $tokens[$i];
                $data = '';
            }
            if ($inquote) {
                if ($token != '"') {
                    continue;
                } else {
                    $inquote = false;
                    continue;
                }
            }
            switch ($token) {
                case T_WHITESPACE:
                    continue;
                case ';':
                    if ($interface) {
                        $current_function = '';
                        $current_function_level = -1;
                    }
                    break;
                case '"':
                    $inquote = true;
                    break;
                case T_CURLY_OPEN:
                case T_DOLLAR_OPEN_CURLY_BRACES:
                case '{': $brace_level++; continue 2;
                case '}':
                    $brace_level--;
                    if ($current_class_level == $brace_level) {
                        $current_class = '';
                        $current_class_level = -1;
                    }
                    if ($current_function_level == $brace_level) {
                        $current_function = '';
                        $current_function_level = -1;
                    }
                    continue 2;
                case '[': $bracket_level++; continue 2;
                case ']': $bracket_level--; continue 2;
                case '(': $paren_level++;   continue 2;
                case ')': $paren_level--;   continue 2;
                case T_INTERFACE:
                    $interface = true;
                case T_CLASS:
                    if (($current_class_level != -1) || ($current_function_level != -1)) {
                        PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"",
                            PEAR_COMMON_ERROR_INVALIDPHP);
                        return false;
                    }
                case T_FUNCTION:
                case T_NEW:
                case T_EXTENDS:
                case T_IMPLEMENTS:
                    $look_for = $token;
                    continue 2;
                case T_STRING:
                    if (version_compare(zend_version(), '2.0', '<')) {
                        if (in_array(strtolower($data),
                            array('public', 'private', 'protected', 'abstract',
                                  'interface', 'implements', 'throw') 
                                 )) {
                            PEAR::raiseError('Error: PHP5 token encountered in ' . $file . 
                            'packaging should be done in PHP 5');
                            return false;
                        }
                    }
                    if ($look_for == T_CLASS) {
                        $current_class = $data;
                        $current_class_level = $brace_level;
                        $declared_classes[] = $current_class;
                    } elseif ($look_for == T_INTERFACE) {
                        $current_interface = $data;
                        $current_class_level = $brace_level;
                        $declared_interfaces[] = $current_interface;
                    } elseif ($look_for == T_IMPLEMENTS) {
                        $implements[$current_class] = $data;
                    } elseif ($look_for == T_EXTENDS) {
                        $extends[$current_class] = $data;
                    } elseif ($look_for == T_FUNCTION) {
                        if ($current_class) {
                            $current_function = "$current_class::$data";
                            $declared_methods[$current_class][] = $data;
                        } elseif ($current_interface) {
                            $current_function = "$current_interface::$data";
                            $declared_methods[$current_interface][] = $data;
                        } else {
                            $current_function = $data;
                            $declared_functions[] = $current_function;
                        }
                        $current_function_level = $brace_level;
                        $m = array();
                    } elseif ($look_for == T_NEW) {
                        $used_classes[$data] = true;
                    }
                    $look_for = 0;
                    continue 2;
                case T_VARIABLE:
                    $look_for = 0;
                    continue 2;
                case T_DOC_COMMENT:
                case T_COMMENT:
                    if (preg_match('!^/\*\*\s!', $data)) {
                        $lastphpdoc = $data;
                        if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
                            $nodeps = array_merge($nodeps, $m[1]);
                        }
                    }
                    continue 2;
                case T_DOUBLE_COLON:
                    if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) {
                        PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"",
                            PEAR_COMMON_ERROR_INVALIDPHP);
                        return false;
                    }
                    $class = $tokens[$i - 1][1];
                    if (strtolower($class) != 'parent') {
                        $used_classes[$class] = true;
                    }
                    continue 2;
            }
        }
        return array(
            "source_file" => $file,
            "declared_classes" => $declared_classes,
            "declared_interfaces" => $declared_interfaces,
            "declared_methods" => $declared_methods,
            "declared_functions" => $declared_functions,
            "used_classes" => array_diff(array_keys($used_classes), $nodeps),
            "inheritance" => $extends,
            "implements" => $implements,
            );
    }

    // }}}
    // {{{  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)
    {
        if (!function_exists("token_get_all")) {
            return false;
        }
        if (PEAR::isError($info = $this->infoFromAny($any))) {
            return $this->raiseError($info);
        }
        if (!is_array($info)) {
            return false;
        }
        $deps = array();
        $used_c = $decl_c = $decl_f = $decl_m = array();
        foreach ($info['filelist'] as $file => $fa) {
            $tmp = $this->analyzeSourceCode($file);
            $used_c = @array_merge($used_c, $tmp['used_classes']);
            $decl_c = @array_merge($decl_c, $tmp['declared_classes']);
            $decl_f = @array_merge($decl_f, $tmp['declared_functions']);
            $decl_m = @array_merge($decl_m, $tmp['declared_methods']);
            $inheri = @array_merge($inheri, $tmp['inheritance']);
        }
        $used_c = array_unique($used_c);
        $decl_c = array_unique($decl_c);
        $undecl_c = array_diff($used_c, $decl_c);
        return array('used_classes' => $used_c,
                     'declared_classes' => $decl_c,
                     'declared_methods' => $decl_m,
                     'declared_functions' => $decl_f,
                     'undeclared_classes' => $undecl_c,
                     'inheritance' => $inheri,
                     );
    }

    // }}}
    // {{{ getUserRoles()

    /**
     * Get the valid roles for a PEAR package maintainer
     *
     * @return array
     * @static
     */
    function getUserRoles()
    {
        return $GLOBALS['_PEAR_Common_maintainer_roles'];
    }

    // }}}
    // {{{ getReleaseStates()

    /**
     * Get the valid package release states of packages
     *
     * @return array
     * @static
     */
    function getReleaseStates()
    {
        return $GLOBALS['_PEAR_Common_release_states'];
    }

    // }}}
    // {{{ getDependencyTypes()

    /**
     * Get the implemented dependency types (php, ext, pkg etc.)
     *
     * @return array
     * @static
     */
    function getDependencyTypes()
    {
        return $GLOBALS['_PEAR_Common_dependency_types'];
    }

    // }}}
    // {{{ getDependencyRelations()

    /**
     * Get the implemented dependency relations (has, lt, ge etc.)
     *
     * @return array
     * @static
     */
    function getDependencyRelations()
    {
        return $GLOBALS['_PEAR_Common_dependency_relations'];
    }

    // }}}
    // {{{ getFileRoles()

    /**
     * Get the implemented file roles
     *
     * @return array
     * @static
     */
    function getFileRoles()
    {
        return $GLOBALS['_PEAR_Common_file_roles'];
    }

    // }}}
    // {{{ getReplacementTypes()

    /**
     * Get the implemented file replacement types in
     *
     * @return array
     * @static
     */
    function getReplacementTypes()
    {
        return $GLOBALS['_PEAR_Common_replacement_types'];
    }

    // }}}
    // {{{ getProvideTypes()

    /**
     * Get the implemented file replacement types in
     *
     * @return array
     * @static
     */
    function getProvideTypes()
    {
        return $GLOBALS['_PEAR_Common_provide_types'];
    }

    // }}}
    // {{{ getScriptPhases()

    /**
     * Get the implemented file replacement types in
     *
     * @return array
     * @static
     */
    function getScriptPhases()
    {
        return $GLOBALS['_PEAR_Common_script_phases'];
    }

    // }}}
    // {{{ validPackageName()

    /**
     * Test whether a string contains a valid package name.
     *
     * @param string $name the package name to test
     *
     * @return bool
     *
     * @access public
     */
    function validPackageName($name)
    {
        return (bool)preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $name);
    }


    // }}}
    // {{{ validPackageVersion()

    /**
     * Test whether a string contains a valid package version.
     *
     * @param string $ver the package version to test
     *
     * @return bool
     *
     * @access public
     */
    function validPackageVersion($ver)
    {
        return (bool)preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver);
    }


    // }}}

    // {{{ downloadHttp()

    /**
     * Download a file through HTTP.  Considers suggested file name in
     * Content-disposition: header and can run a callback function for
     * different events.  The callback will be called with two
     * parameters: the callback type, and parameters.  The implemented
     * callback types are:
     *
     *  'setup'       called at the very beginning, parameter is a UI object
     *                that should be used for all output
     *  'message'     the parameter is a string with an informational message
     *  'saveas'      may be used to save with a different file name, the
     *                parameter is the filename that is about to be used.
     *                If a 'saveas' callback returns a non-empty string,
     *                that file name will be used as the filename instead.
     *                Note that $save_dir will not be affected by this, only
     *                the basename of the file.
     *  'start'       download is starting, parameter is number of bytes
     *                that are expected, or -1 if unknown
     *  'bytesread'   parameter is the number of bytes read so far
     *  'done'        download is complete, parameter is the total number
     *                of bytes read
     *  'connfailed'  if the TCP connection fails, this callback is called
     *                with array(host,port,errno,errmsg)
     *  'writefailed' if writing to disk fails, this callback is called
     *                with array(destfile,errmsg)
     *
     * If an HTTP proxy has been configured (http_proxy PEAR_Config
     * setting), the proxy will be used.
     *
     * @param string  $url       the URL to download
     * @param object  $ui        PEAR_Frontend_* instance
     * @param object  $config    PEAR_Config instance
     * @param string  $save_dir  (optional) directory to save file in
     * @param mixed   $callback  (optional) function/method to call for status
     *                           updates
     *
     * @return string  Returns the full path of the downloaded file or a PEAR
     *                 error on failure.  If the error is caused by
     *                 socket-related errors, the error object will
     *                 have the fsockopen error code available through
     *                 getCode().
     *
     * @access public
     * @deprecated in favor of PEAR_Downloader::downloadHttp()
     */
    function downloadHttp($url, &$ui, $save_dir = '.', $callback = null)
    {
        if (!class_exists('PEAR_Downloader')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Downloader.php';
        }
        return PEAR_Downloader::downloadHttp($url, $ui, $save_dir, $callback);
    }

    // }}}

    /**
     * @param string $path relative or absolute include path
     * @return boolean
     * @static
     */
    function isIncludeable($path)
    {
        if (file_exists($path) && is_readable($path)) {
            return true;
        }
        $ipath = explode(PATH_SEPARATOR, ini_get('include_path'));
        foreach ($ipath as $include) {
            $test = realpath($include . DIRECTORY_SEPARATOR . $path);
            if (file_exists($test) && is_readable($test)) {
                return true;
            }
        }
        return false;
    }
}
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Config.php';
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile.php';
?><?php
/**
 * PEAR_Config, customized configuration handling for the PEAR Installer
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Config.php,v 1.146 2008/05/14 04:16:08 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * Required for error handling
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Registry.php';
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Installer/Role.php';
require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Remote.php';

/**
 * Last created PEAR_Config instance.
 * @var object
 */
$GLOBALS['_PEAR_Config_instance'] = null;
if (!defined('PEAR_INSTALL_DIR') || !PEAR_INSTALL_DIR) {
    $PEAR_INSTALL_DIR = PHP_LIBDIR . DIRECTORY_SEPARATOR . 'pear';
} else {
    $PEAR_INSTALL_DIR = PEAR_INSTALL_DIR;
}

// Below we define constants with default values for all configuration
// parameters except username/password.  All of them can have their
// defaults set through environment variables.  The reason we use the
// PHP_ prefix is for some security, PHP protects environment
// variables starting with PHP_*.

// default channel and preferred mirror is based on whether we are invoked through
// the "pear" or the "pecl" command

if (!defined('PEAR_RUNTYPE') || PEAR_RUNTYPE == 'pear') {
    define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pear.php.net');
} else {
    define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pecl.php.net');
}

if (getenv('PHP_PEAR_SYSCONF_DIR')) {
    define('PEAR_CONFIG_SYSCONFDIR', getenv('PHP_PEAR_SYSCONF_DIR'));
} elseif (getenv('SystemRoot')) {
    define('PEAR_CONFIG_SYSCONFDIR', getenv('SystemRoot'));
} else {
    define('PEAR_CONFIG_SYSCONFDIR', PHP_SYSCONFDIR);
}

// Default for master_server
if (getenv('PHP_PEAR_MASTER_SERVER')) {
    define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', getenv('PHP_PEAR_MASTER_SERVER'));
} else {
    define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', 'pear.php.net');
}

// Default for http_proxy
if (getenv('PHP_PEAR_HTTP_PROXY')) {
    define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('PHP_PEAR_HTTP_PROXY'));
} elseif (getenv('http_proxy')) {
    define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('http_proxy'));
} else {
    define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', '');
}

// Default for php_dir
if (getenv('PHP_PEAR_INSTALL_DIR')) {
    define('PEAR_CONFIG_DEFAULT_PHP_DIR', getenv('PHP_PEAR_INSTALL_DIR'));
} else {
    if (file_exists($PEAR_INSTALL_DIR) && is_dir($PEAR_INSTALL_DIR)) {
        define('PEAR_CONFIG_DEFAULT_PHP_DIR',
               $PEAR_INSTALL_DIR);
    } else {
        define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR);
    }
}

// Default for ext_dir
if (getenv('PHP_PEAR_EXTENSION_DIR')) {
    define('PEAR_CONFIG_DEFAULT_EXT_DIR', getenv('PHP_PEAR_EXTENSION_DIR'));
} else {
    if (ini_get('extension_dir')) {
        define('PEAR_CONFIG_DEFAULT_EXT_DIR', ini_get('extension_dir'));
    } elseif (defined('PEAR_EXTENSION_DIR') &&
              file_exists(PEAR_EXTENSION_DIR) && is_dir(PEAR_EXTENSION_DIR)) {
        define('PEAR_CONFIG_DEFAULT_EXT_DIR', PEAR_EXTENSION_DIR);
    } elseif (defined('PHP_EXTENSION_DIR')) {
        define('PEAR_CONFIG_DEFAULT_EXT_DIR', PHP_EXTENSION_DIR);
    } else {
        define('PEAR_CONFIG_DEFAULT_EXT_DIR', '.');
    }
}

// Default for doc_dir
if (getenv('PHP_PEAR_DOC_DIR')) {
    define('PEAR_CONFIG_DEFAULT_DOC_DIR', getenv('PHP_PEAR_DOC_DIR'));
} else {
    define('PEAR_CONFIG_DEFAULT_DOC_DIR',
           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'docs');
}

// Default for bin_dir
if (getenv('PHP_PEAR_BIN_DIR')) {
    define('PEAR_CONFIG_DEFAULT_BIN_DIR', getenv('PHP_PEAR_BIN_DIR'));
} else {
    define('PEAR_CONFIG_DEFAULT_BIN_DIR', PHP_BINDIR);
}

// Default for data_dir
if (getenv('PHP_PEAR_DATA_DIR')) {
    define('PEAR_CONFIG_DEFAULT_DATA_DIR', getenv('PHP_PEAR_DATA_DIR'));
} else {
    define('PEAR_CONFIG_DEFAULT_DATA_DIR',
           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'data');
}

// Default for cfg_dir
if (getenv('PHP_PEAR_CFG_DIR')) {
    define('PEAR_CONFIG_DEFAULT_CFG_DIR', getenv('PHP_PEAR_CFG_DIR'));
} else {
    define('PEAR_CONFIG_DEFAULT_CFG_DIR',
           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'cfg');
}

// Default for www_dir
if (getenv('PHP_PEAR_WWW_DIR')) {
    define('PEAR_CONFIG_DEFAULT_WWW_DIR', getenv('PHP_PEAR_WWW_DIR'));
} else {
    define('PEAR_CONFIG_DEFAULT_WWW_DIR',
           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'www');
}

// Default for test_dir
if (getenv('PHP_PEAR_TEST_DIR')) {
    define('PEAR_CONFIG_DEFAULT_TEST_DIR', getenv('PHP_PEAR_TEST_DIR'));
} else {
    define('PEAR_CONFIG_DEFAULT_TEST_DIR',
           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'tests');
}

// Default for temp_dir
if (getenv('PHP_PEAR_TEMP_DIR')) {
    define('PEAR_CONFIG_DEFAULT_TEMP_DIR', getenv('PHP_PEAR_TEMP_DIR'));
} else {
    define('PEAR_CONFIG_DEFAULT_TEMP_DIR',
           System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
           DIRECTORY_SEPARATOR . 'temp');
}

// Default for cache_dir
if (getenv('PHP_PEAR_CACHE_DIR')) {
    define('PEAR_CONFIG_DEFAULT_CACHE_DIR', getenv('PHP_PEAR_CACHE_DIR'));
} else {
    define('PEAR_CONFIG_DEFAULT_CACHE_DIR',
           System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
           DIRECTORY_SEPARATOR . 'cache');
}

// Default for download_dir
if (getenv('PHP_PEAR_DOWNLOAD_DIR')) {
    define('PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR', getenv('PHP_PEAR_DOWNLOAD_DIR'));
} else {
    define('PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR',
           System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
           DIRECTORY_SEPARATOR . 'download');
}

// Default for php_bin
if (getenv('PHP_PEAR_PHP_BIN')) {
    define('PEAR_CONFIG_DEFAULT_PHP_BIN', getenv('PHP_PEAR_PHP_BIN'));
} else {
    define('PEAR_CONFIG_DEFAULT_PHP_BIN', PEAR_CONFIG_DEFAULT_BIN_DIR.
           DIRECTORY_SEPARATOR.'php'.(OS_WINDOWS ? '.exe' : ''));
}

// Default for verbose
if (getenv('PHP_PEAR_VERBOSE')) {
    define('PEAR_CONFIG_DEFAULT_VERBOSE', getenv('PHP_PEAR_VERBOSE'));
} else {
    define('PEAR_CONFIG_DEFAULT_VERBOSE', 1);
}

// Default for preferred_state
if (getenv('PHP_PEAR_PREFERRED_STATE')) {
    define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', getenv('PHP_PEAR_PREFERRED_STATE'));
} else {
    define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', 'stable');
}

// Default for umask
if (getenv('PHP_PEAR_UMASK')) {
    define('PEAR_CONFIG_DEFAULT_UMASK', getenv('PHP_PEAR_UMASK'));
} else {
    define('PEAR_CONFIG_DEFAULT_UMASK', decoct(umask()));
}

// Default for cache_ttl
if (getenv('PHP_PEAR_CACHE_TTL')) {
    define('PEAR_CONFIG_DEFAULT_CACHE_TTL', getenv('PHP_PEAR_CACHE_TTL'));
} else {
    define('PEAR_CONFIG_DEFAULT_CACHE_TTL', 3600);
}

// Default for sig_type
if (getenv('PHP_PEAR_SIG_TYPE')) {
    define('PEAR_CONFIG_DEFAULT_SIG_TYPE', getenv('PHP_PEAR_SIG_TYPE'));
} else {
    define('PEAR_CONFIG_DEFAULT_SIG_TYPE', 'gpg');
}

// Default for sig_bin
if (getenv('PHP_PEAR_SIG_BIN')) {
    define('PEAR_CONFIG_DEFAULT_SIG_BIN', getenv('PHP_PEAR_SIG_BIN'));
} else {
    define('PEAR_CONFIG_DEFAULT_SIG_BIN',
           System::which(
               'gpg', OS_WINDOWS ? 'c:\gnupg\gpg.exe' : '/usr/local/bin/gpg'));
}

// Default for sig_keydir
if (getenv('PHP_PEAR_SIG_KEYDIR')) {
    define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR', getenv('PHP_PEAR_SIG_KEYDIR'));
} else {
    define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR',
           PEAR_CONFIG_SYSCONFDIR . DIRECTORY_SEPARATOR . 'pearkeys');
}

/**
 * This is a class for storing configuration data, keeping track of
 * which are system-defined, user-defined or defaulted.
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */
class PEAR_Config extends PEAR
{
    // {{{ properties

    /**
     * Array of config files used.
     *
     * @var array layer => config file
     */
    var $files = array(
        'system' => '',
        'user' => '',
        );

    var $layers = array();
    
    /**
     * Configuration data, two-dimensional array where the first
     * dimension is the config layer ('user', 'system' and 'default'),
     * and the second dimension is keyname => value.
     *
     * The order in the first dimension is important!  Earlier
     * layers will shadow later ones when a config value is
     * requested (if a 'user' value exists, it will be returned first,
     * then 'system' and finally 'default').
     *
     * @var array layer => array(keyname => value, ...)
     */
    var $configuration = array(
        'user' => array(),
        'system' => array(),
        'default' => array(),
        );
    
    /**
     * Configuration values that can be set for a channel
     *
     * All other configuration values can only have a global value
     * @var array
     * @access private
     */
    var $_channelConfigInfo = array(
        'php_dir', 'ext_dir', 'doc_dir', 'bin_dir', 'data_dir', 'cfg_dir',
        'test_dir', 'www_dir', 'php_bin', 'username', 'password', 'verbose',
        'preferred_state', 'umask', 'preferred_mirror', 'php_ini'
        );

    /**
     * Channels that can be accessed
     * @see setChannels()
     * @var array
     * @access private
     */
    var $_channels = array('pear.php.net', 'pecl.php.net', '__uri');

    /**
     * This variable is used to control the directory values returned
     * @see setInstallRoot();
     * @var string|false
     * @access private
     */
    var $_installRoot = false;

    /**
     * If requested, this will always refer to the registry
     * contained in php_dir
     * @var PEAR_Registry
     */
    var $_registry = array();

    /**
     * @var array
     * @access private
     */
    var $_regInitialized = array();

    /**
     * @var bool
     * @access private
     */
    var $_noRegistry = false;

    /**
     * amount of errors found while parsing config
     * @var integer
     * @access private
     */
    var $_errorsFound = 0;
    var $_lastError = null;

    /**
     * Information about the configuration data.  Stores the type,
     * default value and a documentation string for each configuration
     * value.
     *
     * @var array layer => array(infotype => value, ...)
     */
    var $configuration_info = array(
        // Channels/Internet Access
        'default_channel' => array(
            'type' => 'string',
            'default' => PEAR_CONFIG_DEFAULT_CHANNEL,
            'doc' => 'the default channel to use for all non explicit commands',
            'prompt' => 'Default Channel',
            'group' => 'Internet Access',
            ),
        'preferred_mirror' => array(
            'type' => 'string',
            'default' => PEAR_CONFIG_DEFAULT_CHANNEL,
            'doc' => 'the default server or mirror to use for channel actions',
            'prompt' => 'Default Channel Mirror',
            'group' => 'Internet Access',
            ),
        'remote_config' => array(
            'type' => 'password',
            'default' => '',
            'doc' => 'ftp url of remote configuration file to use for synchronized install',
            'prompt' => 'Remote Configuration File',
            'group' => 'Internet Access',
            ),
        'auto_discover' => array(
            'type' => 'integer',
            'default' => 0,
            'doc' => 'whether to automatically discover new channels',
            'prompt' => 'Auto-discover new Channels',
            'group' => 'Internet Access',
            ),
        // Internet Access
        'master_server' => array(
            'type' => 'string',
            'default' => 'pear.php.net',
            'doc' => 'name of the main PEAR server [NOT USED IN THIS VERSION]',
            'prompt' => 'PEAR server [DEPRECATED]',
            'group' => 'Internet Access',
            ),
        'http_proxy' => array(
            'type' => 'string',
            'default' => PEAR_CONFIG_DEFAULT_HTTP_PROXY,
            'doc' => 'HTTP proxy (host:port) to use when downloading packages',
            'prompt' => 'HTTP Proxy Server Address',
            'group' => 'Internet Access',
            ),
        // File Locations
        'php_dir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_PHP_DIR,
            'doc' => 'directory where .php files are installed',
            'prompt' => 'PEAR directory',
            'group' => 'File Locations',
            ),
        'ext_dir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_EXT_DIR,
            'doc' => 'directory where loadable extensions are installed',
            'prompt' => 'PHP extension directory',
            'group' => 'File Locations',
            ),
        'doc_dir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_DOC_DIR,
            'doc' => 'directory where documentation is installed',
            'prompt' => 'PEAR documentation directory',
            'group' => 'File Locations',
            ),
        'bin_dir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_BIN_DIR,
            'doc' => 'directory where executables are installed',
            'prompt' => 'PEAR executables directory',
            'group' => 'File Locations',
            ),
        'data_dir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_DATA_DIR,
            'doc' => 'directory where data files are installed',
            'prompt' => 'PEAR data directory',
            'group' => 'File Locations (Advanced)',
            ),
        'cfg_dir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_CFG_DIR,
            'doc' => 'directory where modifiable configuration files are installed',
            'prompt' => 'PEAR configuration file directory',
            'group' => 'File Locations (Advanced)',
            ),
        'www_dir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_WWW_DIR,
            'doc' => 'directory where www frontend files (html/js) are installed',
            'prompt' => 'PEAR www files directory',
            'group' => 'File Locations (Advanced)',
            ),
        'test_dir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_TEST_DIR,
            'doc' => 'directory where regression tests are installed',
            'prompt' => 'PEAR test directory',
            'group' => 'File Locations (Advanced)',
            ),
        'cache_dir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_CACHE_DIR,
            'doc' => 'directory which is used for XMLRPC cache',
            'prompt' => 'PEAR Installer cache directory',
            'group' => 'File Locations (Advanced)',
            ),
        'temp_dir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_TEMP_DIR,
            'doc' => 'directory which is used for all temp files',
            'prompt' => 'PEAR Installer temp directory',
            'group' => 'File Locations (Advanced)',
            ),
        'download_dir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR,
            'doc' => 'directory which is used for all downloaded files',
            'prompt' => 'PEAR Installer download directory',
            'group' => 'File Locations (Advanced)',
            ),
        'php_bin' => array(
            'type' => 'file',
            'default' => PEAR_CONFIG_DEFAULT_PHP_BIN,
            'doc' => 'PHP CLI/CGI binary for executing scripts',
            'prompt' => 'PHP CLI/CGI binary',
            'group' => 'File Locations (Advanced)',
            ),
        'php_ini' => array(
            'type' => 'file',
            'default' => '',
            'doc' => 'location of php.ini in which to enable PECL extensions on install',
            'prompt' => 'php.ini location',
            'group' => 'File Locations (Advanced)',
            ),
        // Maintainers
        'username' => array(
            'type' => 'string',
            'default' => '',
            'doc' => '(maintainers) your PEAR account name',
            'prompt' => 'PEAR username (for maintainers)',
            'group' => 'Maintainers',
            ),
        'password' => array(
            'type' => 'password',
            'default' => '',
            'doc' => '(maintainers) your PEAR account password',
            'prompt' => 'PEAR password (for maintainers)',
            'group' => 'Maintainers',
            ),
        // Advanced
        'verbose' => array(
            'type' => 'integer',
            'default' => PEAR_CONFIG_DEFAULT_VERBOSE,
            'doc' => 'verbosity level
0: really quiet
1: somewhat quiet
2: verbose
3: debug',
            'prompt' => 'Debug Log Level',
            'group' => 'Advanced',
            ),
        'preferred_state' => array(
            'type' => 'set',
            'default' => PEAR_CONFIG_DEFAULT_PREFERRED_STATE,
            'doc' => 'the installer will prefer releases with this state when installing packages without a version or state specified',
            'valid_set' => array(
                'stable', 'beta', 'alpha', 'devel', 'snapshot'),
            'prompt' => 'Preferred Package State',
            'group' => 'Advanced',
            ),
        'umask' => array(
            'type' => 'mask',
            'default' => PEAR_CONFIG_DEFAULT_UMASK,
            'doc' => 'umask used when creating files (Unix-like systems only)',
            'prompt' => 'Unix file mask',
            'group' => 'Advanced',
            ),
        'cache_ttl' => array(
            'type' => 'integer',
            'default' => PEAR_CONFIG_DEFAULT_CACHE_TTL,
            'doc' => 'amount of secs where the local cache is used and not updated',
            'prompt' => 'Cache TimeToLive',
            'group' => 'Advanced',
            ),
        'sig_type' => array(
            'type' => 'set',
            'default' => PEAR_CONFIG_DEFAULT_SIG_TYPE,
            'doc' => 'which package signature mechanism to use',
            'valid_set' => array('gpg'),
            'prompt' => 'Package Signature Type',
            'group' => 'Maintainers',
            ),
        'sig_bin' => array(
            'type' => 'string',
            'default' => PEAR_CONFIG_DEFAULT_SIG_BIN,
            'doc' => 'which package signature mechanism to use',
            'prompt' => 'Signature Handling Program',
            'group' => 'Maintainers',
            ),
        'sig_keyid' => array(
            'type' => 'string',
            'default' => '',
            'doc' => 'which key to use for signing with',
            'prompt' => 'Signature Key Id',
            'group' => 'Maintainers',
            ),
        'sig_keydir' => array(
            'type' => 'directory',
            'default' => PEAR_CONFIG_DEFAULT_SIG_KEYDIR,
            'doc' => 'directory where signature keys are located',
            'prompt' => 'Signature Key Directory',
            'group' => 'Maintainers',
            ),
        // __channels is reserved - used for channel-specific configuration
        );

    // }}}

    // {{{ PEAR_Config([file], [defaults_file])

    /**
     * Constructor.
     *
     * @param string file to read user-defined options from
     * @param string file to read system-wide defaults from
     * @param bool   determines whether a registry object "follows"
     *               the value of php_dir (is automatically created
     *               and moved when php_dir is changed)
     * @param bool   if true, fails if configuration files cannot be loaded
     *
     * @access public
     *
     * @see PEAR_Config::singleton
     */
    function PEAR_Config($user_file = '', $system_file = '', $ftp_file = false,
                         $strict = true)
    {
        $this->PEAR();
        PEAR_Installer_Role::initializeConfig($this);
        $sl = DIRECTORY_SEPARATOR;
        if (empty($user_file)) {
            if (OS_WINDOWS) {
                $user_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini';
            } else {
                $user_file = getenv('HOME') . $sl . '.pearrc';
            }
        }
        if (empty($system_file)) {
            if (OS_WINDOWS) {
                $system_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pearsys.ini';
            } else {
                $system_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.conf';
            }
        }

        $this->layers = array_keys($this->configuration);
        $this->files['user'] = $user_file;
        $this->files['system'] = $system_file;
        if ($user_file && file_exists($user_file)) {
            $this->pushErrorHandling(PEAR_ERROR_RETURN);
            $this->readConfigFile($user_file, 'user', $strict);
            $this->popErrorHandling();
            if ($this->_errorsFound > 0) {
                return;
            }
        }

        if ($system_file && file_exists($system_file)) {
            $this->mergeConfigFile($system_file, false, 'system', $strict);
            if ($this->_errorsFound > 0) {
                return;
            }

        }

        if (!$ftp_file) {
            $ftp_file = $this->get('remote_config');
        }

        if ($ftp_file && defined('PEAR_REMOTEINSTALL_OK')) {
            $this->readFTPConfigFile($ftp_file);
        }

        foreach ($this->configuration_info as $key => $info) {
            $this->configuration['default'][$key] = $info['default'];
        }

        $this->_registry['default'] = &new PEAR_Registry($this->configuration['default']['php_dir']);
        $this->_registry['default']->setConfig($this, false);
        $this->_regInitialized['default'] = false;
        //$GLOBALS['_PEAR_Config_instance'] = &$this;
    }

    // }}}
    /**
     * Return the default locations of user and system configuration files
     * @static
     */
    function getDefaultConfigFiles()
    {
        $sl = DIRECTORY_SEPARATOR;
        if (OS_WINDOWS) {
            return array(
                'user' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini',
                'system' =>  PEAR_CONFIG_SYSCONFDIR . $sl . 'pearsys.ini'
            );
        } else {
            return array(
                'user' => getenv('HOME') . $sl . '.pearrc',
                'system' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.conf'
            );
        }
    }
    // {{{ singleton([file], [defaults_file])

    /**
     * Static singleton method.  If you want to keep only one instance
     * of this class in use, this method will give you a reference to
     * the last created PEAR_Config object if one exists, or create a
     * new object.
     *
     * @param string (optional) file to read user-defined options from
     * @param string (optional) file to read system-wide defaults from
     *
     * @return object an existing or new PEAR_Config instance
     *
     * @access public
     *
     * @see PEAR_Config::PEAR_Config
     */
    function &singleton($user_file = '', $system_file = '', $strict = true)
    {
        if (is_object($GLOBALS['_PEAR_Config_instance'])) {
            return $GLOBALS['_PEAR_Config_instance'];
        }

        $t_conf = &new PEAR_Config($user_file, $system_file, false, $strict);
        if ($t_conf->_errorsFound > 0) {
             return $t_conf->lastError;
        }

        $GLOBALS['_PEAR_Config_instance'] = &$t_conf;
        return $GLOBALS['_PEAR_Config_instance'];
    }

    // }}}
    // {{{ validConfiguration()

    /**
     * Determine whether any configuration files have been detected, and whether a
     * registry object can be retrieved from this configuration.
     * @return bool
     * @since PEAR 1.4.0a1
     */
    function validConfiguration()
    {
        if ($this->isDefinedLayer('user') || $this->isDefinedLayer('system')) {
            return true;
        }
        return false;
    }

    // }}}
    // {{{ readConfigFile([file], [layer])

    /**
     * Reads configuration data from a file.  All existing values in
     * the config layer are discarded and replaced with data from the
     * file.
     * @param string file to read from, if NULL or not specified, the
     *               last-used file for the same layer (second param) is used
     * @param string config layer to insert data into ('user' or 'system')
     * @return bool TRUE on success or a PEAR error on failure
     */
    function readConfigFile($file = null, $layer = 'user', $strict = true)
    {
        if (empty($this->files[$layer])) {
            return $this->raiseError("unknown config layer `$layer'");
        }

        if ($file === null) {
            $file = $this->files[$layer];
        }

        $data = $this->_readConfigDataFrom($file);

        if (PEAR::isError($data)) {
            if ($strict) {
                $this->_errorsFound++;
                $this->lastError = $data;

                return $data;
            } else {
                return true;
            }
        } else {
            $this->files[$layer] = $file;
        }

        $this->_decodeInput($data);
        $this->configuration[$layer] = $data;
        $this->_setupChannels();
        if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) {
            $this->_registry[$layer] = &new PEAR_Registry($phpdir);
            $this->_registry[$layer]->setConfig($this, false);
            $this->_regInitialized[$layer] = false;
        } else {
            unset($this->_registry[$layer]);
        }
        return true;
    }

    // }}}

    /**
     * @param string url to the remote config file, like ftp://www.example.com/pear/config.ini
     * @return true|PEAR_Error
     */
    function readFTPConfigFile($path)
    {
        do { // poor man's try
            if (!class_exists('PEAR_FTP')) {
                if (!class_exists('PEAR_Common')) {
                    require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Common.php';
                }
                if (PEAR_Common::isIncludeable('PEAR/FTP.php')) {
                    require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/FTP.php';
                }
            }
            if (class_exists('PEAR_FTP')) {
                $this->_ftp = &new PEAR_FTP;
                $this->_ftp->pushErrorHandling(PEAR_ERROR_RETURN);
                $e = $this->_ftp->init($path);
                if (PEAR::isError($e)) {
                    $this->_ftp->popErrorHandling();
                    return $e;
                }
                $tmp = System::mktemp('-d');
                PEAR_Common::addTempFile($tmp);
                $e = $this->_ftp->get(basename($path), $tmp . DIRECTORY_SEPARATOR .
                    'pear.ini', false, FTP_BINARY);
                if (PEAR::isError($e)) {
                    $this->_ftp->popErrorHandling();
                    return $e;
                }
                PEAR_Common::addTempFile($tmp . DIRECTORY_SEPARATOR . 'pear.ini');
                $this->_ftp->disconnect();
                $this->_ftp->popErrorHandling();
                $this->files['ftp'] = $tmp . DIRECTORY_SEPARATOR . 'pear.ini';
                $e = $this->readConfigFile(null, 'ftp');
                if (PEAR::isError($e)) {
                    return $e;
                }
                $fail = array();
                foreach ($this->configuration_info as $key => $val) {
                    if (in_array($this->getGroup($key),
                          array('File Locations', 'File Locations (Advanced)')) &&
                          $this->getType($key) == 'directory') {
                        // any directory configs must be set for this to work
                        if (!isset($this->configuration['ftp'][$key])) {
                            $fail[] = $key;
                        }
                    }
                }
                if (count($fail)) {
                    $fail = '"' . implode('", "', $fail) . '"';
                    unset($this->files['ftp']);
                    unset($this->configuration['ftp']);
                    return PEAR::raiseError('ERROR: Ftp configuration file must set all ' .
                        'directory configuration variables.  These variables were not set: ' .
                        $fail);
                } else {
                    return true;
                }
            } else {
                return PEAR::raiseError('PEAR_RemoteInstaller must be installed to use remote config');
            }
        } while (false); // poor man's catch
        unset($this->files['ftp']);
        return PEAR::raiseError('no remote host specified');
    }

    // {{{ _setupChannels()
    
    /**
     * Reads the existing configurations and creates the _channels array from it
     */
    function _setupChannels()
    {
        $set = array_flip(array_values($this->_channels));
        foreach ($this->configuration as $layer => $data) {
            $i = 1000;
            if (isset($data['__channels']) && is_array($data['__channels'])) {
                foreach ($data['__channels'] as $channel => $info) {
                    $set[$channel] = $i++;
                }
            }
        }
        $this->_channels = array_values(array_flip($set));
        $this->setChannels($this->_channels);
    }

    // }}}
    // {{{ deleteChannel(channel)

    function deleteChannel($channel)
    {
        foreach ($this->configuration as $layer => $data) {
            if (isset($data['__channels'])) {
                if (isset($data['__channels'][strtolower($channel)])) {
                    unset($this->configuration[$layer]['__channels'][strtolower($channel)]);
                }
            }
        }
        $this->_channels = array_flip($this->_channels);
        unset($this->_channels[strtolower($channel)]);
        $this->_channels = array_flip($this->_channels);
    }

    // }}}
    // {{{ mergeConfigFile(file, [override], [layer])

    /**
     * Merges data into a config layer from a file.  Does the same
     * thing as readConfigFile, except it does not replace all
     * existing values in the config layer.
     * @param string file to read from
     * @param bool whether to overwrite existing data (default TRUE)
     * @param string config layer to insert data into ('user' or 'system')
     * @param string if true, errors are returned if file opening fails
     * @return bool TRUE on success or a PEAR error on failure
     */
    function mergeConfigFile($file, $override = true, $layer = 'user', $strict = true)
    {
        if (empty($this->files[$layer])) {
            return $this->raiseError("unknown config layer `$layer'");
        }
        if ($file === null) {
            $file = $this->files[$layer];
        }
        $data = $this->_readConfigDataFrom($file);
        if (PEAR::isError($data)) {
            if ($strict) {
                $this->_errorsFound++;
                $this->lastError = $data;

                return $data;
            } else {
                return true;
            }
        }
        $this->_decodeInput($data);
        if ($override) {
            $this->configuration[$layer] =
                PEAR_Config::arrayMergeRecursive($this->configuration[$layer], $data);
        } else {
            $this->configuration[$layer] =
                PEAR_Config::arrayMergeRecursive($data, $this->configuration[$layer]);
        }
        $this->_setupChannels();
        if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) {
            $this->_registry[$layer] = &new PEAR_Registry($phpdir);
            $this->_registry[$layer]->setConfig($this, false);
            $this->_regInitialized[$layer] = false;
        } else {
            unset($this->_registry[$layer]);
        }
        return true;
    }

    // }}}
    // {{{ arrayMergeRecursive($arr2, $arr1)
    /**
     * @param array
     * @param array
     * @return array
     * @static
     */
    function arrayMergeRecursive($arr2, $arr1)
    {
        $ret = array();
        foreach ($arr2 as $key => $data) {
            if (!isset($arr1[$key])) {
                $ret[$key] = $data;
                unset($arr1[$key]);
                continue;
            }
            if (is_array($data)) {
                if (!is_array($arr1[$key])) {
                    $ret[$key] = $arr1[$key];
                    unset($arr1[$key]);
                    continue;
                }
                $ret[$key] = PEAR_Config::arrayMergeRecursive($arr1[$key], $arr2[$key]);
                unset($arr1[$key]);
            }
        }
        return array_merge($ret, $arr1);
    }

    // }}}
    // {{{ writeConfigFile([file], [layer])

    /**
     * Writes data into a config layer from a file.
     *
     * @param string|null file to read from, or null for default
     * @param string config layer to insert data into ('user' or
     *               'system')
     * @param string|null data to write to config file or null for internal data [DEPRECATED]
     * @return bool TRUE on success or a PEAR error on failure
     */
    function writeConfigFile($file = null, $layer = 'user', $data = null)
    {
        $this->_lazyChannelSetup($layer);
        if ($layer == 'both' || $layer == 'all') {
            foreach ($this->files as $type => $file) {
                $err = $this->writeConfigFile($file, $type, $data);
                if (PEAR::isError($err)) {
                    return $err;
                }
            }
            return true;
        }
        if (empty($this->files[$layer])) {
            return $this->raiseError("unknown config file type `$layer'");
        }
        if ($file === null) {
            $file = $this->files[$layer];
        }
        $data = ($data === null) ? $this->configuration[$layer] : $data;
        $this->_encodeOutput($data);
        $opt = array('-p', dirname($file));
        if (!@System::mkDir($opt)) {
            return $this->raiseError("could not create directory: " . dirname($file));
        }
        if (file_exists($file) && is_file($file) && !is_writeable($file)) {
            return $this->raiseError("no write access to $file!");
        }
        $fp = @fopen($file, "w");
        if (!$fp) {
            return $this->raiseError("PEAR_Config::writeConfigFile fopen('$file','w') failed ($php_errormsg)");
        }
        $contents = "#PEAR_Config 0.9\n" . serialize($data);
        if (!@fwrite($fp, $contents)) {
            return $this->raiseError("PEAR_Config::writeConfigFile: fwrite failed ($php_errormsg)");
        }
        return true;
    }

    // }}}
    // {{{ _readConfigDataFrom(file)

    /**
     * Reads configuration data from a file and returns the parsed data
     * in an array.
     *
     * @param string file to read from
     *
     * @return array configuration data or a PEAR error on failure
     *
     * @access private
     */
    function _readConfigDataFrom($file)
    {
        $fp = false;
        if (file_exists($file)) {
            $fp = @fopen($file, "r");
        }
        if (!$fp) {
            return $this->raiseError("PEAR_Config::readConfigFile fopen('$file','r') failed");
        }
        $size = filesize($file);
        $rt = get_magic_quotes_runtime();
        set_magic_quotes_runtime(0);
        fclose($fp);
        $contents = file_get_contents($file);
        if (empty($contents)) {
            return $this->raiseError('Configuration file "' . $file . '" is empty');
        }
        
        set_magic_quotes_runtime($rt);

        $version = false;
        if (preg_match('/^#PEAR_Config\s+(\S+)\s+/si', $contents, $matches)) {
            $version = $matches[1];
            $contents = substr($contents, strlen($matches[0]));
        } else {
            // Museum config file
            if (substr($contents,0,2) == 'a:') {
                $version = '0.1';
            }
        }
        if ($version && version_compare("$version", '1', '<')) {

            // no '@', it is possible that unserialize
            // raises a notice but it seems to block IO to
            // STDOUT if a '@' is used and a notice is raise
            $data = unserialize($contents);

            if (!is_array($data) && !$data) {
                if ($contents == serialize(false)) {
                    $data = array();
                } else {
                    $err = $this->raiseError("PEAR_Config: bad data in $file");
                    return $err;
                }
            }
            if (!is_array($data)) {
                if (strlen(trim($contents)) > 0) {
                    $error = "PEAR_Config: bad data in $file";
                    $err = $this->raiseError($error);
                    return $err;
                } else {
                    $data = array();
                }
            }
        // add parsing of newer formats here...
        } else {
            $err = $this->raiseError("$file: unknown version `$version'");
            return $err; 
        }
        return $data;
    }

    // }}}
    // {{{ getConfFile(layer)
    /**
    * Gets the file used for storing the config for a layer
    *
    * @param string $layer 'user' or 'system'
    */

    function getConfFile($layer)
    {
        return $this->files[$layer];
    }

    // }}}

    /**
     * @param string Configuration class name, used for detecting duplicate calls
     * @param array information on a role as parsed from its xml file
     * @return true|PEAR_Error
     * @access private
     */
    function _addConfigVars($class, $vars)
    {
        static $called = array();
        if (isset($called[$class])) {
            return;
        }
        $called[$class] = 1;
        if (count($vars) > 3) {
            return $this->raiseError('Roles can only define 3 new config variables or less');
        }
        foreach ($vars as $name => $var) {
            if (!is_array($var)) {
                return $this->raiseError('Configuration information must be an array');
            }
            if (!isset($var['type'])) {
                return $this->raiseError('Configuration information must contain a type');
            } else {
                if (!in_array($var['type'],
                      array('string', 'mask', 'password', 'directory', 'file', 'set'))) {
                    return $this->raiseError(
                        'Configuration type must be one of directory, file, string, ' .
                        'mask, set, or password');
                }
            }
            if (!isset($var['default'])) {
                return $this->raiseError(
                    'Configuration information must contain a default value ("default" index)');
            } else {
                if (is_array($var['default'])) {
                    $real_default = '';
                    foreach ($var['default'] as $config_var => $val) {
                        if (strpos($config_var, 'text') === 0) {
                            $real_default .= $val;
                        } elseif (strpos($config_var, 'constant') === 0) {
                            if (defined($val)) {
                                $real_default .= constant($val);
                            } else {
                                return $this->raiseError(
                                    'Unknown constant "' . $val . '" requested in ' .
                                    'default value for configuration variable "' .
                                    $name . '"');
                            }
                        } elseif (isset($this->configuration_info[$config_var])) {
                            $real_default .=
                                $this->configuration_info[$config_var]['default'];
                        } else {
                            return $this->raiseError(
                                'Unknown request for "' . $config_var . '" value in ' .
                                'default value for configuration variable "' .
                                $name . '"');
                        }
                    }
                    $var['default'] = $real_default;
                }
                if ($var['type'] == 'integer') {
                    $var['default'] = (integer) $var['default'];
                }
            }
            if (!isset($var['doc'])) {
                return $this->raiseError(
                    'Configuration information must contain a summary ("doc" index)');
            }
            if (!isset($var['prompt'])) {
                return $this->raiseError(
                    'Configuration information must contain a simple prompt ("prompt" index)');
            }
            if (!isset($var['group'])) {
                return $this->raiseError(
                    'Configuration information must contain a simple group ("group" index)');
            }
            if (isset($this->configuration_info[$name])) {
                return $this->raiseError('Configuration variable "' . $name .
                    '" already exists');
            }
            $this->configuration_info[$name] = $var;
            // fix bug #7351: setting custom config variable in a channel fails
            $this->_channelConfigInfo[] = $name;
        }
        return true;
    }

    // {{{ _encodeOutput(&data)

    /**
     * Encodes/scrambles configuration data before writing to files.
     * Currently, 'password' values will be base64-encoded as to avoid
     * that people spot cleartext passwords by accident.
     *
     * @param array (reference) array to encode values in
     *
     * @return bool TRUE on success
     *
     * @access private
     */
    function _encodeOutput(&$data)
    {
        foreach ($data as $key => $value) {
            if ($key == '__channels') {
                foreach ($data['__channels'] as $channel => $blah) {
                    $this->_encodeOutput($data['__channels'][$channel]);
                }
            }
            if (!isset($this->configuration_info[$key])) {
                continue;
            }
            $type = $this->configuration_info[$key]['type'];
            switch ($type) {
                // we base64-encode passwords so they are at least
                // not shown in plain by accident
                case 'password': {
                    $data[$key] = base64_encode($data[$key]);
                    break;
                }
                case 'mask': {
                    $data[$key] = octdec($data[$key]);
                    break;
                }
            }
        }
        return true;
    }

    // }}}
    // {{{ _decodeInput(&data)

    /**
     * Decodes/unscrambles configuration data after reading from files.
     *
     * @param array (reference) array to encode values in
     *
     * @return bool TRUE on success
     *
     * @access private
     *
     * @see PEAR_Config::_encodeOutput
     */
    function _decodeInput(&$data)
    {
        if (!is_array($data)) {
            return true;
        }
        foreach ($data as $key => $value) {
            if ($key == '__channels') {
                foreach ($data['__channels'] as $channel => $blah) {
                    $this->_decodeInput($data['__channels'][$channel]);
                }
            }
            if (!isset($this->configuration_info[$key])) {
                continue;
            }
            $type = $this->configuration_info[$key]['type'];
            switch ($type) {
                case 'password': {
                    $data[$key] = base64_decode($data[$key]);
                    break;
                }
                case 'mask': {
                    $data[$key] = decoct($data[$key]);
                    break;
                }
            }
        }
        return true;
    }

    // }}}
    // {{{ getDefaultChannel([layer])
    /**
     * Retrieve the default channel.
     *
     * On startup, channels are not initialized, so if the default channel is not
     * pear.php.net, then initialize the config.
     * @param string registry layer
     * @return string|false
     */
    function getDefaultChannel($layer = null)
    {
        $ret = false;
        if ($layer === null) {
            foreach ($this->layers as $layer) {
                if (isset($this->configuration[$layer]['default_channel'])) {
                    $ret = $this->configuration[$layer]['default_channel'];
                    break;
                }
            }
        } elseif (isset($this->configuration[$layer]['default_channel'])) {
            $ret = $this->configuration[$layer]['default_channel'];
        }
        if ($ret == 'pear.php.net' && defined('PEAR_RUNTYPE') && PEAR_RUNTYPE == 'pecl') {
            $ret = 'pecl.php.net';
        }
        if ($ret) {
            if ($ret != 'pear.php.net') {
                $this->_lazyChannelSetup();
            }
            return $ret;
        }
        return PEAR_CONFIG_DEFAULT_CHANNEL;
    }

    // {{{ get(key, [layer])
    /**
     * Returns a configuration value, prioritizing layers as per the
     * layers property.
     *
     * @param string config key
     *
     * @return mixed the config value, or NULL if not found
     *
     * @access public
     */
    function get($key, $layer = null, $channel = false)
    {
        if (!isset($this->configuration_info[$key])) {
            return null;
        }
        if ($key == '__channels') {
            return null;
        }
        if ($key == 'default_channel') {
            return $this->getDefaultChannel($layer);
        }
        if (!$channel) {
            $channel = $this->getDefaultChannel();
        } elseif ($channel != 'pear.php.net') {
            $this->_lazyChannelSetup();
        }
        $channel = strtolower($channel);
        
        $test = (in_array($key, $this->_channelConfigInfo)) ?
            $this->_getChannelValue($key, $layer, $channel) :
            null;
        if ($test !== null) {
            if ($this->_installRoot) {
                if (in_array($this->getGroup($key),
                      array('File Locations', 'File Locations (Advanced)')) &&
                      $this->getType($key) == 'directory') {
                    return $this->_prependPath($test, $this->_installRoot);
                }
            }
            return $test;
        }
        if ($layer === null) {
            foreach ($this->layers as $layer) {
                if (isset($this->configuration[$layer][$key])) {
                    $test = $this->configuration[$layer][$key];
                    if ($this->_installRoot) {
                        if (in_array($this->getGroup($key),
                              array('File Locations', 'File Locations (Advanced)')) &&
                              $this->getType($key) == 'directory') {
                            return $this->_prependPath($test, $this->_installRoot);
                        }
                    }
                    if ($key == 'preferred_mirror') {
                        $reg = &$this->getRegistry();
                        if (is_object($reg)) {
                            $chan = &$reg->getChannel($channel);
                            if (PEAR::isError($chan)) {
                                return $channel;
                            }
                            if (!$chan->getMirror($test) && $chan->getName() != $test) {
                                return $channel; // mirror does not exist
                            }
                        }
                    }
                    return $test;
                }
            }
        } elseif (isset($this->configuration[$layer][$key])) {
            $test = $this->configuration[$layer][$key];
            if ($this->_installRoot) {
                if (in_array($this->getGroup($key),
                      array('File Locations', 'File Locations (Advanced)')) &&
                      $this->getType($key) == 'directory') {
                    return $this->_prependPath($test, $this->_installRoot);
                }
            }
            if ($key == 'preferred_mirror') {
                $reg = &$this->getRegistry();
                if (is_object($reg)) {
                    $chan = &$reg->getChannel($channel);
                    if (PEAR::isError($chan)) {
                        return $channel;
                    }
                    if (!$chan->getMirror($test) && $chan->getName() != $test) {
                        return $channel; // mirror does not exist
                    }
                }
            }
            return $test;
        }
        return null;
    }

    // }}}
    // {{{ _getChannelValue(key, value, [layer])
    /**
     * Returns a channel-specific configuration value, prioritizing layers as per the
     * layers property.
     *
     * @param string config key
     *
     * @return mixed the config value, or NULL if not found
     *
     * @access private
     */
    function _getChannelValue($key, $layer, $channel)
    {
        if ($key == '__channels' || $channel == 'pear.php.net') {
            return null;
        }
        $ret = null;
        if ($layer === null) {
            foreach ($this->layers as $ilayer) {
                if (isset($this->configuration[$ilayer]['__channels'][$channel][$key])) {
                    $ret = $this->configuration[$ilayer]['__channels'][$channel][$key];
                    break;
                }
            }
        } elseif (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
            $ret = $this->configuration[$layer]['__channels'][$channel][$key];
        }
        if ($key == 'preferred_mirror') {
            if ($ret !== null) {
                $reg = &$this->getRegistry($layer);
                if (is_object($reg)) {
                    $chan = &$reg->getChannel($channel);
                    if (PEAR::isError($chan)) {
                        return $channel;
                    }
                    if (!$chan->getMirror($ret) && $chan->getName() != $ret) {
                        return $channel; // mirror does not exist
                    }
                }
                return $ret;
            }
            if ($channel != $this->getDefaultChannel($layer)) {
                return $channel; // we must use the channel name as the preferred mirror
                                 // if the user has not chosen an alternate
            } else {
                return $this->getDefaultChannel($layer);
            }
        }
        return $ret;
    }


    // }}}
    // {{{ set(key, value, [layer])

    /**
     * Set a config value in a specific layer (defaults to 'user').
     * Enforces the types defined in the configuration_info array.  An
     * integer config variable will be cast to int, and a set config
     * variable will be validated against its legal values.
     *
     * @param string config key
     * @param string config value
     * @param string (optional) config layer
     * @param string channel to set this value for, or null for global value
     * @return bool TRUE on success, FALSE on failure
     */
    function set($key, $value, $layer = 'user', $channel = false)
    {
        if ($key == '__channels') {
            return false;
        }
        if (!isset($this->configuration[$layer])) {
            return false;
        }
        if ($key == 'default_channel') {
            // can only set this value globally
            $channel = 'pear.php.net';
            if ($value != 'pear.php.net') {
                $this->_lazyChannelSetup($layer);
            }
        }
        if ($key == 'preferred_mirror') {
            if ($channel == '__uri') {
                return false; // can't set the __uri pseudo-channel's mirror
            }
            $reg = &$this->getRegistry($layer);
            if (is_object($reg)) {
                $chan = &$reg->getChannel($channel ? $channel : 'pear.php.net');
                if (PEAR::isError($chan)) {
                    return false;
                }
                if (!$chan->getMirror($value) && $chan->getName() != $value) {
                    return false; // mirror does not exist
                }
            }
        }
        if (!isset($this->configuration_info[$key])) {
            return false;
        }
        extract($this->configuration_info[$key]);
        switch ($type) {
            case 'integer':
                $value = (int)$value;
                break;
            case 'set': {
                // If a valid_set is specified, require the value to
                // be in the set.  If there is no valid_set, accept
                // any value.
                if ($valid_set) {
                    reset($valid_set);
                    if ((key($valid_set) === 0 && !in_array($value, $valid_set)) ||
                        (key($valid_set) !== 0 && empty($valid_set[$value])))
                    {
                        return false;
                    }
                }
                break;
            }
        }
        if (!$channel) {
            $channel = $this->get('default_channel', null, 'pear.php.net');
        }
        if (!in_array($channel, $this->_channels)) {
            $this->_lazyChannelSetup($layer);
            $reg = &$this->getRegistry($layer);
            if ($reg) {
                $channel = $reg->channelName($channel);
            }
            if (!in_array($channel, $this->_channels)) {
                return false;
            }
        }
        if ($channel != 'pear.php.net') {
            if (in_array($key, $this->_channelConfigInfo)) {
                $this->configuration[$layer]['__channels'][$channel][$key] = $value;
                return true;
            } else {
                return false;
            }
        } else {
            if ($key == 'default_channel') {
                if (!isset($reg)) {
                    $reg = &$this->getRegistry($layer);
                    if (!$reg) {
                        $reg = &$this->getRegistry();
                    }
                }
                if ($reg) {
                    $value = $reg->channelName($value);
                }
                if (!$value) {
                    return false;
                }
            }
        }
        $this->configuration[$layer][$key] = $value;
        if ($key == 'php_dir' && !$this->_noRegistry) {
            if (!isset($this->_registry[$layer]) ||
                  $value != $this->_registry[$layer]->install_dir) {
                $this->_registry[$layer] = &new PEAR_Registry($value);
                $this->_regInitialized[$layer] = false;
                $this->_registry[$layer]->setConfig($this, false);
            }
        }
        return true;
    }

    // }}}
    function _lazyChannelSetup($uselayer = false)
    {
        if ($this->_noRegistry) {
            return;
        }
        $merge = false;
        foreach ($this->_registry as $layer => $p) {
            if ($uselayer && $uselayer != $layer) {
                continue;
            }
            if (!$this->_regInitialized[$layer]) {
                if ($layer == 'default' && isset($this->_registry['user']) ||
                      isset($this->_registry['system'])) {
                    // only use the default registry if there are no alternatives
                    continue;
                }
                if (!is_object($this->_registry[$layer])) {
                    if ($phpdir = $this->get('php_dir', $layer, 'pear.php.net')) {
                        $this->_registry[$layer] = &new PEAR_Registry($phpdir);
                        $this->_registry[$layer]->setConfig($this, false);
                        $this->_regInitialized[$layer] = false;
                    } else {
                        unset($this->_registry[$layer]);
                        return;
                    }
                }
                $this->setChannels($this->_registry[$layer]->listChannels(), $merge);
                $this->_regInitialized[$layer] = true;
                $merge = true;
            }
        }
    }
    // {{{ setChannels()
    
    /**
     * Set the list of channels.
     *
     * This should be set via a call to {@link PEAR_Registry::listChannels()}
     * @param array
     * @param bool
     * @return bool success of operation
     */
    function setChannels($channels, $merge = false)
    {
        if (!is_array($channels)) {
            return false;
        }
        if ($merge) {
            $this->_channels = array_merge($this->_channels, $channels);
        } else {
            $this->_channels = $channels;
        }
        foreach ($channels as $channel) {
            $channel = strtolower($channel);
            if ($channel == 'pear.php.net') {
                continue;
            }
            foreach ($this->layers as $layer) {
                if (!isset($this->configuration[$layer]['__channels'])) {
                    $this->configuration[$layer]['__channels'] = array();
                }
                if (!isset($this->configuration[$layer]['__channels'][$channel])
                      || !is_array($this->configuration[$layer]['__channels'][$channel])) {
                    $this->configuration[$layer]['__channels'][$channel] = array();
                }
            }
        }
        return true;
    }

    // }}}
    // {{{ getType(key)

    /**
     * Get the type of a config value.
     *
     * @param string  config key
     *
     * @return string type, one of "string", "integer", "file",
     * "directory", "set" or "password".
     *
     * @access public
     *
     */
    function getType($key)
    {
        if (isset($this->configuration_info[$key])) {
            return $this->configuration_info[$key]['type'];
        }
        return false;
    }

    // }}}
    // {{{ getDocs(key)

    /**
     * Get the documentation for a config value.
     *
     * @param string  config key
     *
     * @return string documentation string
     *
     * @access public
     *
     */
    function getDocs($key)
    {
        if (isset($this->configuration_info[$key])) {
            return $this->configuration_info[$key]['doc'];
        }
        return false;
    }
       // }}}
    // {{{ getPrompt(key)

    /**
     * Get the short documentation for a config value.
     *
     * @param string  config key
     *
     * @return string short documentation string
     *
     * @access public
     *
     */
    function getPrompt($key)
    {
        if (isset($this->configuration_info[$key])) {
            return $this->configuration_info[$key]['prompt'];
        }
        return false;
    }
    // }}}
    // {{{ getGroup(key)

    /**
     * Get the parameter group for a config key.
     *
     * @param string  config key
     *
     * @return string parameter group
     *
     * @access public
     *
     */
    function getGroup($key)
    {
        if (isset($this->configuration_info[$key])) {
            return $this->configuration_info[$key]['group'];
        }
        return false;
    }

    // }}}
    // {{{ getGroups()

    /**
     * Get the list of parameter groups.
     *
     * @return array list of parameter groups
     *
     * @access public
     *
     */
    function getGroups()
    {
        $tmp = array();
        foreach ($this->configuration_info as $key => $info) {
            $tmp[$info['group']] = 1;
        }
        return array_keys($tmp);
    }

    // }}}
    // {{{ getGroupKeys()

    /**
     * Get the list of the parameters in a group.
     *
     * @param string $group parameter group
     *
     * @return array list of parameters in $group
     *
     * @access public
     *
     */
    function getGroupKeys($group)
    {
        $keys = array();
        foreach ($this->configuration_info as $key => $info) {
            if ($info['group'] == $group) {
                $keys[] = $key;
            }
        }
        return $keys;
    }

    // }}}
    // {{{ getSetValues(key)

    /**
     * Get the list of allowed set values for a config value.  Returns
     * NULL for config values that are not sets.
     *
     * @param string  config key
     *
     * @return array enumerated array of set values, or NULL if the
     *               config key is unknown or not a set
     *
     * @access public
     *
     */
    function getSetValues($key)
    {
        if (isset($this->configuration_info[$key]) &&
            isset($this->configuration_info[$key]['type']) &&
            $this->configuration_info[$key]['type'] == 'set')
        {
            $valid_set = $this->configuration_info[$key]['valid_set'];
            reset($valid_set);
            if (key($valid_set) === 0) {
                return $valid_set;
            }
            return array_keys($valid_set);
        }
        return null;
    }

    // }}}
    // {{{ getKeys()

    /**
     * Get all the current config keys.
     *
     * @return array simple array of config keys
     *
     * @access public
     */
    function getKeys()
    {
        $keys = array();
        foreach ($this->layers as $layer) {
            $test = $this->configuration[$layer];
            if (isset($test['__channels'])) {
                foreach ($test['__channels'] as $channel => $configs) {
                    $keys = array_merge($keys, $configs);
                }
            }
            unset($test['__channels']);
            $keys = array_merge($keys, $test);
        }
        return array_keys($keys);
    }

    // }}}
    // {{{ remove(key, [layer])

    /**
     * Remove the a config key from a specific config layer.
     *
     * @param string config key
     *
     * @param string (optional) config layer
     *
     * @return bool TRUE on success, FALSE on failure
     *
     * @access public
     */
    function remove($key, $layer = 'user')
    {
        $channel = $this->getDefaultChannel();
        if ($channel !== 'pear.php.net') {
            if (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
                unset($this->configuration[$layer]['__channels'][$channel][$key]);
                return true;
            }
        }
        if (isset($this->configuration[$layer][$key])) {
            unset($this->configuration[$layer][$key]);
            return true;
        }
        return false;
    }

    // }}}
    // {{{ removeLayer(layer)

    /**
     * Temporarily remove an entire config layer.  USE WITH CARE!
     *
     * @param string config key
     *
     * @param string (optional) config layer
     *
     * @return bool TRUE on success, FALSE on failure
     *
     * @access public
     */
    function removeLayer($layer)
    {
        if (isset($this->configuration[$layer])) {
            $this->configuration[$layer] = array();
            return true;
        }
        return false;
    }

    // }}}
    // {{{ store([layer])

    /**
     * Stores configuration data in a layer.
     *
     * @param string config layer to store
     *
     * @return bool TRUE on success, or PEAR error on failure
     *
     * @access public
     */
    function store($layer = 'user', $data = null)
    {
        return $this->writeConfigFile(null, $layer, $data);
    }

    // }}}
    // {{{ toDefault(key)

    /**
     * Unset the user-defined value of a config key, reverting the
     * value to the system-defined one.
     *
     * @param string config key
     *
     * @return bool TRUE on success, FALSE on failure
     *
     * @access public
     */
    function toDefault($key)
    {
        trigger_error("PEAR_Config::toDefault() deprecated, use PEAR_Config::remove() instead", E_USER_NOTICE);
        return $this->remove($key, 'user');
    }

    // }}}
    // {{{ definedBy(key)

    /**
     * Tells what config layer that gets to define a key.
     *
     * @param string config key
     * @param boolean return the defining channel
     *
     * @return string|array the config layer, or an empty string if not found.
     *
     *         if $returnchannel, the return is an array array('layer' => layername,
     *         'channel' => channelname), or an empty string if not found
     *
     * @access public
     */
    function definedBy($key, $returnchannel = false)
    {
        foreach ($this->layers as $layer) {
            $channel = $this->getDefaultChannel();
            if ($channel !== 'pear.php.net') {
                if (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
                    if ($returnchannel) {
                        return array('layer' => $layer, 'channel' => $channel);
                    }
                    return $layer;
                }
            }
            if (isset($this->configuration[$layer][$key])) {
                if ($returnchannel) {
                    return array('layer' => $layer, 'channel' => 'pear.php.net');
                }
                return $layer;
            }
        }
        return '';
    }

    // }}}
    // {{{ isDefaulted(key)

    /**
     * Tells whether a config value has a system-defined value.
     *
     * @param string   config key
     *
     * @return bool
     *
     * @access public
     *
     * @deprecated
     */
    function isDefaulted($key)
    {
        trigger_error("PEAR_Config::isDefaulted() deprecated, use PEAR_Config::definedBy() instead", E_USER_NOTICE);
        return $this->definedBy($key) == 'system';
    }

    // }}}
    // {{{ isDefined(key)

    /**
     * Tells whether a given key exists as a config value.
     *
     * @param string config key
     *
     * @return bool whether <config key> exists in this object
     *
     * @access public
     */
    function isDefined($key)
    {
        foreach ($this->layers as $layer) {
            if (isset($this->configuration[$layer][$key])) {
                return true;
            }
        }
        return false;
    }

    // }}}
    // {{{ isDefinedLayer(key)

    /**
     * Tells whether a given config layer exists.
     *
     * @param string config layer
     *
     * @return bool whether <config layer> exists in this object
     *
     * @access public
     */
    function isDefinedLayer($layer)
    {
        return isset($this->configuration[$layer]);
    }

    // }}}
    // {{{ getLayers()

    /**
     * Returns the layers defined (except the 'default' one)
     *
     * @return array of the defined layers
     */
    function getLayers()
    {
        $cf = $this->configuration;
        unset($cf['default']);
        return array_keys($cf);
    }

    // }}}
    // {{{ apiVersion()
    function apiVersion()
    {
        return '1.1';
    }
    // }}}

    /**
     * @return PEAR_Registry
     */
    function &getRegistry($use = null)
    {
        if ($use === null) {
            $layer = 'user';
        } else {
            $layer = $use;
        }
        if (isset($this->_registry[$layer])) {
            return $this->_registry[$layer];
        } elseif ($use === null && isset($this->_registry['system'])) {
            return $this->_registry['system'];
        } elseif ($use === null && isset($this->_registry['default'])) {
            return $this->_registry['default'];
        } elseif ($use) {
            $a = false;
            return $a;
        } else {
            // only go here if null was passed in
            echo "CRITICAL ERROR: Registry could not be initialized from any value";
            exit(1);
        }
    }
    /**
     * This is to allow customization like the use of installroot
     * @param PEAR_Registry
     * @return bool
     */
    function setRegistry(&$reg, $layer = 'user')
    {
        if ($this->_noRegistry) {
            return false;
        }
        if (!in_array($layer, array('user', 'system'))) {
            return false;
        }
        $this->_registry[$layer] = &$reg;
        if (is_object($reg)) {
            $this->_registry[$layer]->setConfig($this, false);
        }
        return true;
    }

    function noRegistry()
    {
        $this->_noRegistry = true;
    }

    /**
     * @return PEAR_Remote
     */
    function &getRemote()
    {
        $remote = &new PEAR_Remote($this);
        return $remote;
    }

    /**
     * @return PEAR_REST
     */
    function &getREST($version, $options = array())
    {
        $version = str_replace('.', '', $version);
        if (!class_exists($class = 'PEAR_REST_' . $version)) {
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/REST/' . $version . '.php';
        }
        $remote = &new $class($this, $options);
        return $remote;
    }

    /**
     * The ftp server is set in {@link readFTPConfigFile()}.  It exists only if a
     * remote configuration file has been specified
     * @return PEAR_FTP|false
     */
    function &getFTP()
    {
        if (isset($this->_ftp)) {
            return $this->_ftp;
        } else {
            $a = false;
            return $a;
        }
    }

    // {{{ _prependPath($path, $prepend)

    function _prependPath($path, $prepend)
    {
        if (strlen($prepend) > 0) {
            if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
                if (preg_match('/^[a-z]:/i', $prepend)) {
                    $prepend = substr($prepend, 2);
                } elseif ($prepend{0} != '\\') {
                    $prepend = "\\$prepend";
                }
                $path = substr($path, 0, 2) . $prepend . substr($path, 2);
            } else {
                $path = $prepend . $path;
            }
        }
        return $path;
    }
    // }}}

    /**
     * @param string|false installation directory to prepend to all _dir variables, or false to
     *                     disable
     */
    function setInstallRoot($root)
    {
        if (substr($root, -1) == DIRECTORY_SEPARATOR) {
            $root = substr($root, 0, -1);
        }
        $old = $this->_installRoot;
        $this->_installRoot = $root;
        if (($old != $root) && !$this->_noRegistry) {
            foreach (array_keys($this->_registry) as $layer) {
                if ($layer == 'ftp' || !isset($this->_registry[$layer])) {
                    continue;
                }
                $this->_registry[$layer] =
                    &new PEAR_Registry($this->get('php_dir', $layer, 'pear.php.net'));
                $this->_registry[$layer]->setConfig($this, false);
                $this->_regInitialized[$layer] = false;
            }
        }
    }
}

?>
<?php
/**
 * PEAR_Dependency2, advanced dependency validation
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Dependency2.php,v 1.56 2008/01/03 20:26:35 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * Required for the PEAR_VALIDATE_* constants
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Validate.php';

/**
 * Dependency check for PEAR packages
 *
 * This class handles both version 1.0 and 2.0 dependencies
 * WARNING: *any* changes to this class must be duplicated in the
 * test_PEAR_Dependency2 class found in tests/PEAR_Dependency2/setup.php.inc,
 * or unit tests will not actually validate the changes
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Dependency2
{
    /**
     * One of the PEAR_VALIDATE_* states
     * @see PEAR_VALIDATE_NORMAL
     * @var integer
     */
    var $_state;
    /**
     * Command-line options to install/upgrade/uninstall commands
     * @param array
     */
    var $_options;
    /**
     * @var OS_Guess
     */
    var $_os;
    /**
     * @var PEAR_Registry
     */
    var $_registry;
    /**
     * @var PEAR_Config
     */
    var $_config;
    /**
     * @var PEAR_DependencyDB
     */
    var $_dependencydb;
    /**
     * Output of PEAR_Registry::parsedPackageName()
     * @var array
     */
    var $_currentPackage;
    /**
     * @param PEAR_Config
     * @param array installation options
     * @param array format of PEAR_Registry::parsedPackageName()
     * @param int installation state (one of PEAR_VALIDATE_*)
     */
    function PEAR_Dependency2(&$config, $installoptions, $package,
                              $state = PEAR_VALIDATE_INSTALLING)
    {
        $this->_config = &$config;
        if (!class_exists('PEAR_DependencyDB')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/DependencyDB.php';
        }
        if (isset($installoptions['packagingroot'])) {
            // make sure depdb is in the right location
            $config->setInstallRoot($installoptions['packagingroot']);
        }
        $this->_registry = &$config->getRegistry();
        $this->_dependencydb = &PEAR_DependencyDB::singleton($config);
        if (isset($installoptions['packagingroot'])) {
            $config->setInstallRoot(false);
        }
        $this->_options = $installoptions;
        $this->_state = $state;
        if (!class_exists('OS_Guess')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'OS/Guess.php';
        }
        $this->_os = new OS_Guess;
        $this->_currentPackage = $package;
    }

    function _getExtraString($dep)
    {
        $extra = ' (';
        if (isset($dep['uri'])) {
            return '';
        }
        if (isset($dep['recommended'])) {
            $extra .= 'recommended version ' . $dep['recommended'];
        } else {
            if (isset($dep['min'])) {
                $extra .= 'version >= ' . $dep['min'];
            }
            if (isset($dep['max'])) {
                if ($extra != ' (') {
                    $extra .= ', ';
                }
                $extra .= 'version <= ' . $dep['max'];
            }
            if (isset($dep['exclude'])) {
                if (!is_array($dep['exclude'])) {
                    $dep['exclude'] = array($dep['exclude']);
                }
                if ($extra != ' (') {
                    $extra .= ', ';
                }
                $extra .= 'excluded versions: ';
                foreach ($dep['exclude'] as $i => $exclude) {
                    if ($i) {
                        $extra .= ', ';
                    }
                    $extra .= $exclude;
                }
            }
        }
        $extra .= ')';
        if ($extra == ' ()') {
            $extra = '';
        }
        return $extra;
    }

    /**
     * This makes unit-testing a heck of a lot easier
     */
    function getPHP_OS()
    {
        return PHP_OS;
    }

    /**
     * This makes unit-testing a heck of a lot easier
     */
    function getsysname()
    {
        return $this->_os->getSysname();
    }

    /**
     * Specify a dependency on an OS.  Use arch for detailed os/processor information
     *
     * There are two generic OS dependencies that will be the most common, unix and windows.
     * Other options are linux, freebsd, darwin (OS X), sunos, irix, hpux, aix
     */
    function validateOsDependency($dep)
    {
        if ($this->_state != PEAR_VALIDATE_INSTALLING &&
              $this->_state != PEAR_VALIDATE_DOWNLOADING) {
            return true;
        }
        if (isset($dep['conflicts'])) {
            $not = true;
        } else {
            $not = false;
        }
        if ($dep['name'] == '*') {
            return true;
        }
        switch (strtolower($dep['name'])) {
            case 'windows' :
                if ($not) {
                    if (strtolower(substr($this->getPHP_OS(), 0, 3)) == 'win') {
                        if (!isset($this->_options['nodeps']) &&
                              !isset($this->_options['force'])) {
                            return $this->raiseError("Cannot install %s on Windows");
                        } else {
                            return $this->warning("warning: Cannot install %s on Windows");
                        }
                    }
                } else {
                    if (strtolower(substr($this->getPHP_OS(), 0, 3)) != 'win') {
                        if (!isset($this->_options['nodeps']) &&
                              !isset($this->_options['force'])) {
                            return $this->raiseError("Can only install %s on Windows");
                        } else {
                            return $this->warning("warning: Can only install %s on Windows");
                        }
                    }
                }
            break;
            case 'unix' :
                $unices = array('linux', 'freebsd', 'darwin', 'sunos', 'irix', 'hpux', 'aix');
                if ($not) {
                    if (in_array($this->getSysname(), $unices)) {
                        if (!isset($this->_options['nodeps']) &&
                              !isset($this->_options['force'])) {
                            return $this->raiseError("Cannot install %s on any Unix system");
                        } else {
                            return $this->warning(
                                "warning: Cannot install %s on any Unix system");
                        }
                    }
                } else {
                    if (!in_array($this->getSysname(), $unices)) {
                        if (!isset($this->_options['nodeps']) &&
                              !isset($this->_options['force'])) {
                            return $this->raiseError("Can only install %s on a Unix system");
                        } else {
                            return $this->warning(
                                "warning: Can only install %s on a Unix system");
                        }
                    }
                }
            break;
            default :
                if ($not) {
                    if (strtolower($dep['name']) == strtolower($this->getSysname())) {
                        if (!isset($this->_options['nodeps']) &&
                              !isset($this->_options['force'])) {
                            return $this->raiseError('Cannot install %s on ' . $dep['name'] .
                                ' operating system');
                        } else {
                            return $this->warning('warning: Cannot install %s on ' .
                                $dep['name'] . ' operating system');
                        }
                    }
                } else {
                    if (strtolower($dep['name']) != strtolower($this->getSysname())) {
                        if (!isset($this->_options['nodeps']) &&
                              !isset($this->_options['force'])) {
                            return $this->raiseError('Cannot install %s on ' .
                                $this->getSysname() .
                                ' operating system, can only install on ' . $dep['name']);
                        } else {
                            return $this->warning('warning: Cannot install %s on ' .
                                $this->getSysname() .
                                ' operating system, can only install on ' . $dep['name']);
                        }
                    }
                }
        }
        return true;
    }

    /**
     * This makes unit-testing a heck of a lot easier
     */
    function matchSignature($pattern)
    {
        return $this->_os->matchSignature($pattern);
    }

    /**
     * Specify a complex dependency on an OS/processor/kernel version,
     * Use OS for simple operating system dependency.
     *
     * This is the only dependency that accepts an eregable pattern.  The pattern
     * will be matched against the php_uname() output parsed by OS_Guess
     */
    function validateArchDependency($dep)
    {
        if ($this->_state != PEAR_VALIDATE_INSTALLING) {
            return true;
        }
        if (isset($dep['conflicts'])) {
            $not = true;
        } else {
            $not = false;
        }
        if (!$this->matchSignature($dep['pattern'])) {
            if (!$not) {
                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                    return $this->raiseError('%s Architecture dependency failed, does not ' .
                        'match "' . $dep['pattern'] . '"');
                } else {
                    return $this->warning('warning: %s Architecture dependency failed, does ' .
                        'not match "' . $dep['pattern'] . '"');
                }
            }
            return true;
        } else {
            if ($not) {
                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                    return $this->raiseError('%s Architecture dependency failed, required "' .
                        $dep['pattern'] . '"');
                } else {
                    return $this->warning('warning: %s Architecture dependency failed, ' .
                        'required "' . $dep['pattern'] . '"');
                }
            }
            return true;
        }
    }

    /**
     * This makes unit-testing a heck of a lot easier
     */
    function extension_loaded($name)
    {
        return extension_loaded($name);
    }

    /**
     * This makes unit-testing a heck of a lot easier
     */
    function phpversion($name = null)
    {
        if ($name !== null) {
            return phpversion($name);
        } else {
            return phpversion();
        }
    }

    function validateExtensionDependency($dep, $required = true)
    {
        if ($this->_state != PEAR_VALIDATE_INSTALLING &&
              $this->_state != PEAR_VALIDATE_DOWNLOADING) {
            return true;
        }
        $loaded = $this->extension_loaded($dep['name']);
        $extra = $this->_getExtraString($dep);
        if (isset($dep['exclude'])) {
            if (!is_array($dep['exclude'])) {
                $dep['exclude'] = array($dep['exclude']);
            }
        }
        if (!isset($dep['min']) && !isset($dep['max']) &&
              !isset($dep['recommended']) && !isset($dep['exclude'])) {
            if ($loaded) {
                if (isset($dep['conflicts'])) {
                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                        return $this->raiseError('%s conflicts with PHP extension "' .
                            $dep['name'] . '"' . $extra);
                    } else {
                        return $this->warning('warning: %s conflicts with PHP extension "' .
                            $dep['name'] . '"' . $extra);
                    }
                }
                return true;
            } else {
                if (isset($dep['conflicts'])) {
                    return true;
                }
                if ($required) {
                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                        return $this->raiseError('%s requires PHP extension "' .
                            $dep['name'] . '"' . $extra);
                    } else {
                        return $this->warning('warning: %s requires PHP extension "' .
                            $dep['name'] . '"' . $extra);
                    }
                } else {
                    return $this->warning('%s can optionally use PHP extension "' .
                        $dep['name'] . '"' . $extra);
                }
            }
        }
        if (!$loaded) {
            if (isset($dep['conflicts'])) {
                return true;
            }
            if (!$required) {
                return $this->warning('%s can optionally use PHP extension "' .
                    $dep['name'] . '"' . $extra);
            } else {
                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                    return $this->raiseError('%s requires PHP extension "' . $dep['name'] .
                        '"' . $extra);
                }
                    return $this->warning('warning: %s requires PHP extension "' . $dep['name'] .
                        '"' . $extra);
            }
        }
        $version = (string) $this->phpversion($dep['name']);
        if (empty($version)) {
            $version = '0';
        }
        $fail = false;
        if (isset($dep['min'])) {
            if (!version_compare($version, $dep['min'], '>=')) {
                $fail = true;
            }
        }
        if (isset($dep['max'])) {
            if (!version_compare($version, $dep['max'], '<=')) {
                $fail = true;
            }
        }
        if ($fail && !isset($dep['conflicts'])) {
            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                return $this->raiseError('%s requires PHP extension "' . $dep['name'] .
                    '"' . $extra . ', installed version is ' . $version);
            } else {
                return $this->warning('warning: %s requires PHP extension "' . $dep['name'] .
                    '"' . $extra . ', installed version is ' . $version);
            }
        } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail && isset($dep['conflicts'])) {
            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                return $this->raiseError('%s conflicts with PHP extension "' .
                    $dep['name'] . '"' . $extra . ', installed version is ' . $version);
            } else {
                return $this->warning('warning: %s conflicts with PHP extension "' .
                    $dep['name'] . '"' . $extra . ', installed version is ' . $version);
            }
        }
        if (isset($dep['exclude'])) {
            foreach ($dep['exclude'] as $exclude) {
                if (version_compare($version, $exclude, '==')) {
                    if (isset($dep['conflicts'])) {
                        continue;
                    }
                    if (!isset($this->_options['nodeps']) &&
                          !isset($this->_options['force'])) {
                        return $this->raiseError('%s is not compatible with PHP extension "' .
                            $dep['name'] . '" version ' .
                            $exclude);
                    } else {
                        return $this->warning('warning: %s is not compatible with PHP extension "' .
                            $dep['name'] . '" version ' .
                            $exclude);
                    }
                } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) {
                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                        return $this->raiseError('%s conflicts with PHP extension "' .
                            $dep['name'] . '"' . $extra . ', installed version is ' . $version);
                    } else {
                        return $this->warning('warning: %s conflicts with PHP extension "' .
                            $dep['name'] . '"' . $extra . ', installed version is ' . $version);
                    }
                }
            }
        }
        if (isset($dep['recommended'])) {
            if (version_compare($version, $dep['recommended'], '==')) {
                return true;
            } else {
                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                    return $this->raiseError('%s dependency: PHP extension ' . $dep['name'] .
                        ' version "' . $version . '"' .
                        ' is not the recommended version "' . $dep['recommended'] .
                        '", but may be compatible, use --force to install');
                } else {
                    return $this->warning('warning: %s dependency: PHP extension ' .
                        $dep['name'] . ' version "' . $version . '"' .
                        ' is not the recommended version "' . $dep['recommended'].'"');
                }
            }
        }
        return true;
    }

    function validatePhpDependency($dep)
    {
        if ($this->_state != PEAR_VALIDATE_INSTALLING &&
              $this->_state != PEAR_VALIDATE_DOWNLOADING) {
            return true;
        }
        $version = $this->phpversion();
        $extra = $this->_getExtraString($dep);
        if (isset($dep['exclude'])) {
            if (!is_array($dep['exclude'])) {
                $dep['exclude'] = array($dep['exclude']);
            }
        }
        if (isset($dep['min'])) {
            if (!version_compare($version, $dep['min'], '>=')) {
                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                    return $this->raiseError('%s requires PHP' .
                        $extra . ', installed version is ' . $version);
                } else {
                    return $this->warning('warning: %s requires PHP' .
                        $extra . ', installed version is ' . $version);
                }
            }
        }
        if (isset($dep['max'])) {
            if (!version_compare($version, $dep['max'], '<=')) {
                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                    return $this->raiseError('%s requires PHP' .
                        $extra . ', installed version is ' . $version);
                } else {
                    return $this->warning('warning: %s requires PHP' .
                        $extra . ', installed version is ' . $version);
                }
            }
        }
        if (isset($dep['exclude'])) {
            foreach ($dep['exclude'] as $exclude) {
                if (version_compare($version, $exclude, '==')) {
                    if (!isset($this->_options['nodeps']) &&
                          !isset($this->_options['force'])) {
                        return $this->raiseError('%s is not compatible with PHP version ' .
                            $exclude);
                    } else {
                        return $this->warning(
                            'warning: %s is not compatible with PHP version ' .
                            $exclude);
                    }
                }
            }
        }
        return true;
    }

    /**
     * This makes unit-testing a heck of a lot easier
     */
    function getPEARVersion()
    {
        return '1.7.2';
    }

    function validatePearinstallerDependency($dep)
    {
        $pearversion = $this->getPEARVersion();
        $extra = $this->_getExtraString($dep);
        if (isset($dep['exclude'])) {
            if (!is_array($dep['exclude'])) {
                $dep['exclude'] = array($dep['exclude']);
            }
        }
        if (version_compare($pearversion, $dep['min'], '<')) {
            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                return $this->raiseError('%s requires PEAR Installer' . $extra .
                    ', installed version is ' . $pearversion);
            } else {
                return $this->warning('warning: %s requires PEAR Installer' . $extra .
                    ', installed version is ' . $pearversion);
            }
        }
        if (isset($dep['max'])) {
            if (version_compare($pearversion, $dep['max'], '>')) {
                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                    return $this->raiseError('%s requires PEAR Installer' . $extra .
                        ', installed version is ' . $pearversion);
                } else {
                    return $this->warning('warning: %s requires PEAR Installer' . $extra .
                        ', installed version is ' . $pearversion);
                }
            }
        }
        if (isset($dep['exclude'])) {
            if (!isset($dep['exclude'][0])) {
                $dep['exclude'] = array($dep['exclude']);
            }
            foreach ($dep['exclude'] as $exclude) {
                if (version_compare($exclude, $pearversion, '==')) {
                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                        return $this->raiseError('%s is not compatible with PEAR Installer ' .
                            'version ' . $exclude);
                    } else {
                        return $this->warning('warning: %s is not compatible with PEAR ' .
                            'Installer version ' . $exclude);
                    }
                }
            }
        }
        return true;
    }

    function validateSubpackageDependency($dep, $required, $params)
    {
        return $this->validatePackageDependency($dep, $required, $params);
    }

    /**
     * @param array dependency information (2.0 format)
     * @param boolean whether this is a required dependency
     * @param array a list of downloaded packages to be installed, if any
     * @param boolean if true, then deps on pear.php.net that fail will also check
     *                against pecl.php.net packages to accomodate extensions that have
     *                moved to pecl.php.net from pear.php.net
     */
    function validatePackageDependency($dep, $required, $params, $depv1 = false)
    {
        if ($this->_state != PEAR_VALIDATE_INSTALLING &&
              $this->_state != PEAR_VALIDATE_DOWNLOADING) {
            return true;
        }
        if (isset($dep['providesextension'])) {
            if ($this->extension_loaded($dep['providesextension'])) {
                $save = $dep;
                $subdep = $dep;
                $subdep['name'] = $subdep['providesextension'];
                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                $ret = $this->validateExtensionDependency($subdep, $required);
                PEAR::popErrorHandling();
                if (!PEAR::isError($ret)) {
                    return true;
                }
            }
        }
        if ($this->_state == PEAR_VALIDATE_INSTALLING) {
            return $this->_validatePackageInstall($dep, $required, $depv1);
        }
        if ($this->_state == PEAR_VALIDATE_DOWNLOADING) {
            return $this->_validatePackageDownload($dep, $required, $params, $depv1);
        }
    }

    function _validatePackageDownload($dep, $required, $params, $depv1 = false)
    {
        $dep['package'] = $dep['name'];
        if (isset($dep['uri'])) {
            $dep['channel'] = '__uri';
        }
        $depname = $this->_registry->parsedPackageNameToString($dep, true);
        $found = false;
        foreach ($params as $param) {
            if ($param->isEqual(
                  array('package' => $dep['name'],
                        'channel' => $dep['channel']))) {
                $found = true;
                break;
            }
            if ($depv1 && $dep['channel'] == 'pear.php.net') {
                if ($param->isEqual(
                  array('package' => $dep['name'],
                        'channel' => 'pecl.php.net'))) {
                    $found = true;
                    break;
                }
            }
        }
        if (!$found && isset($dep['providesextension'])) {
            foreach ($params as $param) {
                if ($param->isExtension($dep['providesextension'])) {
                    $found = true;
                    break;
                }
            }
        }
        if ($found) {
            $version = $param->getVersion();
            $installed = false;
            $downloaded = true;
        } else {
            if ($this->_registry->packageExists($dep['name'], $dep['channel'])) {
                $installed = true;
                $downloaded = false;
                $version = $this->_registry->packageinfo($dep['name'], 'version',
                    $dep['channel']);
            } else {
                if ($dep['channel'] == 'pecl.php.net' && $this->_registry->packageExists($dep['name'],
                      'pear.php.net')) {
                    $installed = true;
                    $downloaded = false;
                    $version = $this->_registry->packageinfo($dep['name'], 'version',
                        'pear.php.net');
                } else {
                    $version = 'not installed or downloaded';
                    $installed = false;
                    $downloaded = false;
                }
            }
        }
        $extra = $this->_getExtraString($dep);
        if (isset($dep['exclude'])) {
            if (!is_array($dep['exclude'])) {
                $dep['exclude'] = array($dep['exclude']);
            }
        }
        if (!isset($dep['min']) && !isset($dep['max']) &&
              !isset($dep['recommended']) && !isset($dep['exclude'])) {
            if ($installed || $downloaded) {
                $installed = $installed ? 'installed' : 'downloaded';
                if (isset($dep['conflicts'])) {
                    if ($version) {
                        $rest = ", $installed version is " . $version;
                    } else {
                        $rest = '';
                    }
                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                        return $this->raiseError('%s conflicts with package "' . $depname . '"' .
                            $extra . $rest);
                    } else {
                        return $this->warning('warning: %s conflicts with package "' . $depname . '"' .
                            $extra . $rest);
                    }
                }
                return true;
            } else {
                if (isset($dep['conflicts'])) {
                    return true;
                }
                if ($required) {
                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                        return $this->raiseError('%s requires package "' . $depname . '"' .
                            $extra);
                    } else {
                        return $this->warning('warning: %s requires package "' . $depname . '"' .
                            $extra);
                    }
                } else {
                    return $this->warning('%s can optionally use package "' . $depname . '"' .
                        $extra);
                }
            }
        }
        if (!$installed && !$downloaded) {
            if (isset($dep['conflicts'])) {
                return true;
            }
            if ($required) {
                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                    return $this->raiseError('%s requires package "' . $depname . '"' .
                        $extra);
                } else {
                    return $this->warning('warning: %s requires package "' . $depname . '"' .
                        $extra);
                }
            } else {
                return $this->warning('%s can optionally use package "' . $depname . '"' .
                    $extra);
            }
        }
        $fail = false;
        if (isset($dep['min'])) {
            if (version_compare($version, $dep['min'], '<')) {
                $fail = true;
            }
        }
        if (isset($dep['max'])) {
            if (version_compare($version, $dep['max'], '>')) {
                $fail = true;
            }
        }
        if ($fail && !isset($dep['conflicts'])) {
            $installed = $installed ? 'installed' : 'downloaded';
            $dep['package'] = $dep['name'];
            $dep = $this->_registry->parsedPackageNameToString($dep, true);
            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                return $this->raiseError('%s requires package "' . $depname . '"' .
                    $extra . ", $installed version is " . $version);
            } else {
                return $this->warning('warning: %s requires package "' . $depname . '"' .
                    $extra . ", $installed version is " . $version);
            }
        } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail &&
              isset($dep['conflicts']) && !isset($dep['exclude'])) {
            $installed = $installed ? 'installed' : 'downloaded';
            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra .
                    ", $installed version is " . $version);
            } else {
                return $this->warning('warning: %s conflicts with package "' . $depname . '"' .
                    $extra . ", $installed version is " . $version);
            }
        }
        if (isset($dep['exclude'])) {
            $installed = $installed ? 'installed' : 'downloaded';
            foreach ($dep['exclude'] as $exclude) {
                if (version_compare($version, $exclude, '==') && !isset($dep['conflicts'])) {
                    if (!isset($this->_options['nodeps']) &&
                          !isset($this->_options['force'])) {
                        return $this->raiseError('%s is not compatible with ' .
                            $installed . ' package "' .
                            $depname . '" version ' .
                            $exclude);
                    } else {
                        return $this->warning('warning: %s is not compatible with ' .
                            $installed . ' package "' .
                            $depname . '" version ' .
                            $exclude);
                    }
                } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) {
                    $installed = $installed ? 'installed' : 'downloaded';
                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                        return $this->raiseError('%s conflicts with package "' . $depname . '"' .
                            $extra . ", $installed version is " . $version);
                    } else {
                        return $this->warning('warning: %s conflicts with package "' . $depname . '"' .
                            $extra . ", $installed version is " . $version);
                    }
                }
            }
        }
        if (isset($dep['recommended'])) {
            $installed = $installed ? 'installed' : 'downloaded';
            if (version_compare($version, $dep['recommended'], '==')) {
                return true;
            } else {
                if (!$found && $installed) {
                    $param = $this->_registry->getPackage($dep['name'], $dep['channel']);
                }
                if ($param) {
                    $found = false;
                    foreach ($params as $parent) {
                        if ($parent->isEqual($this->_currentPackage)) {
                            $found = true;
                            break;
                        }
                    }
                    if ($found) {
                        if ($param->isCompatible($parent)) {
                            return true;
                        }
                    } else { // this is for validPackage() calls
                        $parent = $this->_registry->getPackage($this->_currentPackage['package'],
                            $this->_currentPackage['channel']);
                        if ($parent !== null) {
                            if ($param->isCompatible($parent)) {
                                return true;
                            }
                        }
                    }
                }
                if (!isset($this->_options['nodeps']) && !isset($this->_options['force']) &&
                      !isset($this->_options['loose'])) {
                    return $this->raiseError('%s dependency package "' . $depname .
                        '" ' . $installed . ' version ' . $version . 
                        ' is not the recommended version ' . $dep['recommended'] .
                        ', but may be compatible, use --force to install');
                } else {
                    return $this->warning('warning: %s dependency package "' . $depname .
                        '" ' . $installed . ' version ' . $version .
                        ' is not the recommended version ' . $dep['recommended']);
                }
            }
        }
        return true;
    }

    function _validatePackageInstall($dep, $required, $depv1 = false)
    {
        return $this->_validatePackageDownload($dep, $required, array(), $depv1);
    }

    /**
     * Verify that uninstalling packages passed in to command line is OK.
     *
     * @param PEAR_Installer $dl
     * @return PEAR_Error|true
     */
    function validatePackageUninstall(&$dl)
    {
        if (PEAR::isError($this->_dependencydb)) {
            return $this->_dependencydb;
        }
        $params = array();
        // construct an array of "downloaded" packages to fool the package dependency checker
        // into using these to validate uninstalls of circular dependencies
        $downloaded = &$dl->getUninstallPackages();
        foreach ($downloaded as $i => $pf) {
            if (!class_exists('PEAR_Downloader_Package')) {
                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Downloader/Package.php';
            }
            $dp = &new PEAR_Downloader_Package($dl);
            $dp->setPackageFile($downloaded[$i]);
            $params[$i] = &$dp;
        }
        // check cache
        $memyselfandI = strtolower($this->_currentPackage['channel']) . '/' .
            strtolower($this->_currentPackage['package']);
        if (isset($dl->___uninstall_package_cache)) {
            $badpackages = $dl->___uninstall_package_cache;
            if (isset($badpackages[$memyselfandI]['warnings'])) {
                foreach ($badpackages[$memyselfandI]['warnings'] as $warning) {
                    $dl->log(0, $warning[0]);
                }
            }
            if (isset($badpackages[$memyselfandI]['errors'])) {
                foreach ($badpackages[$memyselfandI]['errors'] as $error) {
                    if (is_array($error)) {
                        $dl->log(0, $error[0]);
                    } else {
                        $dl->log(0, $error->getMessage());
                    }
                }
                if (isset($this->_options['nodeps']) || isset($this->_options['force'])) {
                    return $this->warning(
                        'warning: %s should not be uninstalled, other installed packages depend ' .
                        'on this package');
                } else {
                    return $this->raiseError(
                        '%s cannot be uninstalled, other installed packages depend on this package');
                }
            }
            return true;
        }
        // first, list the immediate parents of each package to be uninstalled
        $perpackagelist = array();
        $allparents = array();
        foreach ($params as $i => $param) {
            $a = array('channel' => strtolower($param->getChannel()),
                      'package' => strtolower($param->getPackage()));
            $deps = $this->_dependencydb->getDependentPackages($a);
            if ($deps) {
                foreach ($deps as $d) {
                    $pardeps = $this->_dependencydb->getDependencies($d);
                    foreach ($pardeps as $dep) {
                        if (strtolower($dep['dep']['channel']) == $a['channel'] &&
                              strtolower($dep['dep']['name']) == $a['package']) {
                            if (!isset($perpackagelist[$a['channel'] . '/' . $a['package']])) {
                                $perpackagelist[$a['channel'] . '/' . $a['package']] = array();
                            }
                            $perpackagelist[$a['channel'] . '/' . $a['package']][]
                                = array($d['channel'] . '/' . $d['package'], $dep);
                            if (!isset($allparents[$d['channel'] . '/' . $d['package']])) {
                                $allparents[$d['channel'] . '/' . $d['package']] = array();
                            }
                            if (!isset($allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']])) {
                                $allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']] = array();
                            }
                            $allparents[$d['channel'] . '/' . $d['package']]
                                       [$a['channel'] . '/' . $a['package']][]
                                = array($d, $dep);
                        }
                    }
                }
            }
        }
        // next, remove any packages from the parents list that are not installed
        $remove = array();
        foreach ($allparents as $parent => $d1) {
            foreach ($d1 as $d) {
                if ($this->_registry->packageExists($d[0][0]['package'], $d[0][0]['channel'])) {
                    continue;
                }
                $remove[$parent] = true;
            }
        }
        // next remove any packages from the parents list that are not passed in for
        // uninstallation
        foreach ($allparents as $parent => $d1) {
            foreach ($d1 as $d) {
                foreach ($params as $param) {
                    if (strtolower($param->getChannel()) == $d[0][0]['channel'] &&
                          strtolower($param->getPackage()) == $d[0][0]['package']) {
                        // found it
                        continue 3;
                    }
                }
                $remove[$parent] = true;
            }
        }
        // remove all packages whose dependencies fail
        // save which ones failed for error reporting
        $badchildren = array();
        do {
            $fail = false;
            foreach ($remove as $package => $unused) {
                if (!isset($allparents[$package])) {
                    continue;
                }
                foreach ($allparents[$package] as $kid => $d1) {
                    foreach ($d1 as $depinfo) {
                        if ($depinfo[1]['type'] != 'optional') {
                            if (isset($badchildren[$kid])) {
                                continue;
                            }
                            $badchildren[$kid] = true;
                            $remove[$kid] = true;
                            $fail = true;
                            continue 2;
                        }
                    }
                }
                if ($fail) {
                    // start over, we removed some children
                    continue 2;
                }
            }
        } while ($fail);
        // next, construct the list of packages that can't be uninstalled
        $badpackages = array();
        $save = $this->_currentPackage;
        foreach ($perpackagelist as $package => $packagedeps) {
            foreach ($packagedeps as $parent) {
                if (!isset($remove[$parent[0]])) {
                    continue;
                }
                $packagename = $this->_registry->parsePackageName($parent[0]);
                $packagename['channel'] = $this->_registry->channelAlias($packagename['channel']);
                $pa = $this->_registry->getPackage($packagename['package'], $packagename['channel']);
                $packagename['package'] = $pa->getPackage();
                $this->_currentPackage = $packagename;
                // parent is not present in uninstall list, make sure we can actually
                // uninstall it (parent dep is optional)
                $parentname['channel'] = $this->_registry->channelAlias($parent[1]['dep']['channel']);
                $pa = $this->_registry->getPackage($parent[1]['dep']['name'], $parent[1]['dep']['channel']);
                $parentname['package'] = $pa->getPackage();
                $parent[1]['dep']['package'] = $parentname['package'];
                $parent[1]['dep']['channel'] = $parentname['channel'];
                if ($parent[1]['type'] == 'optional') {
                    $test = $this->_validatePackageUninstall($parent[1]['dep'], false, $dl);
                    if ($test !== true) {
                        $badpackages[$package]['warnings'][] = $test;
                    }
                } else {
                    $test = $this->_validatePackageUninstall($parent[1]['dep'], true, $dl);
                    if ($test !== true) {
                        $badpackages[$package]['errors'][] = $test;
                    }
                }
            }
        }
        $this->_currentPackage = $save;
        $dl->___uninstall_package_cache = $badpackages;
        if (isset($badpackages[$memyselfandI])) {
            if (isset($badpackages[$memyselfandI]['warnings'])) {
                foreach ($badpackages[$memyselfandI]['warnings'] as $warning) {
                    $dl->log(0, $warning[0]);
                }
            }
            if (isset($badpackages[$memyselfandI]['errors'])) {
                foreach ($badpackages[$memyselfandI]['errors'] as $error) {
                    if (is_array($error)) {
                        $dl->log(0, $error[0]);
                    } else {
                        $dl->log(0, $error->getMessage());
                    }
                }
                if (isset($this->_options['nodeps']) || isset($this->_options['force'])) {
                    return $this->warning(
                        'warning: %s should not be uninstalled, other installed packages depend ' .
                        'on this package');
                } else {
                    return $this->raiseError(
                        '%s cannot be uninstalled, other installed packages depend on this package');
                }
            }
        }
        return true;
    }

    function _validatePackageUninstall($dep, $required, $dl)
    {
        $depname = $this->_registry->parsedPackageNameToString($dep, true);
        $version = $this->_registry->packageinfo($dep['package'], 'version',
            $dep['channel']);
        if (!$version) {
            return true;
        }
        $extra = $this->_getExtraString($dep);
        if (isset($dep['exclude'])) {
            if (!is_array($dep['exclude'])) {
                $dep['exclude'] = array($dep['exclude']);
            }
        }
        if (isset($dep['conflicts'])) {
            return true; // uninstall OK - these packages conflict (probably installed with --force)
        }
        if (!isset($dep['min']) && !isset($dep['max'])) {
            if ($required) {
                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                    return $this->raiseError('"' . $depname . '" is required by ' .
                        'installed package %s' . $extra);
                } else {
                    return $this->warning('warning: "' . $depname . '" is required by ' .
                        'installed package %s' . $extra);
                }
            } else {
                return $this->warning('"' . $depname . '" can be optionally used by ' .
                        'installed package %s' . $extra);
            }
        }
        $fail = false;
        if (isset($dep['min'])) {
            if (version_compare($version, $dep['min'], '>=')) {
                $fail = true;
            }
        }
        if (isset($dep['max'])) {
            if (version_compare($version, $dep['max'], '<=')) {
                $fail = true;
            }
        }
        // we re-use this variable, preserve the original value
        $saverequired = $required;
        if ($required) {
            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
                return $this->raiseError($depname . $extra . ' is required by installed package' .
                    ' "%s"');
            } else {
                return $this->raiseError('warning: ' . $depname . $extra .
                    ' is required by installed package "%s"');
            }
        } else {
            return $this->warning($depname . $extra . ' can be optionally used by installed package' .
                    ' "%s"');
        }
        return true;
    }

    /**
     * validate a downloaded package against installed packages
     * 
     * As of PEAR 1.4.3, this will only validate
     *
     * @param array|PEAR_Downloader_Package|PEAR_PackageFile_v1|PEAR_PackageFile_v2
     *              $pkg package identifier (either
     *                   array('package' => blah, 'channel' => blah) or an array with
     *                   index 'info' referencing an object)
     * @param PEAR_Downloader $dl
     * @param array $params full list of packages to install
     * @return true|PEAR_Error
     */
    function validatePackage($pkg, &$dl, $params = array())
    {
        if (is_array($pkg) && isset($pkg['info'])) {
            $deps = $this->_dependencydb->getDependentPackageDependencies($pkg['info']);
        } else {
            $deps = $this->_dependencydb->getDependentPackageDependencies($pkg);
        }
        $fail = false;
        if ($deps) {
            if (!class_exists('PEAR_Downloader_Package')) {
                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Downloader/Package.php';
            }
            $dp = &new PEAR_Downloader_Package($dl);
            if (is_object($pkg)) {
                $dp->setPackageFile($pkg);
            } else {
                $dp->setDownloadURL($pkg);
            }
            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
            foreach ($deps as $channel => $info) {
                foreach ($info as $package => $ds) {
                    foreach ($params as $packd) {
                        if (strtolower($packd->getPackage()) == strtolower($package) &&
                              $packd->getChannel() == $channel) {
                            $dl->log(3, 'skipping installed package check of "' .
                                        $this->_registry->parsedPackageNameToString(
                                            array('channel' => $channel, 'package' => $package),
                                            true) .
                                        '", version "' . $packd->getVersion() . '" will be ' .
                                        'downloaded and installed');
                            continue 2; // jump to next package
                        }
                    }
                    foreach ($ds as $d) {
                        $checker = &new PEAR_Dependency2($this->_config, $this->_options,
                            array('channel' => $channel, 'package' => $package), $this->_state);
                        $dep = $d['dep'];
                        $required = $d['type'] == 'required';
                        $ret = $checker->_validatePackageDownload($dep, $required, array(&$dp));
                        if (is_array($ret)) {
                            $dl->log(0, $ret[0]);
                        } elseif (PEAR::isError($ret)) {
                            $dl->log(0, $ret->getMessage());
                            $fail = true;
                        }
                    }
                }
            }
            PEAR::popErrorHandling();
        }
        if ($fail) {
            return $this->raiseError(
                '%s cannot be installed, conflicts with installed packages');
        }
        return true;
    }

    /**
     * validate a package.xml 1.0 dependency
     */
    function validateDependency1($dep, $params = array())
    {
        if (!isset($dep['optional'])) {
            $dep['optional'] = 'no';
        }
        list($newdep, $type) = $this->normalizeDep($dep);
        if (!$newdep) {
            return $this->raiseError("Invalid Dependency");
        }
        if (method_exists($this, "validate{$type}Dependency")) {
            return $this->{"validate{$type}Dependency"}($newdep, $dep['optional'] == 'no',
                $params, true);
        }
    }

    /**
     * Convert a 1.0 dep into a 2.0 dep
     */
    function normalizeDep($dep)
    {
        $types = array(
            'pkg' => 'Package',
            'ext' => 'Extension',
            'os' => 'Os',
            'php' => 'Php'
        );
        if (isset($types[$dep['type']])) {
            $type = $types[$dep['type']];
        } else {
            return array(false, false);
        }
        $newdep = array();
        switch ($type) {
            case 'Package' :
                $newdep['channel'] = 'pear.php.net';
            case 'Extension' :
            case 'Os' :
                $newdep['name'] = $dep['name'];
            break;
        }
        $dep['rel'] = PEAR_Dependency2::signOperator($dep['rel']);
        switch ($dep['rel']) {
            case 'has' :
                return array($newdep, $type);
            break;
            case 'not' :
                $newdep['conflicts'] = true;
            break;
            case '>=' :
            case '>' :
                $newdep['min'] = $dep['version'];
                if ($dep['rel'] == '>') {
                    $newdep['exclude'] = $dep['version'];
                }
            break;
            case '<=' :
            case '<' :
                $newdep['max'] = $dep['version'];
                if ($dep['rel'] == '<') {
                    $newdep['exclude'] = $dep['version'];
                }
            break;
            case 'ne' :
            case '!=' :
                $newdep['min'] = '0';
                $newdep['max'] = '100000';
                $newdep['exclude'] = $dep['version'];
            break;
            case '==' :
                $newdep['min'] = $dep['version'];
                $newdep['max'] = $dep['version'];
            break;
        }
        if ($type == 'Php') {
            if (!isset($newdep['min'])) {
                $newdep['min'] = '4.2.0';
            }
            if (!isset($newdep['max'])) {
                $newdep['max'] = '6.0.0';
            }
        }
        return array($newdep, $type);
    }

    /**
     * Converts text comparing operators to them sign equivalents
     *
     * Example: 'ge' to '>='
     *
     * @access public
     * @param  string Operator
     * @return string Sign equivalent
     */
    function signOperator($operator)
    {
        switch($operator) {
            case 'lt': return '<';
            case 'le': return '<=';
            case 'gt': return '>';
            case 'ge': return '>=';
            case 'eq': return '==';
            case 'ne': return '!=';
            default:
                return $operator;
        }
    }

    function raiseError($msg)
    {
        if (isset($this->_options['ignore-errors'])) {
            return $this->warning($msg);
        }
        return PEAR::raiseError(sprintf($msg, $this->_registry->parsedPackageNameToString(
            $this->_currentPackage, true)));
    }

    function warning($msg)
    {
        return array(sprintf($msg, $this->_registry->parsedPackageNameToString(
            $this->_currentPackage, true)));
    }
}
?><?php
/**
 * PEAR_DependencyDB, advanced installed packages dependency database
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Tomas V. V. Cox <cox@idecnet.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: DependencyDB.php,v 1.37 2008/01/03 20:26:35 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * Needed for error handling
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Config.php';

$GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'] = array();
/**
 * Track dependency relationships between installed packages
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @author     Tomas V.V.Cox <cox@idec.net.com>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_DependencyDB
{
    // {{{ properties

    /**
     * This is initialized by {@link setConfig()}
     * @var PEAR_Config
     * @access private
     */
    var $_config;
    /**
     * This is initialized by {@link setConfig()}
     * @var PEAR_Registry
     * @access private
     */
    var $_registry;
    /**
     * Filename of the dependency DB (usually .depdb)
     * @var string
     * @access private
     */
    var $_depdb = false;
    /**
     * File name of the lockfile (usually .depdblock)
     * @var string
     * @access private
     */
    var $_lockfile = false;
    /**
     * Open file resource for locking the lockfile
     * @var resource|false
     * @access private
     */
    var $_lockFp = false;
    /**
     * API version of this class, used to validate a file on-disk
     * @var string
     * @access private
     */
    var $_version = '1.0';
    /**
     * Cached dependency database file
     * @var array|null
     * @access private
     */
    var $_cache;

    // }}}
    // {{{ & singleton()

    /**
     * Get a raw dependency database.  Calls setConfig() and assertDepsDB()
     * @param PEAR_Config
     * @param string|false full path to the dependency database, or false to use default
     * @return PEAR_DependencyDB|PEAR_Error
     * @static
     */
    function &singleton(&$config, $depdb = false)
    {
        if (!isset($GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE']
              [$config->get('php_dir', null, 'pear.php.net')])) {
            $a = new PEAR_DependencyDB;
            $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE']
              [$config->get('php_dir', null, 'pear.php.net')] = &$a;
            $a->setConfig($config, $depdb);
            if (PEAR::isError($e = $a->assertDepsDB())) {
                return $e;
            }
        }
        return $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE']
              [$config->get('php_dir', null, 'pear.php.net')];
    }

    /**
     * Set up the registry/location of dependency DB
     * @param PEAR_Config|false
     * @param string|false full path to the dependency database, or false to use default
     */
    function setConfig(&$config, $depdb = false)
    {
        if (!$config) {
            $this->_config = &PEAR_Config::singleton();
        } else {
            $this->_config = &$config;
        }
        $this->_registry = &$this->_config->getRegistry();
        if (!$depdb) {
            $this->_depdb = $this->_config->get('php_dir', null, 'pear.php.net') .
                DIRECTORY_SEPARATOR . '.depdb';
        } else {
            $this->_depdb = $depdb;
        }
        $this->_lockfile = dirname($this->_depdb) . DIRECTORY_SEPARATOR . '.depdblock';
    }
    // }}}

    function hasWriteAccess()
    {
        if (!file_exists($this->_depdb)) {
            $dir = $this->_depdb;
            while ($dir && $dir != '.') {
                $dir = dirname($dir); // cd ..
                if ($dir != '.' && file_exists($dir)) {
                    if (is_writeable($dir)) {
                        return true;
                    } else {
                        return false;
                    }
                }
            }
            return false;
        }
        return is_writeable($this->_depdb);
    }

    // {{{ assertDepsDB()

    /**
     * Create the dependency database, if it doesn't exist.  Error if the database is
     * newer than the code reading it.
     * @return void|PEAR_Error
     */
    function assertDepsDB()
    {
        if (!is_file($this->_depdb)) {
            $this->rebuildDB();
        } else {
            $depdb = $this->_getDepDB();
            // Datatype format has been changed, rebuild the Deps DB
            if ($depdb['_version'] < $this->_version) {
                $this->rebuildDB();
            }
            if ($depdb['_version']{0} > $this->_version{0}) {
                return PEAR::raiseError('Dependency database is version ' .
                    $depdb['_version'] . ', and we are version ' .
                    $this->_version . ', cannot continue');
            }
        }
    }

    /**
     * Get a list of installed packages that depend on this package
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
     * @return array|false
     */
    function getDependentPackages(&$pkg)
    {
        $data = $this->_getDepDB();
        if (is_object($pkg)) {
            $channel = strtolower($pkg->getChannel());
            $package = strtolower($pkg->getPackage());
        } else {
            $channel = strtolower($pkg['channel']);
            $package = strtolower($pkg['package']);
        }
        if (isset($data['packages'][$channel][$package])) {
            return $data['packages'][$channel][$package];
        }
        return false;
    }

    /**
     * Get a list of the actual dependencies of installed packages that depend on
     * a package.
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
     * @return array|false
     */
    function getDependentPackageDependencies(&$pkg)
    {
        $data = $this->_getDepDB();
        if (is_object($pkg)) {
            $channel = strtolower($pkg->getChannel());
            $package = strtolower($pkg->getPackage());
        } else {
            $channel = strtolower($pkg['channel']);
            $package = strtolower($pkg['package']);
        }
        $depend = $this->getDependentPackages($pkg);
        if (!$depend) {
            return false;
        }
        $dependencies = array();
        foreach ($depend as $info) {
            $temp = $this->getDependencies($info);
            foreach ($temp as $dep) {
                if (strtolower($dep['dep']['channel']) == strtolower($channel) &&
                      strtolower($dep['dep']['name']) == strtolower($package)) {
                    if (!isset($dependencies[$info['channel']])) {
                        $dependencies[$info['channel']] = array();
                    }
                    if (!isset($dependencies[$info['channel']][$info['package']])) {
                        $dependencies[$info['channel']][$info['package']] = array();
                    }
                    $dependencies[$info['channel']][$info['package']][] = $dep;
                }
            }
        }
        return $dependencies;
    }

    /**
     * Get a list of dependencies of this installed package
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
     * @return array|false
     */
    function getDependencies(&$pkg)
    {
        if (is_object($pkg)) {
            $channel = strtolower($pkg->getChannel());
            $package = strtolower($pkg->getPackage());
        } else {
            $channel = strtolower($pkg['channel']);
            $package = strtolower($pkg['package']);
        }
        $data = $this->_getDepDB();
        if (isset($data['dependencies'][$channel][$package])) {
            return $data['dependencies'][$channel][$package];
        }
        return false;
    }

    /**
     * Determine whether $parent depends on $child, near or deep
     * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2
     * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2
     */
    function dependsOn($parent, $child)
    {
        $c = array();
        $this->_getDepDB();
        return $this->_dependsOn($parent, $child, $c);
    }
    
    function _dependsOn($parent, $child, &$checked)
    {
        if (is_object($parent)) {
            $channel = strtolower($parent->getChannel());
            $package = strtolower($parent->getPackage());
        } else {
            $channel = strtolower($parent['channel']);
            $package = strtolower($parent['package']);
        }
        if (is_object($child)) {
            $depchannel = strtolower($child->getChannel());
            $deppackage = strtolower($child->getPackage());
        } else {
            $depchannel = strtolower($child['channel']);
            $deppackage = strtolower($child['package']);
        }
        if (isset($checked[$channel][$package][$depchannel][$deppackage])) {
            return false; // avoid endless recursion
        }
        $checked[$channel][$package][$depchannel][$deppackage] = true;
        if (!isset($this->_cache['dependencies'][$channel][$package])) {
            return false;
        }
        foreach ($this->_cache['dependencies'][$channel][$package] as $info) {
            if (isset($info['dep']['uri'])) {
                if (is_object($child)) {
                    if ($info['dep']['uri'] == $child->getURI()) {
                        return true;
                    }
                } elseif (isset($child['uri'])) {
                    if ($info['dep']['uri'] == $child['uri']) {
                        return true;
                    }
                }
                return false;
            }
            if (strtolower($info['dep']['channel']) == strtolower($depchannel) &&
                  strtolower($info['dep']['name']) == strtolower($deppackage)) {
                return true;
            }
        }
        foreach ($this->_cache['dependencies'][$channel][$package] as $info) {
            if (isset($info['dep']['uri'])) {
                if ($this->_dependsOn(array(
                        'uri' => $info['dep']['uri'],
                        'package' => $info['dep']['name']), $child, $checked)) {
                    return true;
                }
            } else {
                if ($this->_dependsOn(array(
                        'channel' => $info['dep']['channel'],
                        'package' => $info['dep']['name']), $child, $checked)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Register dependencies of a package that is being installed or upgraded
     * @param PEAR_PackageFile_v2|PEAR_PackageFile_v2
     */
    function installPackage(&$package)
    {
        $data = $this->_getDepDB();
        unset($this->_cache);
        $this->_setPackageDeps($data, $package);
        $this->_writeDepDB($data);
    }

    /**
     * Remove dependencies of a package that is being uninstalled, or upgraded.
     *
     * Upgraded packages first uninstall, then install
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array If an array, then it must have
     *        indices 'channel' and 'package'
     */
    function uninstallPackage(&$pkg)
    {
        $data = $this->_getDepDB();
        unset($this->_cache);
        if (is_object($pkg)) {
            $channel = strtolower($pkg->getChannel());
            $package = strtolower($pkg->getPackage());
        } else {
            $channel = strtolower($pkg['channel']);
            $package = strtolower($pkg['package']);
        }
        if (!isset($data['dependencies'][$channel][$package])) {
            return true;
        }
        foreach ($data['dependencies'][$channel][$package] as $dep) {
            $found = false;
            if (isset($dep['dep']['uri'])) {
                $depchannel = '__uri';
            } else {
                $depchannel = strtolower($dep['dep']['channel']);
            }
            if (isset($data['packages'][$depchannel][strtolower($dep['dep']['name'])])) {
                foreach ($data['packages'][$depchannel][strtolower($dep['dep']['name'])] as
                      $i => $info) {
                    if ($info['channel'] == $channel &&
                          $info['package'] == $package) {
                        $found = true;
                        break;
                    }
                }
            }
            if ($found) {
                unset($data['packages'][$depchannel][strtolower($dep['dep']['name'])][$i]);
                if (!count($data['packages'][$depchannel][strtolower($dep['dep']['name'])])) {
                    unset($data['packages'][$depchannel][strtolower($dep['dep']['name'])]);
                    if (!count($data['packages'][$depchannel])) {
                        unset($data['packages'][$depchannel]);
                    }
                } else {
                    $data['packages'][$depchannel][strtolower($dep['dep']['name'])] =
                        array_values(
                            $data['packages'][$depchannel][strtolower($dep['dep']['name'])]);
                }
            }
        }
        unset($data['dependencies'][$channel][$package]);
        if (!count($data['dependencies'][$channel])) {
            unset($data['dependencies'][$channel]);
        }
        if (!count($data['dependencies'])) {
            unset($data['dependencies']);
        }
        if (!count($data['packages'])) {
            unset($data['packages']);
        }
        $this->_writeDepDB($data);
    }

    /**
     * Rebuild the dependency DB by reading registry entries.
     * @return true|PEAR_Error
     */
    function rebuildDB()
    {
        $depdb = array('_version' => $this->_version);
        if (!$this->hasWriteAccess()) {
            // allow startup for read-only with older Registry
            return $depdb;
        }
        $packages = $this->_registry->listAllPackages();
        if (PEAR::isError($packages)) {
            return $packages;
        }
        foreach ($packages as $channel => $ps) {
            foreach ($ps as $package) {
                $package = $this->_registry->getPackage($package, $channel);
                if (PEAR::isError($package)) {
                    return $package;
                }
                $this->_setPackageDeps($depdb, $package);
            }
        }
        $error = $this->_writeDepDB($depdb);
        if (PEAR::isError($error)) {
            return $error;
        }
        $this->_cache = $depdb;
        return true;
    }

    /**
     * Register usage of the dependency DB to prevent race conditions
     * @param int one of the LOCK_* constants
     * @return true|PEAR_Error
     * @access private
     */
    function _lock($mode = LOCK_EX)
    {
        if (!eregi('Windows 9', php_uname())) {
            if ($mode != LOCK_UN && is_resource($this->_lockFp)) {
                // XXX does not check type of lock (LOCK_SH/LOCK_EX)
                return true;
            }
            $open_mode = 'w';
            // XXX People reported problems with LOCK_SH and 'w'
            if ($mode === LOCK_SH) {
                if (!file_exists($this->_lockfile)) {
                    touch($this->_lockfile);
                } elseif (!is_file($this->_lockfile)) {
                    return PEAR::raiseError('could not create Dependency lock file, ' .
                        'it exists and is not a regular file');
                }
                $open_mode = 'r';
            }

            if (!is_resource($this->_lockFp)) {
                $this->_lockFp = @fopen($this->_lockfile, $open_mode);
            }
            if (!is_resource($this->_lockFp)) {
                return PEAR::raiseError("could not create Dependency lock file" .
                                         (isset($php_errormsg) ? ": " . $php_errormsg : ""));
            }
            if (!(int)flock($this->_lockFp, $mode)) {
                switch ($mode) {
                    case LOCK_SH: $str = 'shared';    break;
                    case LOCK_EX: $str = 'exclusive'; break;
                    case LOCK_UN: $str = 'unlock';    break;
                    default:      $str = 'unknown';   break;
                }
                return PEAR::raiseError("could not acquire $str lock ($this->_lockfile)");
            }
        }
        return true;
    }

    /**
     * Release usage of dependency DB
     * @return true|PEAR_Error
     * @access private
     */
    function _unlock()
    {
        $ret = $this->_lock(LOCK_UN);
        if (is_resource($this->_lockFp)) {
            fclose($this->_lockFp);
        }
        $this->_lockFp = null;
        return $ret;
    }

    /**
     * Load the dependency database from disk, or return the cache
     * @return array|PEAR_Error
     */
    function _getDepDB()
    {
        if (!$this->hasWriteAccess()) {
            return array('_version' => $this->_version);
        }
        if (isset($this->_cache)) {
            return $this->_cache;
        }
        if (!$fp = fopen($this->_depdb, 'r')) {
            $err = PEAR::raiseError("Could not open dependencies file `".$this->_depdb."'");
            return $err;
        }
        $rt = get_magic_quotes_runtime();
        set_magic_quotes_runtime(0);
        clearstatcache();
        fclose($fp);
        $data = unserialize(file_get_contents($this->_depdb));
        set_magic_quotes_runtime($rt);
        $this->_cache = $data;
        return $data;
    }

    /**
     * Write out the dependency database to disk
     * @param array the database
     * @return true|PEAR_Error
     * @access private
     */
    function _writeDepDB(&$deps)
    {
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
            return $e;
        }
        if (!$fp = fopen($this->_depdb, 'wb')) {
            $this->_unlock();
            return PEAR::raiseError("Could not open dependencies file `".$this->_depdb."' for writing");
        }
        $rt = get_magic_quotes_runtime();
        set_magic_quotes_runtime(0);
        fwrite($fp, serialize($deps));
        set_magic_quotes_runtime($rt);
        fclose($fp);
        $this->_unlock();
        $this->_cache = $deps;
        return true;
    }

    /**
     * Register all dependencies from a package in the dependencies database, in essence
     * "installing" the package's dependency information
     * @param array the database
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @access private
     */
    function _setPackageDeps(&$data, &$pkg)
    {
        $pkg->setConfig($this->_config);
        if ($pkg->getPackagexmlVersion() == '1.0') {
            $gen = &$pkg->getDefaultGenerator();
            $deps = $gen->dependenciesToV2();
        } else {
            $deps = $pkg->getDeps(true);
        }
        if (!$deps) {
            return;
        }
        if (!is_array($data)) {
            $data = array();
        }
        if (!isset($data['dependencies'])) {
            $data['dependencies'] = array();
        }
        if (!isset($data['dependencies'][strtolower($pkg->getChannel())])) {
            $data['dependencies'][strtolower($pkg->getChannel())] = array();
        }
        $data['dependencies'][strtolower($pkg->getChannel())][strtolower($pkg->getPackage())]
            = array();
        if (isset($deps['required']['package'])) {
            if (!isset($deps['required']['package'][0])) {
                $deps['required']['package'] = array($deps['required']['package']);
            }
            foreach ($deps['required']['package'] as $dep) {
                $this->_registerDep($data, $pkg, $dep, 'required');
            }
        }
        if (isset($deps['optional']['package'])) {
            if (!isset($deps['optional']['package'][0])) {
                $deps['optional']['package'] = array($deps['optional']['package']);
            }
            foreach ($deps['optional']['package'] as $dep) {
                $this->_registerDep($data, $pkg, $dep, 'optional');
            }
        }
        if (isset($deps['required']['subpackage'])) {
            if (!isset($deps['required']['subpackage'][0])) {
                $deps['required']['subpackage'] = array($deps['required']['subpackage']);
            }
            foreach ($deps['required']['subpackage'] as $dep) {
                $this->_registerDep($data, $pkg, $dep, 'required');
            }
        }
        if (isset($deps['optional']['subpackage'])) {
            if (!isset($deps['optional']['subpackage'][0])) {
                $deps['optional']['subpackage'] = array($deps['optional']['subpackage']);
            }
            foreach ($deps['optional']['subpackage'] as $dep) {
                $this->_registerDep($data, $pkg, $dep, 'optional');
            }
        }
        if (isset($deps['group'])) {
            if (!isset($deps['group'][0])) {
                $deps['group'] = array($deps['group']);
            }
            foreach ($deps['group'] as $group) {
                if (isset($group['package'])) {
                    if (!isset($group['package'][0])) {
                        $group['package'] = array($group['package']);
                    }
                    foreach ($group['package'] as $dep) {
                        $this->_registerDep($data, $pkg, $dep, 'optional',
                            $group['attribs']['name']);
                    }
                }
                if (isset($group['subpackage'])) {
                    if (!isset($group['subpackage'][0])) {
                        $group['subpackage'] = array($group['subpackage']);
                    }
                    foreach ($group['subpackage'] as $dep) {
                        $this->_registerDep($data, $pkg, $dep, 'optional',
                            $group['attribs']['name']);
                    }
                }
            }
        }
        if ($data['dependencies'][strtolower($pkg->getChannel())]
              [strtolower($pkg->getPackage())] == array()) {
            unset($data['dependencies'][strtolower($pkg->getChannel())]
              [strtolower($pkg->getPackage())]);
            if (!count($data['dependencies'][strtolower($pkg->getChannel())])) {
                unset($data['dependencies'][strtolower($pkg->getChannel())]);
            }
        }
    }

    /**
     * @param array the database
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @param array the specific dependency
     * @param required|optional whether this is a required or an optional dep
     * @param string|false dependency group this dependency is from, or false for ordinary dep
     */
    function _registerDep(&$data, &$pkg, $dep, $type, $group = false)
    {
        $info = array(
            'dep' => $dep,
            'type' => $type,
            'group' => $group);

        if (isset($dep['channel'])) {
            $depchannel = $dep['channel'];
        } else {
            $depchannel = '__uri';
        }
        if (!isset($data['dependencies'])) {
            $data['dependencies'] = array();
        }
        if (!isset($data['dependencies'][strtolower($pkg->getChannel())])) {
            $data['dependencies'][strtolower($pkg->getChannel())] = array();
        }
        if (!isset($data['dependencies'][strtolower($pkg->getChannel())][strtolower($pkg->getPackage())])) {
            $data['dependencies'][strtolower($pkg->getChannel())][strtolower($pkg->getPackage())] = array();
        }
        $data['dependencies'][strtolower($pkg->getChannel())][strtolower($pkg->getPackage())][]
            = $info;
        if (isset($data['packages'][strtolower($depchannel)][strtolower($dep['name'])])) {
            $found = false;
            foreach ($data['packages'][strtolower($depchannel)][strtolower($dep['name'])]
                  as $i => $p) {
                if ($p['channel'] == strtolower($pkg->getChannel()) &&
                      $p['package'] == strtolower($pkg->getPackage())) {
                    $found = true;
                    break;
                }
            }
            if (!$found) {
                $data['packages'][strtolower($depchannel)][strtolower($dep['name'])][]
                    = array('channel' => strtolower($pkg->getChannel()),
                            'package' => strtolower($pkg->getPackage()));
            }
        } else {
            if (!isset($data['packages'])) {
                $data['packages'] = array();
            }
            if (!isset($data['packages'][strtolower($depchannel)])) {
                $data['packages'][strtolower($depchannel)] = array();
            }
            if (!isset($data['packages'][strtolower($depchannel)][strtolower($dep['name'])])) {
                $data['packages'][strtolower($depchannel)][strtolower($dep['name'])] = array();
            }
            $data['packages'][strtolower($depchannel)][strtolower($dep['name'])][]
                = array('channel' => strtolower($pkg->getChannel()),
                        'package' => strtolower($pkg->getPackage()));
        }
    }
}
?><?php
/**
 * PEAR_Downloader, the PEAR Installer's download utility class
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V. V. Cox <cox@idecnet.com>
 * @author     Martin Jansen <mj@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Downloader.php,v 1.138 2008/04/11 01:16:40 dufuz Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.3.0
 */

/**
 * Needed for constants, extending
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Common.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 download anything from the internet (PEAR Packages,
 * static URLs, xml files)
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V. V. Cox <cox@idecnet.com>
 * @author     Martin Jansen <mj@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.3.0
 */
class PEAR_Downloader extends PEAR_Common
{
    /**
     * @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
     *  - nocompress    : download uncompressed tarballs
     * @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();

    /**
     * @var boolean
     * @access private
     */
    var $_internalDownload = false;

    /**
     * Temporary variable used in sorting packages by dependency in {@link sortPkgDeps()}
     * @var array
     * @access private
     */
    var $_packageSortTree;

    /**
     * Temporary directory, or configuration value where downloads will occur
     * @var string
     */
    var $_downloadDir;
    // {{{ PEAR_Downloader()

    /**
     * @param PEAR_Frontend_*
     * @param array
     * @param PEAR_Config
     */
    function PEAR_Downloader(&$ui, $options, &$config)
    {
        parent::PEAR_Common();
        $this->_options = $options;
        $this->config = &$config;
        $this->_preferredState = $this->config->get('preferred_state');
        $this->ui = &$ui;
        if (!$this->_preferredState) {
            // don't inadvertantly use a non-set preferred_state
            $this->_preferredState = null;
        }

        if (isset($this->_options['installroot'])) {
            $this->config->setInstallRoot($this->_options['installroot']);
        }
        $this->_registry = &$config->getRegistry();
        $this->_remote = &$config->getRemote();

        if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
            $this->_installed = $this->_registry->listAllPackages();
            foreach ($this->_installed as $key => $unused) {
                if (!count($unused)) {
                    continue;
                }
                $strtolower = create_function('$a','return strtolower($a);');
                array_walk($this->_installed[$key], $strtolower);
            }
        }
    }

    /**
     * Attempt to discover a channel's remote capabilities from
     * its server name
     * @param string
     * @return boolean
     */
    function discover($channel)
    {
        $this->log(1, 'Attempting to discover channel "' . $channel . '"...');
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        $callback = $this->ui ? array(&$this, '_downloadCallback') : null;
        if (!class_exists('System')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
        }
        $a = $this->downloadHttp('http://' . $channel . '/channel.xml', $this->ui,
            System::mktemp(array('-d')), $callback, false);
        PEAR::popErrorHandling();
        if (PEAR::isError($a)) {
            return false;
        }
        list($a, $lastmodified) = $a;
        if (!class_exists('PEAR_ChannelFile')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
        }
        $b = new PEAR_ChannelFile;
        if ($b->fromXmlFile($a)) {
            unlink($a);
            if ($this->config->get('auto_discover')) {
                $this->_registry->addChannel($b, $lastmodified);
                $alias = $b->getName();
                if ($b->getName() == $this->_registry->channelName($b->getAlias())) {
                    $alias = $b->getAlias();
                }
                $this->log(1, 'Auto-discovered channel "' . $channel .
                    '", alias "' . $alias . '", adding to registry');
            }
            return true;
        }
        unlink($a);
        return false;
    }

    /**
     * For simpler unit-testing
     * @param PEAR_Downloader
     * @return PEAR_Downloader_Package
     */
    function &newDownloaderPackage(&$t)
    {
        if (!class_exists('PEAR_Downloader_Package')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Downloader/Package.php';
        }
        $a = &new PEAR_Downloader_Package($t);
        return $a;
    }

    /**
     * For simpler unit-testing
     * @param PEAR_Config
     * @param array
     * @param array
     * @param int
     */
    function &getDependency2Object(&$c, $i, $p, $s)
    {
        if (!class_exists('PEAR_Dependency2')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Dependency2.php';
        }
        $z = &new PEAR_Dependency2($c, $i, $p, $s);
        return $z;
    }

    function &download($params)
    {
        if (!count($params)) {
            $a = array();
            return $a;
        }
        if (!isset($this->_registry)) {
            $this->_registry = &$this->config->getRegistry();
        }
        if (!isset($this->_remote)) {
            $this->_remote = &$this->config->getRemote();
        }
        $channelschecked = array();
        // convert all parameters into PEAR_Downloader_Package objects
        foreach ($params as $i => $param) {
            $params[$i] = &$this->newDownloaderPackage($this);
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            $err = $params[$i]->initialize($param);
            PEAR::staticPopErrorHandling();
            if (!$err) {
                // skip parameters that were missed by preferred_state
                continue;
            }
            if (PEAR::isError($err)) {
                if (!isset($this->_options['soft'])) {
                    $this->log(0, $err->getMessage());
                }
                $params[$i] = false;
                if (is_object($param)) {
                    $param = $param->getChannel() . '/' . $param->getPackage();
                }
                $this->pushError('Package "' . $param . '" is not valid',
                    PEAR_INSTALLER_SKIPPED);
            } else {
                do {
                    if ($params[$i] && $params[$i]->getType() == 'local') {
                        // bug #7090
                        // skip channel.xml check for local packages
                        break;
                    }
                    if ($params[$i] && !isset($channelschecked[$params[$i]->getChannel()]) &&
                          !isset($this->_options['offline'])) {
                        $channelschecked[$params[$i]->getChannel()] = true;
                        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
                        if (!class_exists('System')) {
                            require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
                        }
                        $curchannel = &$this->_registry->getChannel($params[$i]->getChannel());
                        if (PEAR::isError($curchannel)) {
                            PEAR::staticPopErrorHandling();
                            return $this->raiseError($curchannel);
                        }
                        if (PEAR::isError($dir = $this->getDownloadDir())) {
                            PEAR::staticPopErrorHandling();
                            break;
                        }
                        $mirror = $this->config->get('preferred_mirror', null,
                                                     $params[$i]->getChannel());
                        $a = $this->downloadHttp('http://' . $mirror .
                            '/channel.xml', $this->ui, $dir, null, $curchannel->lastModified());

                        PEAR::staticPopErrorHandling();
                        if (PEAR::isError($a) || !$a) {
                            break;
                        }
                        $this->log(0, 'WARNING: channel "' . $params[$i]->getChannel() . '" has ' .
                            'updated its protocols, use "channel-update ' . $params[$i]->getChannel() .
                            '" to update');
                    }
                } while (false);
                if ($params[$i] && !isset($this->_options['downloadonly'])) {
                    if (isset($this->_options['packagingroot'])) {
                        $checkdir = $this->_prependPath(
                            $this->config->get('php_dir', null, $params[$i]->getChannel()),
                            $this->_options['packagingroot']);
                    } else {
                        $checkdir = $this->config->get('php_dir',
                            null, $params[$i]->getChannel());
                    }
                    while ($checkdir && $checkdir != '/' && !file_exists($checkdir)) {
                        $checkdir = dirname($checkdir);
                    }
                    if ($checkdir == '.') {
                        $checkdir = '/';
                    }
                    if (!is_writeable($checkdir)) {
                        return PEAR::raiseError('Cannot install, php_dir for channel "' .
                            $params[$i]->getChannel() . '" is not writeable by the current user');
                    }
                }
            }
        }
        unset($channelschecked);
        PEAR_Downloader_Package::removeDuplicates($params);
        if (!count($params)) {
            $a = array();
            return $a;
        }
        if (!isset($this->_options['nodeps']) && !isset($this->_options['offline'])) {
            $reverify = true;
            while ($reverify) {
                $reverify = false;
                foreach ($params as $i => $param) {
                    //PHP Bug 40768 / PEAR Bug #10944
                    //Nested foreaches fail in PHP 5.2.1
                    key($params);
                    $ret = $params[$i]->detectDependencies($params);
                    if (PEAR::isError($ret)) {
                        $reverify = true;
                        $params[$i] = false;
                        PEAR_Downloader_Package::removeDuplicates($params);
                        if (!isset($this->_options['soft'])) {
                            $this->log(0, $ret->getMessage());
                        }
                        continue 2;
                    }
                }
            }
        }
        if (isset($this->_options['offline'])) {
            $this->log(3, 'Skipping dependency download check, --offline specified');
        }
        if (!count($params)) {
            $a = array();
            return $a;
        }
        while (PEAR_Downloader_Package::mergeDependencies($params));
        PEAR_Downloader_Package::removeDuplicates($params, true);
        $errorparams = array();
        if (PEAR_Downloader_Package::detectStupidDuplicates($params, $errorparams)) {
            if (count($errorparams)) {
                foreach ($errorparams as $param) {
                    $name = $this->_registry->parsedPackageNameToString($param->getParsedPackage());
                    $this->pushError('Duplicate package ' . $name . ' found', PEAR_INSTALLER_FAILED);
                }
                $a = array();
                return $a;
            }
        }
        PEAR_Downloader_Package::removeInstalled($params);
        if (!count($params)) {
            $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED);
            $a = array();
            return $a;
        }
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        $err = $this->analyzeDependencies($params);
        PEAR::popErrorHandling();
        if (!count($params)) {
            $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED);
            $a = array();
            return $a;
        }
        $ret = array();
        $newparams = array();
        if (isset($this->_options['pretend'])) {
            return $params;
        }
        $somefailed = false;
        foreach ($params as $i => $package) {
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            $pf = &$params[$i]->download();
            PEAR::staticPopErrorHandling();
            if (PEAR::isError($pf)) {
                if (!isset($this->_options['soft'])) {
                    $this->log(1, $pf->getMessage());
                    $this->log(0, 'Error: cannot download "' .
                        $this->_registry->parsedPackageNameToString($package->getParsedPackage(),
                            true) .
                        '"');
                }
                $somefailed = true;
                continue;
            }
            $newparams[] = &$params[$i];
            $ret[] = array('file' => $pf->getArchiveFile(),
                                   'info' => &$pf,
                                   'pkg' => $pf->getPackage());
        }
        if ($somefailed) {
            // remove params that did not download successfully
            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
            $err = $this->analyzeDependencies($newparams, true);
            PEAR::popErrorHandling();
            if (!count($newparams)) {
                $this->pushError('Download failed', PEAR_INSTALLER_FAILED);
                $a = array();
                return $a;
            }
        }
        $this->_downloadedPackages = $ret;
        return $newparams;
    }

    /**
     * @param array all packages to be installed
     */
    function analyzeDependencies(&$params, $force = false)
    {
        $hasfailed = $failed = false;
        if (isset($this->_options['downloadonly'])) {
            return;
        }
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        $redo = true;
        $reset = false;
        while ($redo) {
            $redo = false;
            foreach ($params as $i => $param) {
                $deps = $param->getDeps();
                if (!$deps) {
                    $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(),
                        $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING);
                    if ($param->getType() == 'xmlrpc') {
                        $send = $param->getDownloadURL();
                    } else {
                        $send = $param->getPackageFile();
                    }
                    $installcheck = $depchecker->validatePackage($send, $this, $params);
                    if (PEAR::isError($installcheck)) {
                        if (!isset($this->_options['soft'])) {
                            $this->log(0, $installcheck->getMessage());
                        }
                        $hasfailed = true;
                        $params[$i] = false;
                        $reset = true;
                        $redo = true;
                        $failed = false;
                        PEAR_Downloader_Package::removeDuplicates($params);
                        continue 2;
                    }
                    continue;
                }
                if (!$reset && $param->alreadyValidated() && !$force) {
                    continue;
                }
                if (count($deps)) {
                    $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(),
                        $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING);
                    if ($param->getType() == 'xmlrpc') {
                        $send = $param->getDownloadURL();
                    } else {
                        $send = $param->getPackageFile();
                    }
                    $installcheck = $depchecker->validatePackage($send, $this, $params);
                    if (PEAR::isError($installcheck)) {
                        if (!isset($this->_options['soft'])) {
                            $this->log(0, $installcheck->getMessage());
                        }
                        $hasfailed = true;
                        $params[$i] = false;
                        $reset = true;
                        $redo = true;
                        $failed = false;
                        PEAR_Downloader_Package::removeDuplicates($params);
                        continue 2;
                    }
                    $failed = false;
                    if (isset($deps['required'])) {
                        foreach ($deps['required'] as $type => $dep) {
                            // note: Dependency2 will never return a PEAR_Error if ignore-errors
                            // is specified, so soft is needed to turn off logging
                            if (!isset($dep[0])) {
                                if (PEAR::isError($e = $depchecker->{"validate{$type}Dependency"}($dep,
                                      true, $params))) {
                                    $failed = true;
                                    if (!isset($this->_options['soft'])) {
                                        $this->log(0, $e->getMessage());
                                    }
                                } elseif (is_array($e) && !$param->alreadyValidated()) {
                                    if (!isset($this->_options['soft'])) {
                                        $this->log(0, $e[0]);
                                    }
                                }
                            } else {
                                foreach ($dep as $d) {
                                    if (PEAR::isError($e =
                                          $depchecker->{"validate{$type}Dependency"}($d,
                                          true, $params))) {
                                        $failed = true;
                                        if (!isset($this->_options['soft'])) {
                                            $this->log(0, $e->getMessage());
                                        }
                                    } elseif (is_array($e) && !$param->alreadyValidated()) {
                                        if (!isset($this->_options['soft'])) {
                                            $this->log(0, $e[0]);
                                        }
                                    }
                                }
                            }
                        }
                        if (isset($deps['optional'])) {
                            foreach ($deps['optional'] as $type => $dep) {
                                if (!isset($dep[0])) {
                                    if (PEAR::isError($e =
                                          $depchecker->{"validate{$type}Dependency"}($dep,
                                          false, $params))) {
                                        $failed = true;
                                        if (!isset($this->_options['soft'])) {
                                            $this->log(0, $e->getMessage());
                                        }
                                    } elseif (is_array($e) && !$param->alreadyValidated()) {
                                        if (!isset($this->_options['soft'])) {
                                            $this->log(0, $e[0]);
                                        }
                                    }
                                } else {
                                    foreach ($dep as $d) {
                                        if (PEAR::isError($e =
                                              $depchecker->{"validate{$type}Dependency"}($d,
                                              false, $params))) {
                                            $failed = true;
                                            if (!isset($this->_options['soft'])) {
                                                $this->log(0, $e->getMessage());
                                            }
                                        } elseif (is_array($e) && !$param->alreadyValidated()) {
                                            if (!isset($this->_options['soft'])) {
                                                $this->log(0, $e[0]);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        $groupname = $param->getGroup();
                        if (isset($deps['group']) && $groupname) {
                            if (!isset($deps['group'][0])) {
                                $deps['group'] = array($deps['group']);
                            }
                            $found = false;
                            foreach ($deps['group'] as $group) {
                                if ($group['attribs']['name'] == $groupname) {
                                    $found = true;
                                    break;
                                }
                            }
                            if ($found) {
                                unset($group['attribs']);
                                foreach ($group as $type => $dep) {
                                    if (!isset($dep[0])) {
                                        if (PEAR::isError($e =
                                              $depchecker->{"validate{$type}Dependency"}($dep,
                                              false, $params))) {
                                            $failed = true;
                                            if (!isset($this->_options['soft'])) {
                                                $this->log(0, $e->getMessage());
                                            }
                                        } elseif (is_array($e) && !$param->alreadyValidated()) {
                                            if (!isset($this->_options['soft'])) {
                                                $this->log(0, $e[0]);
                                            }
                                        }
                                    } else {
                                        foreach ($dep as $d) {
                                            if (PEAR::isError($e =
                                                  $depchecker->{"validate{$type}Dependency"}($d,
                                                  false, $params))) {
                                                $failed = true;
                                                if (!isset($this->_options['soft'])) {
                                                    $this->log(0, $e->getMessage());
                                                }
                                            } elseif (is_array($e) && !$param->alreadyValidated()) {
                                                if (!isset($this->_options['soft'])) {
                                                    $this->log(0, $e[0]);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    } else {
                        foreach ($deps as $dep) {
                            if (PEAR::isError($e = $depchecker->validateDependency1($dep, $params))) {
                                $failed = true;
                                if (!isset($this->_options['soft'])) {
                                    $this->log(0, $e->getMessage());
                                }
                            } elseif (is_array($e) && !$param->alreadyValidated()) {
                                if (!isset($this->_options['soft'])) {
                                    $this->log(0, $e[0]);
                                }
                            }
                        }
                    }
                    $params[$i]->setValidated();
                }
                if ($failed) {
                    $hasfailed = true;
                    $params[$i] = false;
                    $reset = true;
                    $redo = true;
                    $failed = false;
                    PEAR_Downloader_Package::removeDuplicates($params);
                    continue 2;
                }
            }
        }
        PEAR::staticPopErrorHandling();
        if ($hasfailed && (isset($this->_options['ignore-errors']) ||
              isset($this->_options['nodeps']))) {
            // this is probably not needed, but just in case
            if (!isset($this->_options['soft'])) {
                $this->log(0, 'WARNING: dependencies failed');
            }
        }
    }

    /**
     * Retrieve the directory that downloads will happen in
     * @access private
     * @return string
     */
    function getDownloadDir()
    {
        if (isset($this->_downloadDir)) {
            return $this->_downloadDir;
        }
        $downloaddir = $this->config->get('download_dir');
        if (empty($downloaddir) || (is_dir($downloaddir) && !is_writable($downloaddir))) {
            if  (is_dir($downloaddir) && !is_writable($downloaddir)) {
                $this->log(0, 'WARNING: configuration download directory "' . $downloaddir .
                    '" is not writeable.  Change download_dir config variable to ' .
                    'a writeable dir to avoid this warning');
            }
            if (!class_exists('System')) {
                require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
            }
            if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
                return $downloaddir;
            }
            $this->log(3, '+ tmp dir created at ' . $downloaddir);
        }
        if (!is_writable($downloaddir)) {
            if (PEAR::isError(System::mkdir(array('-p', $downloaddir))) ||
                  !is_writable($downloaddir)) {
                return PEAR::raiseError('download directory "' . $downloaddir .
                    '" is not writeable.  Change download_dir config variable to ' .
                    'a writeable dir');
            }
        }
        return $this->_downloadDir = $downloaddir;
    }

    function setDownloadDir($dir)
    {
        if (!@is_writable($dir)) {
            if (PEAR::isError(System::mkdir(array('-p', $dir)))) {
                return PEAR::raiseError('download directory "' . $dir .
                    '" is not writeable.  Change download_dir config variable to ' .
                    'a writeable dir');
            }
        }
        $this->_downloadDir = $dir;
    }

    // }}}
    // {{{ configSet()
    function configSet($key, $value, $layer = 'user', $channel = false)
    {
        $this->config->set($key, $value, $layer, $channel);
        $this->_preferredState = $this->config->get('preferred_state', null, $channel);
        if (!$this->_preferredState) {
            // don't inadvertantly use a non-set preferred_state
            $this->_preferredState = null;
        }
    }

    // }}}
    // {{{ setOptions()
    function setOptions($options)
    {
        $this->_options = $options;
    }

    // }}}
    // {{{ setOptions()
    function getOptions()
    {
        return $this->_options;
    }

    // }}}

    /**
     * For simpler unit-testing
     * @param PEAR_Config
     * @param int
     * @param string
     */
    function &getPackagefileObject(&$c, $d, $t = false)
    {
        if (!class_exists('PEAR_PackageFile')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile.php';
        }
        $a = &new PEAR_PackageFile($c, $d, $t);
        return $a;
    }

    // {{{ _getPackageDownloadUrl()

    /**
     * @param array output of {@link parsePackageName()}
     * @access private
     */
    function _getPackageDownloadUrl($parr)
    {
        $curchannel = $this->config->get('default_channel');
        $this->configSet('default_channel', $parr['channel']);
        // getDownloadURL returns an array.  On error, it only contains information
        // on the latest release as array(version, info).  On success it contains
        // array(version, info, download url string)
        $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state');
        if (!$this->_registry->channelExists($parr['channel'])) {
            do {
                if ($this->config->get('auto_discover')) {
                    if ($this->discover($parr['channel'])) {
                        break;
                    }
                }
                $this->configSet('default_channel', $curchannel);
                return PEAR::raiseError('Unknown remote channel: ' . $remotechannel);
            } while (false);
        }
        $chan = &$this->_registry->getChannel($parr['channel']);
        if (PEAR::isError($chan)) {
            return $chan;
        }
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        $version = $this->_registry->packageInfo($parr['package'], 'version',
            $parr['channel']);
        PEAR::staticPopErrorHandling();
        $base2 = false;
        if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
              (($base2 = $chan->getBaseURL('REST1.3', $this->config->get('preferred_mirror'))) ||
              ($base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))))) {
            if ($base2) {
                $rest = &$this->config->getREST('1.3', $this->_options);
                $base = $base2;
            } else {
                $rest = &$this->config->getREST('1.0', $this->_options);
            }
            if (!isset($parr['version']) && !isset($parr['state']) && $version
                  && !PEAR::isError($version)
                  && !isset($this->_options['downloadonly'])) {
                $url = $rest->getDownloadURL($base, $parr, $state, $version, $chan->getName());
            } else {
                $url = $rest->getDownloadURL($base, $parr, $state, false, $chan->getName());
            }
            if (PEAR::isError($url)) {
                $this->configSet('default_channel', $curchannel);
                return $url;
            }
            if ($parr['channel'] != $curchannel) {
                $this->configSet('default_channel', $curchannel);
            }
            if (!is_array($url)) {
                return $url;
            }
            $url['raw'] = false; // no checking is necessary for REST
            if (!is_array($url['info'])) {
                return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' .
                    'this should never happen');
            }
            if (!isset($this->_options['force']) &&
                  !isset($this->_options['downloadonly']) &&
                  $version &&
                  !PEAR::isError($version) &&
                  !isset($parr['group'])) {
                if (version_compare($version, $url['version'], '>=')) {
                    return PEAR::raiseError($this->_registry->parsedPackageNameToString(
                        $parr, true) . ' is already installed and is newer than detected ' .
                        'release version ' . $url['version'], -976);
                }
            }
            if (isset($url['info']['required']) || $url['compatible']) {
                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2.php';
                $pf = new PEAR_PackageFile_v2;
                $pf->setRawChannel($parr['channel']);
                if ($url['compatible']) {
                    $pf->setRawCompatible($url['compatible']);
                }
            } else {
                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v1.php';
                $pf = new PEAR_PackageFile_v1;
            }
            $pf->setRawPackage($url['package']);
            $pf->setDeps($url['info']);
            if ($url['compatible']) {
                $pf->setCompatible($url['compatible']);
            }
            $pf->setRawState($url['stability']);
            $url['info'] = &$pf;
            if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
                $ext = '.tar';
            } else {
                $ext = '.tgz';
            }
            if (is_array($url)) {
                if (isset($url['url'])) {
                    $url['url'] .= $ext;
                }
            }
            return $url;
        } elseif ($chan->supports('xmlrpc', 'package.getDownloadURL', false, '1.1')) {
            // don't install with the old version information unless we're doing a plain
            // vanilla simple installation.  If the user says to install a particular
            // version or state, ignore the current installed version
            if (!isset($parr['version']) && !isset($parr['state']) && $version
                  && !isset($this->_options['downloadonly'])) {
                $url = $this->_remote->call('package.getDownloadURL', $parr, $state, $version);
            } else {
                $url = $this->_remote->call('package.getDownloadURL', $parr, $state);
            }
        } else {
            $url = $this->_remote->call('package.getDownloadURL', $parr, $state);
        }
        if (PEAR::isError($url)) {
            return $url;
        }
        if ($parr['channel'] != $curchannel) {
            $this->configSet('default_channel', $curchannel);
        }
        if (isset($url['__PEAR_ERROR_CLASS__'])) {
            return PEAR::raiseError($url['message']);
        }
        if (!is_array($url)) {
            return $url;
        }
        $url['raw'] = $url['info'];
        if (isset($this->_options['downloadonly'])) {
            $pkg = &$this->getPackagefileObject($this->config, $this->debug);
        } else {
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            if (PEAR::isError($dir = $this->getDownloadDir())) {
                PEAR::staticPopErrorHandling();
                return $dir;
            }
            PEAR::staticPopErrorHandling();
            $pkg = &$this->getPackagefileObject($this->config, $this->debug, $dir);
        }
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        $pinfo = &$pkg->fromXmlString($url['info'], PEAR_VALIDATE_DOWNLOADING, 'remote');
        PEAR::staticPopErrorHandling();
        if (PEAR::isError($pinfo)) {
            if (!isset($this->_options['soft'])) {
                $this->log(0, $pinfo->getMessage());
            }
            return PEAR::raiseError('Remote package.xml is not valid - this should never happen');
        }
        $url['info'] = &$pinfo;
        if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
            $ext = '.tar';
        } else {
            $ext = '.tgz';
        }
        if (is_array($url)) {
            if (isset($url['url'])) {
                $url['url'] .= $ext;
            }
        }
        return $url;
    }
    // }}}
    // {{{ getDepPackageDownloadUrl()

    /**
     * @param array dependency array
     * @access private
     */
    function _getDepPackageDownloadUrl($dep, $parr)
    {
        $xsdversion = isset($dep['rel']) ? '1.0' : '2.0';
        $curchannel = $this->config->get('default_channel');
        if (isset($dep['uri'])) {
            $xsdversion = '2.0';
            $chan = &$this->_registry->getChannel('__uri');
            if (PEAR::isError($chan)) {
                return $chan;
            }
            $version = $this->_registry->packageInfo($dep['name'], 'version', '__uri');
            $this->configSet('default_channel', '__uri');
        } else {
            if (isset($dep['channel'])) {
                $remotechannel = $dep['channel'];
            } else {
                $remotechannel = 'pear.php.net';
            }
            if (!$this->_registry->channelExists($remotechannel)) {
                do {
                    if ($this->config->get('auto_discover')) {
                        if ($this->discover($remotechannel)) {
                            break;
                        }
                    }
                    return PEAR::raiseError('Unknown remote channel: ' . $remotechannel);
                } while (false);
            }
            $chan = &$this->_registry->getChannel($remotechannel);
            if (PEAR::isError($chan)) {
                return $chan;
            }
            $version = $this->_registry->packageInfo($dep['name'], 'version',
                $remotechannel);
            $this->configSet('default_channel', $remotechannel);
        }
        $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state');
        if (isset($parr['state']) && isset($parr['version'])) {
            unset($parr['state']);
        }
        if (isset($dep['uri'])) {
            $info = &$this->newDownloaderPackage($this);
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            $err = $info->initialize($dep);
            PEAR::staticPopErrorHandling();
            if (!$err) {
                // skip parameters that were missed by preferred_state
                return PEAR::raiseError('Cannot initialize dependency');
            }
            if (PEAR::isError($err)) {
                if (!isset($this->_options['soft'])) {
                    $this->log(0, $err->getMessage());
                }
                if (is_object($info)) {
                    $param = $info->getChannel() . '/' . $info->getPackage();
                }
                return PEAR::raiseError('Package "' . $param . '" is not valid');
            }
            return $info;
        } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) &&
              $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
            $rest = &$this->config->getREST('1.0', $this->_options);
            $url = $rest->getDepDownloadURL($base, $xsdversion, $dep, $parr,
                    $state, $version, $chan->getName());
            if (PEAR::isError($url)) {
                return $url;
            }
            if ($parr['channel'] != $curchannel) {
                $this->configSet('default_channel', $curchannel);
            }
            if (!is_array($url)) {
                return $url;
            }
            $url['raw'] = false; // no checking is necessary for REST
            if (!is_array($url['info'])) {
                return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' .
                    'this should never happen');
            }
            if (isset($url['info']['required'])) {
                if (!class_exists('PEAR_PackageFile_v2')) {
                    require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2.php';
                }
                $pf = new PEAR_PackageFile_v2;
                $pf->setRawChannel($remotechannel);
            } else {
                if (!class_exists('PEAR_PackageFile_v1')) {
                    require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v1.php';
                }
                $pf = new PEAR_PackageFile_v1;
            }
            $pf->setRawPackage($url['package']);
            $pf->setDeps($url['info']);
            if ($url['compatible']) {
                $pf->setCompatible($url['compatible']);
            }
            $pf->setRawState($url['stability']);
            $url['info'] = &$pf;
            if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
                $ext = '.tar';
            } else {
                $ext = '.tgz';
            }
            if (is_array($url)) {
                if (isset($url['url'])) {
                    $url['url'] .= $ext;
                }
            }
            return $url;
        } elseif ($chan->supports('xmlrpc', 'package.getDepDownloadURL', false, '1.1')) {
            if ($version) {
                $url = $this->_remote->call('package.getDepDownloadURL', $xsdversion, $dep, $parr,
                    $state, $version);
            } else {
                $url = $this->_remote->call('package.getDepDownloadURL', $xsdversion, $dep, $parr,
                    $state);
            }
        } else {
            $url = $this->_remote->call('package.getDepDownloadURL', $xsdversion, $dep, $parr, $state);
        }
        if ($this->config->get('default_channel') != $curchannel) {
            $this->configSet('default_channel', $curchannel);
        }
        if (!is_array($url)) {
            return $url;
        }
        if (isset($url['__PEAR_ERROR_CLASS__'])) {
            return PEAR::raiseError($url['message']);
        }
        $url['raw'] = $url['info'];
        $pkg = &$this->getPackagefileObject($this->config, $this->debug);
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        $pinfo = &$pkg->fromXmlString($url['info'], PEAR_VALIDATE_DOWNLOADING, 'remote');
        PEAR::staticPopErrorHandling();
        if (PEAR::isError($pinfo)) {
            if (!isset($this->_options['soft'])) {
                $this->log(0, $pinfo->getMessage());
            }
            return PEAR::raiseError('Remote package.xml is not valid - this should never happen');
        }
        $url['info'] = &$pinfo;
        if (is_array($url)) {
            if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
                $ext = '.tar';
            } else {
                $ext = '.tgz';
            }
            if (isset($url['url'])) {
                $url['url'] .= $ext;
            }
        }
        return $url;
    }
    // }}}
    // {{{ getPackageDownloadUrl()

    /**
     * @deprecated in favor of _getPackageDownloadUrl
     */
    function getPackageDownloadUrl($package, $version = null, $channel = false)
    {
        if ($version) {
            $package .= "-$version";
        }
        if ($this === null || $this->_registry === null) {
            $package = "http://pear.php.net/get/$package";
        } else {
            $chan = $this->_registry->getChannel($channel);
            if (PEAR::isError($chan)) {
                return '';
            }
            $package = "http://" . $chan->getServer() . "/get/$package";
        }
        if (!extension_loaded("zlib")) {
            $package .= '?uncompress=yes';
        }
        return $package;
    }

    // }}}
    // {{{ 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;
    }

    // }}}
    // {{{ _downloadCallback()

    function _downloadCallback($msg, $params = null)
    {
        switch ($msg) {
            case 'saveas':
                $this->log(1, "downloading $params ...");
                break;
            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':
                if($params[1] == -1) {
                    $length = "Unknown size";
                } else {
                    $length = number_format($params[1], 0, '', ',')." bytes";
                }
                $this->log(1, "Starting to download {$params[0]} ($length)");
                break;
        }
        if (method_exists($this->ui, '_downloadCallback'))
            $this->ui->_downloadCallback($msg, $params);
    }

    // }}}
    // {{{ _prependPath($path, $prepend)

    function _prependPath($path, $prepend)
    {
        if (strlen($prepend) > 0) {
            if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
                if (preg_match('/^[a-z]:/i', $prepend)) {
                    $prepend = substr($prepend, 2);
                } elseif ($prepend{0} != '\\') {
                    $prepend = "\\$prepend";
                }
                $path = substr($path, 0, 2) . $prepend . substr($path, 2);
            } else {
                $path = $prepend . $path;
            }
        }
        return $path;
    }
    // }}}
    // {{{ 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;
    }

    // }}}

    /**
     * for BC
     */
    function sortPkgDeps(&$packages, $uninstall = false)
    {
        $uninstall ?
            $this->sortPackagesForUninstall($packages) :
            $this->sortPackagesForInstall($packages);
    }

    /**
     * Sort a list of arrays of array(downloaded packagefilename) by dependency.
     *
     * This uses the topological sort method from graph theory, and the
     * Structures_Graph package to properly sort dependencies for installation.
     * @param array an array of downloaded PEAR_Downloader_Packages
     * @return array array of array(packagefilename, package.xml contents)
     */
    function sortPackagesForInstall(&$packages)
    {
        require_once 'phar://install-pear-nozlib.phar/' . 'Structures/Graph.php';
        require_once 'phar://install-pear-nozlib.phar/' . 'Structures/Graph/Node.php';
        require_once 'phar://install-pear-nozlib.phar/' . 'Structures/Graph/Manipulator/TopologicalSorter.php';
        $depgraph = new Structures_Graph(true);
        $nodes = array();
        $reg = &$this->config->getRegistry();
        foreach ($packages as $i => $package) {
            $pname = $reg->parsedPackageNameToString(
                array(
                    'channel' => $package->getChannel(),
                    'package' => strtolower($package->getPackage()),
                ));
            $nodes[$pname] = new Structures_Graph_Node;
            $nodes[$pname]->setData($packages[$i]);
            $depgraph->addNode($nodes[$pname]);
        }
        $deplinks = array();
        foreach ($nodes as $package => $node) {
            $pf = &$node->getData();
            $pdeps = $pf->getDeps(true);
            if (!$pdeps) {
                continue;
            }
            if ($pf->getPackagexmlVersion() == '1.0') {
                foreach ($pdeps as $dep) {
                    if ($dep['type'] != 'pkg' ||
                          (isset($dep['optional']) && $dep['optional'] == 'yes')) {
                        continue;
                    }
                    $dname = $reg->parsedPackageNameToString(
                          array(
                              'channel' => 'pear.php.net',
                              'package' => strtolower($dep['name']),
                          ));
                    if (isset($nodes[$dname]))
                    {
                        if (!isset($deplinks[$dname])) {
                            $deplinks[$dname] = array();
                        }
                        $deplinks[$dname][$package] = 1;
                        // dependency is in installed packages
                        continue;
                    }
                    $dname = $reg->parsedPackageNameToString(
                          array(
                              'channel' => 'pecl.php.net',
                              'package' => strtolower($dep['name']),
                          ));
                    if (isset($nodes[$dname]))
                    {
                        if (!isset($deplinks[$dname])) {
                            $deplinks[$dname] = array();
                        }
                        $deplinks[$dname][$package] = 1;
                        // dependency is in installed packages
                        continue;
                    }
                }
            } else {
                // the only ordering we care about is:
                // 1) subpackages must be installed before packages that depend on them
                // 2) required deps must be installed before packages that depend on them
                if (isset($pdeps['required']['subpackage'])) {
                    $t = $pdeps['required']['subpackage'];
                    if (!isset($t[0])) {
                        $t = array($t);
                    }
                    $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
                }
                if (isset($pdeps['group'])) {
                    if (!isset($pdeps['group'][0])) {
                        $pdeps['group'] = array($pdeps['group']);
                    }
                    foreach ($pdeps['group'] as $group) {
                        if (isset($group['subpackage'])) {
                            $t = $group['subpackage'];
                            if (!isset($t[0])) {
                                $t = array($t);
                            }
                            $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
                        }
                    }
                }
                if (isset($pdeps['optional']['subpackage'])) {
                    $t = $pdeps['optional']['subpackage'];
                    if (!isset($t[0])) {
                        $t = array($t);
                    }
                    $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
                }
                if (isset($pdeps['required']['package'])) {
                    $t = $pdeps['required']['package'];
                    if (!isset($t[0])) {
                        $t = array($t);
                    }
                    $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
                }
                if (isset($pdeps['group'])) {
                    if (!isset($pdeps['group'][0])) {
                        $pdeps['group'] = array($pdeps['group']);
                    }
                    foreach ($pdeps['group'] as $group) {
                        if (isset($group['package'])) {
                            $t = $group['package'];
                            if (!isset($t[0])) {
                                $t = array($t);
                            }
                            $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
                        }
                    }
                }
            }
        }
        $this->_detectDepCycle($deplinks);
        foreach ($deplinks as $dependent => $parents) {
            foreach ($parents as $parent => $unused) {
                $nodes[$dependent]->connectTo($nodes[$parent]);
            }
        }
        $installOrder = Structures_Graph_Manipulator_TopologicalSorter::sort($depgraph);
        $ret = array();
        for ($i = 0; $i < count($installOrder); $i++) {
            foreach ($installOrder[$i] as $index => $sortedpackage) {
                $data = &$installOrder[$i][$index]->getData();
                $ret[] = &$nodes[$reg->parsedPackageNameToString(
                          array(
                              'channel' => $data->getChannel(),
                              'package' => strtolower($data->getPackage()),
                          ))]->getData();
            }
        }
        $packages = $ret;
        return;
    }

    /**
     * Detect recursive links between dependencies and break the cycles
     *
     * @param array
     * @access private
     */
    function _detectDepCycle(&$deplinks)
    {
        do {
            $keepgoing = false;
            foreach ($deplinks as $dep => $parents) {
                foreach ($parents as $parent => $unused) {
                    // reset the parent cycle detector
                    $this->_testCycle(null, null, null);
                    if ($this->_testCycle($dep, $deplinks, $parent)) {
                        $keepgoing = true;
                        unset($deplinks[$dep][$parent]);
                        if (count($deplinks[$dep]) == 0) {
                            unset($deplinks[$dep]);
                        }
                        continue 3;
                    }
                }
            }
        } while ($keepgoing);
    }

    function _testCycle($test, $deplinks, $dep)
    {
        static $visited = array();
        if ($test === null) {
            $visited = array();
            return;
        }
        // this happens when a parent has a dep cycle on another dependency
        // but the child is not part of the cycle
        if (isset($visited[$dep])) {
            return false;
        }
        $visited[$dep] = 1;
        if ($test == $dep) {
            return true;
        }
        if (isset($deplinks[$dep])) {
            if (in_array($test, array_keys($deplinks[$dep]), true)) {
                return true;
            }
            foreach ($deplinks[$dep] as $parent => $unused) {
                if ($this->_testCycle($test, $deplinks, $parent)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Set up the dependency for installation parsing
     *
     * @param array $t dependency information
     * @param PEAR_Registry $reg
     * @param array $deplinks list of dependency links already established
     * @param array $nodes all existing package nodes
     * @param string $package parent package name
     * @access private
     */
    function _setupGraph($t, $reg, &$deplinks, &$nodes, $package)
    {
        foreach ($t as $dep) {
            $depchannel = !isset($dep['channel']) ?
                '__uri': $dep['channel'];
            $dname = $reg->parsedPackageNameToString(
                  array(
                      'channel' => $depchannel,
                      'package' => strtolower($dep['name']),
                  ));
            if (isset($nodes[$dname]))
            {
                if (!isset($deplinks[$dname])) {
                    $deplinks[$dname] = array();
                }
                $deplinks[$dname][$package] = 1;
            }
        }
    }

    function _dependsOn($a, $b)
    {
        return $this->_checkDepTree(strtolower($a->getChannel()), strtolower($a->getPackage()),
            $b);
    }

    function _checkDepTree($channel, $package, $b, $checked = array())
    {
        $checked[$channel][$package] = true;
        if (!isset($this->_depTree[$channel][$package])) {
            return false;
        }
        if (isset($this->_depTree[$channel][$package][strtolower($b->getChannel())]
              [strtolower($b->getPackage())])) {
            return true;
        }
        foreach ($this->_depTree[$channel][$package] as $ch => $packages) {
            foreach ($packages as $pa => $true) {
                if ($this->_checkDepTree($ch, $pa, $b, $checked)) {
                    return true;
                }
            }
        }
        return false;
    }

    function _sortInstall($a, $b)
    {
        if (!$a->getDeps() && !$b->getDeps()) {
            return 0; // neither package has dependencies, order is insignificant
        }
        if ($a->getDeps() && !$b->getDeps()) {
            return 1; // $a must be installed after $b because $a has dependencies
        }
        if (!$a->getDeps() && $b->getDeps()) {
            return -1; // $b must be installed after $a because $b has dependencies
        }
        // both packages have dependencies
        if ($this->_dependsOn($a, $b)) {
            return 1;
        }
        if ($this->_dependsOn($b, $a)) {
            return -1;
        }
        return 0;
    }

    /**
     * Download a file through HTTP.  Considers suggested file name in
     * Content-disposition: header and can run a callback function for
     * different events.  The callback will be called with two
     * parameters: the callback type, and parameters.  The implemented
     * callback types are:
     *
     *  'setup'       called at the very beginning, parameter is a UI object
     *                that should be used for all output
     *  'message'     the parameter is a string with an informational message
     *  'saveas'      may be used to save with a different file name, the
     *                parameter is the filename that is about to be used.
     *                If a 'saveas' callback returns a non-empty string,
     *                that file name will be used as the filename instead.
     *                Note that $save_dir will not be affected by this, only
     *                the basename of the file.
     *  'start'       download is starting, parameter is number of bytes
     *                that are expected, or -1 if unknown
     *  'bytesread'   parameter is the number of bytes read so far
     *  'done'        download is complete, parameter is the total number
     *                of bytes read
     *  'connfailed'  if the TCP/SSL connection fails, this callback is called
     *                with array(host,port,errno,errmsg)
     *  'writefailed' if writing to disk fails, this callback is called
     *                with array(destfile,errmsg)
     *
     * If an HTTP proxy has been configured (http_proxy PEAR_Config
     * setting), the proxy will be used.
     *
     * @param string  $url       the URL to download
     * @param object  $ui        PEAR_Frontend_* instance
     * @param object  $config    PEAR_Config instance
     * @param string  $save_dir  directory to save file in
     * @param mixed   $callback  function/method to call for status
     *                           updates
     * @param false|string|array $lastmodified header values to check against for caching
     *                           use false to return the header values from this download
     * @param false|array $accept Accept headers to send
     * @param false|string $channel Channel to use for retrieving authentication
     * @return string|array  Returns the full path of the downloaded file or a PEAR
     *                       error on failure.  If the error is caused by
     *                       socket-related errors, the error object will
     *                       have the fsockopen error code available through
     *                       getCode().  If caching is requested, then return the header
     *                       values.
     *
     * @access public
     */
    function downloadHttp($url, &$ui, $save_dir = '.', $callback = null, $lastmodified = null,
                          $accept = false, $channel = false)
    {
        static $redirect = 0;
        // allways reset , so we are clean case of error
        $wasredirect = $redirect;
        $redirect = 0;
        if ($callback) {
            call_user_func($callback, 'setup', array(&$ui));
        }
        $info = parse_url($url);
        if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) {
            return PEAR::raiseError('Cannot download non-http URL "' . $url . '"');
        }
        if (!isset($info['host'])) {
            return PEAR::raiseError('Cannot download from non-URL "' . $url . '"');
        } else {
            $host = isset($info['host']) ? $info['host'] : null;
            $port = isset($info['port']) ? $info['port'] : null;
            $path = isset($info['path']) ? $info['path'] : null;
        }
        if (isset($this)) {
            $config = &$this->config;
        } else {
            $config = &PEAR_Config::singleton();
        }
        $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
        if ($config->get('http_proxy') &&
              $proxy = parse_url($config->get('http_proxy'))) {
            $proxy_host = isset($proxy['host']) ? $proxy['host'] : null;
            if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') {
                $proxy_host = 'ssl://' . $proxy_host;
            }
            $proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080;
            $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null;
            $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null;

            if ($callback) {
                call_user_func($callback, 'message', "Using HTTP proxy $host:$port");
            }
        }
        if (empty($port)) {
            if (isset($info['scheme']) && $info['scheme'] == 'https') {
                $port = 443;
            } else {
                $port = 80;
            }
        }
        if ($proxy_host != '') {
            $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr);
            if (!$fp) {
                if ($callback) {
                    call_user_func($callback, 'connfailed', array($proxy_host, $proxy_port,
                                                                  $errno, $errstr));
                }
                return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", $errno);
            }
            if ($lastmodified === false || $lastmodified) {
                $request = "GET $url HTTP/1.1\r\n";
            } else {
                $request = "GET $url HTTP/1.0\r\n";
            }
        } else {
            if (isset($info['scheme']) && $info['scheme'] == 'https') {
                $host = 'ssl://' . $host;
            }
            $fp = @fsockopen($host, $port, $errno, $errstr);
            if (!$fp) {
                if ($callback) {
                    call_user_func($callback, 'connfailed', array($host, $port,
                                                                  $errno, $errstr));
                }
                return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno);
            }
            if ($lastmodified === false || $lastmodified) {
                $request = "GET $path HTTP/1.1\r\n";
                $request .= "Host: $host:$port\r\n";
            } else {
                $request = "GET $path HTTP/1.0\r\n";
                $request .= "Host: $host\r\n";
            }
        }
        $ifmodifiedsince = '';
        if (is_array($lastmodified)) {
            if (isset($lastmodified['Last-Modified'])) {
                $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n";
            }
            if (isset($lastmodified['ETag'])) {
                $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n";
            }
        } else {
            $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : '');
        }
        $request .= $ifmodifiedsince . "User-Agent: PEAR/1.7.2/PHP/" .
            PHP_VERSION . "\r\n";
        if (isset($this)) { // only pass in authentication for non-static calls
            $username = $config->get('username', null, $channel);
            $password = $config->get('password', null, $channel);
            if ($username && $password) {
                $tmp = base64_encode("$username:$password");
                $request .= "Authorization: Basic $tmp\r\n";
            }
        }
        if ($proxy_host != '' && $proxy_user != '') {
            $request .= 'Proxy-Authorization: Basic ' .
                base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n";
        }
        if ($accept) {
            $request .= 'Accept: ' . implode(', ', $accept) . "\r\n";
        }
        $request .= "Connection: close\r\n";
        $request .= "\r\n";
        fwrite($fp, $request);
        $headers = array();
        $reply = 0;
        while (trim($line = fgets($fp, 1024))) {
            if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) {
                $headers[strtolower($matches[1])] = trim($matches[2]);
            } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
                $reply = (int) $matches[1];
                if ($reply == 304 && ($lastmodified || ($lastmodified === false))) {
                    return false;
                }
                if (! in_array($reply, array(200, 301, 302, 303, 305, 307))) {
                    return PEAR::raiseError("File http://$host:$port$path not valid (received: $line)");
                }
            }
        }
        if ($reply != 200) {
            if (isset($headers['location'])) {
                if ($wasredirect < 5) {
                    $redirect = $wasredirect + 1;
                    return $this->downloadHttp($headers['location'],
                            $ui, $save_dir, $callback, $lastmodified, $accept);
                } else {
                    return PEAR::raiseError("File http://$host:$port$path not valid (redirection looped more than 5 times)");
                }
            } else {
                return PEAR::raiseError("File http://$host:$port$path not valid (redirected but no location)");
            }
        }
        if (isset($headers['content-disposition']) &&
            preg_match('/\sfilename=\"([^;]*\S)\"\s*(;|\\z)/', $headers['content-disposition'], $matches)) {
            $save_as = basename($matches[1]);
        } else {
            $save_as = basename($url);
        }
        if ($callback) {
            $tmp = call_user_func($callback, 'saveas', $save_as);
            if ($tmp) {
                $save_as = $tmp;
            }
        }
        $dest_file = $save_dir . DIRECTORY_SEPARATOR . $save_as;
        if (!$wp = @fopen($dest_file, 'wb')) {
            fclose($fp);
            if ($callback) {
                call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg));
            }
            return PEAR::raiseError("could not open $dest_file for writing");
        }
        if (isset($headers['content-length'])) {
            $length = $headers['content-length'];
        } else {
            $length = -1;
        }
        $bytes = 0;
        if ($callback) {
            call_user_func($callback, 'start', array(basename($dest_file), $length));
        }
        while ($data = fread($fp, 1024)) {
            $bytes += strlen($data);
            if ($callback) {
                call_user_func($callback, 'bytesread', $bytes);
            }
            if (!@fwrite($wp, $data)) {
                fclose($fp);
                if ($callback) {
                    call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg));
                }
                return PEAR::raiseError("$dest_file: write failed ($php_errormsg)");
            }
        }
        fclose($fp);
        fclose($wp);
        if ($callback) {
            call_user_func($callback, 'done', $bytes);
        }
        if ($lastmodified === false || $lastmodified) {
            if (isset($headers['etag'])) {
                $lastmodified = array('ETag' => $headers['etag']);
            }
            if (isset($headers['last-modified'])) {
                if (is_array($lastmodified)) {
                    $lastmodified['Last-Modified'] = $headers['last-modified'];
                } else {
                    $lastmodified = $headers['last-modified'];
                }
            }
            return array($dest_file, $lastmodified, $headers);
        }
        return $dest_file;
    }
}
// }}}

?>
<?php
/**
 * PEAR_Downloader_Package
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Package.php,v 1.113 2008/03/29 14:18:36 dufuz Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * Error code when parameter initialization fails because no releases
 * exist within preferred_state, but releases do exist
 */
define('PEAR_DOWNLOADER_PACKAGE_STATE', -1003);
/**
 * Error code when parameter initialization fails because no releases
 * exist that will work with the existing PHP version
 */
define('PEAR_DOWNLOADER_PACKAGE_PHPVERSION', -1004);
/**
 * Coordinates download parameters and manages their dependencies
 * prior to downloading them.
 *
 * Input can come from three sources:
 *
 * - local files (archives or package.xml)
 * - remote files (downloadable urls)
 * - abstract package names
 *
 * The first two elements are handled cleanly by PEAR_PackageFile, but the third requires
 * accessing pearweb's xml-rpc interface to determine necessary dependencies, and the
 * format returned of dependencies is slightly different from that used in package.xml.
 *
 * This class hides the differences between these elements, and makes automatic
 * dependency resolution a piece of cake.  It also manages conflicts when
 * two classes depend on incompatible dependencies, or differing versions of the same
 * package dependency.  In addition, download will not be attempted if the php version is
 * not supported, PEAR installer version is not supported, or non-PECL extensions are not
 * installed.
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Downloader_Package
{
    /**
     * @var PEAR_Downloader
     */
    var $_downloader;
    /**
     * @var PEAR_Config
     */
    var $_config;
    /**
     * @var PEAR_Registry
     */
    var $_registry;
    /**
     * Used to implement packagingroot properly
     * @var PEAR_Registry
     */
    var $_installRegistry;
    /**
     * @var PEAR_PackageFile_v1|PEAR_PackageFile|v2
     */
    var $_packagefile;
    /**
     * @var array
     */
    var $_parsedname;
    /**
     * @var array
     */
    var $_downloadURL;
    /**
     * @var array
     */
    var $_downloadDeps = array();
    /**
     * @var boolean
     */
    var $_valid = false;
    /**
     * @var boolean
     */
    var $_analyzed = false;
    /**
     * if this or a parent package was invoked with Package-state, this is set to the
     * state variable.
     *
     * This allows temporary reassignment of preferred_state for a parent package and all of
     * its dependencies.
     * @var string|false
     */
    var $_explicitState = false;
    /**
     * If this package is invoked with Package#group, this variable will be true
     */
    var $_explicitGroup = false;
    /**
     * Package type local|url|xmlrpc
     * @var string
     */
    var $_type;
    /**
     * Contents of package.xml, if downloaded from a remote channel
     * @var string|false
     * @access private
     */
    var $_rawpackagefile;
    /**
     * @var boolean
     * @access private
     */
    var $_validated = false;

    /**
     * @param PEAR_Downloader
     */
    function PEAR_Downloader_Package(&$downloader)
    {
        $this->_downloader = &$downloader;
        $this->_config = &$this->_downloader->config;
        $this->_registry = &$this->_config->getRegistry();
        $options = $downloader->getOptions();
        if (isset($options['packagingroot'])) {
            $this->_config->setInstallRoot($options['packagingroot']);
            $this->_installRegistry = &$this->_config->getRegistry();
            $this->_config->setInstallRoot(false);
        } else {
            $this->_installRegistry = &$this->_registry;
        }
        $this->_valid = $this->_analyzed = false;
    }

    /**
     * Parse the input and determine whether this is a local file, a remote uri, or an
     * abstract package name.
     *
     * This is the heart of the PEAR_Downloader_Package(), and is used in
     * {@link PEAR_Downloader::download()}
     * @param string
     * @return bool|PEAR_Error
     */
    function initialize($param)
    {
        $origErr = $this->_fromFile($param);
        if (!$this->_valid) {
            $options = $this->_downloader->getOptions();
            if (isset($options['offline'])) {
                if (PEAR::isError($origErr)) {
                    if (!isset($options['soft'])) {
                        $this->_downloader->log(0, $origErr->getMessage());
                    }
                }
                return PEAR::raiseError('Cannot download non-local package "' . $param . '"');
            }
            $err = $this->_fromUrl($param);
            if (PEAR::isError($err) || !$this->_valid) {
                if ($this->_type == 'url') {
                    if (PEAR::isError($err)) {
                        if (!isset($options['soft'])) {
                            $this->_downloader->log(0, $err->getMessage());
                        }
                    }
                    return PEAR::raiseError("Invalid or missing remote package file");
                }
                $err = $this->_fromString($param);
                if (PEAR::isError($err) || !$this->_valid) {
                    if (PEAR::isError($err) &&
                          $err->getCode() == PEAR_DOWNLOADER_PACKAGE_STATE) {
                        return false; // instruct the downloader to silently skip
                    }
                    if (isset($this->_type) && $this->_type == 'local' &&
                          PEAR::isError($origErr)) {
                        if (is_array($origErr->getUserInfo())) {
                            foreach ($origErr->getUserInfo() as $err) {
                                if (is_array($err)) {
                                    $err = $err['message'];
                                }
                                if (!isset($options['soft'])) {
                                    $this->_downloader->log(0, $err);
                                }
                            }
                        }
                        if (!isset($options['soft'])) {
                            $this->_downloader->log(0, $origErr->getMessage());
                        }
                        if (is_array($param)) {
                            $param = $this->_registry->parsedPackageNameToString($param,
                                true);
                        }
                        return PEAR::raiseError(
                            "Cannot initialize '$param', invalid or missing package file");
                    }
                    if (PEAR::isError($err)) {
                        if (!isset($options['soft'])) {
                            $this->_downloader->log(0, $err->getMessage());
                        }
                    }
                    if (is_array($param)) {
                        $param = $this->_registry->parsedPackageNameToString($param, true);
                    }
                    return PEAR::raiseError(
                        "Cannot initialize '$param', invalid or missing package file");
                }
            }
        }
        return true;
    }

    /**
     * Retrieve any non-local packages
     * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|PEAR_Error
     */
    function &download()
    {
        if (isset($this->_packagefile)) {
            return $this->_packagefile;
        }
        if (isset($this->_downloadURL['url'])) {
            $this->_isvalid = false;
            $info = $this->getParsedPackage();
            foreach ($info as $i => $p) {
                $info[$i] = strtolower($p);
            }
            $err = $this->_fromUrl($this->_downloadURL['url'],
                $this->_registry->parsedPackageNameToString($this->_parsedname, true));
            $newinfo = $this->getParsedPackage();
            foreach ($newinfo as $i => $p) {
                $newinfo[$i] = strtolower($p);
            }
            if ($info != $newinfo) {
                do {
                    if ($info['package'] == 'pecl.php.net' && $newinfo['package'] == 'pear.php.net') {
                        $info['package'] = 'pear.php.net';
                        if ($info == $newinfo) {
                            // skip the channel check if a pecl package says it's a PEAR package
                            break;
                        }
                    }
                    return PEAR::raiseError('CRITICAL ERROR: We are ' .
                        $this->_registry->parsedPackageNameToString($info) . ', but the file ' .
                        'downloaded claims to be ' .
                        $this->_registry->parsedPackageNameToString($this->getParsedPackage()));
                } while (false);
            }
            if (PEAR::isError($err) || !$this->_valid) {
                return $err;
            }
        }
        $this->_type = 'local';
        return $this->_packagefile;
    }

    function &getPackageFile()
    {
        return $this->_packagefile;
    }

    function &getDownloader()
    {
        return $this->_downloader;
    }

    function getType()
    {
        return $this->_type;
    }

    /**
     * Like {@link initialize()}, but operates on a dependency
     */
    function fromDepURL($dep)
    {
        $this->_downloadURL = $dep;
        if (isset($dep['uri'])) {
            $options = $this->_downloader->getOptions();
            if (!extension_loaded("zlib") || isset($options['nocompress'])) {
                $ext = '.tar';
            } else {
                $ext = '.tgz';
            }
            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
            $err = $this->_fromUrl($dep['uri'] . $ext);
            PEAR::popErrorHandling();
            if (PEAR::isError($err)) {
                if (!isset($options['soft'])) {
                    $this->_downloader->log(0, $err->getMessage());
                }
                return PEAR::raiseError('Invalid uri dependency "' . $dep['uri'] . $ext . '", ' .
                    'cannot download');
            }
        } else {
            $this->_parsedname =
                array(
                    'package' => $dep['info']->getPackage(),
                    'channel' => $dep['info']->getChannel(),
                    'version' => $dep['version']
                );
            if (!isset($dep['nodefault'])) {
                $this->_parsedname['group'] = 'default'; // download the default dependency group
                $this->_explicitGroup = false;
            }
            $this->_rawpackagefile = $dep['raw'];
        }
    }

    function detectDependencies($params)
    {
        $options = $this->_downloader->getOptions();
        if (isset($options['downloadonly'])) {
            return;
        }
        if (isset($options['offline'])) {
            $this->_downloader->log(3, 'Skipping dependency download check, --offline specified');
            return;
        }
        $pname = $this->getParsedPackage();
        if (!$pname) {
            return;
        }
        $deps = $this->getDeps();
        if (!$deps) {
            return;
        }
        if (isset($deps['required'])) { // package.xml 2.0
            return $this->_detect2($deps, $pname, $options, $params);
        } else {
            return $this->_detect1($deps, $pname, $options, $params);
        }
    }

    function setValidated()
    {
        $this->_validated = true;
    }

    function alreadyValidated()
    {
        return $this->_validated;
    }

    /**
     * Remove packages to be downloaded that are already installed
     * @param array of PEAR_Downloader_Package objects
     * @static
     */
    function removeInstalled(&$params)
    {
        if (!isset($params[0])) {
            return;
        }
        $options = $params[0]->_downloader->getOptions();
        if (!isset($options['downloadonly'])) {
            foreach ($params as $i => $param) {
                // remove self if already installed with this version
                // this does not need any pecl magic - we only remove exact matches
                if ($param->_installRegistry->packageExists($param->getPackage(), $param->getChannel())) {
                    if (version_compare($param->_installRegistry->packageInfo($param->getPackage(), 'version',
                          $param->getChannel()), $param->getVersion(), '==')) {
                        if (!isset($options['force'])) {
                            $info = $param->getParsedPackage();
                            unset($info['version']);
                            unset($info['state']);
                            if (!isset($options['soft'])) {
                                $param->_downloader->log(1, 'Skipping package "' .
                                    $param->getShortName() .
                                    '", already installed as version ' .
                                    $param->_installRegistry->packageInfo($param->getPackage(),
                                        'version', $param->getChannel()));
                            }
                            $params[$i] = false;
                        }
                    } elseif (!isset($options['force']) && !isset($options['upgrade']) &&
                          !isset($options['soft'])) {
                        $info = $param->getParsedPackage();
                        $param->_downloader->log(1, 'Skipping package "' .
                            $param->getShortName() .
                            '", already installed as version ' .
                            $param->_installRegistry->packageInfo($param->getPackage(), 'version',
                                $param->getChannel()));
                        $params[$i] = false;
                    }
                }
            }
        }
        PEAR_Downloader_Package::removeDuplicates($params);
    }

    function _detect2($deps, $pname, $options, $params)
    {
        $this->_downloadDeps = array();
        $groupnotfound = false;
        foreach (array('package', 'subpackage') as $packagetype) {
            // get required dependency group
            if (isset($deps['required'][$packagetype])) {
                if (isset($deps['required'][$packagetype][0])) {
                    foreach ($deps['required'][$packagetype] as $dep) {
                        if (isset($dep['conflicts'])) {
                            // skip any package that this package conflicts with
                            continue;
                        }
                        $ret = $this->_detect2Dep($dep, $pname, 'required', $params);
                        if (is_array($ret)) {
                            $this->_downloadDeps[] = $ret;
                        }
                    }
                } else {
                    $dep = $deps['required'][$packagetype];
                    if (!isset($dep['conflicts'])) {
                        // skip any package that this package conflicts with
                        $ret = $this->_detect2Dep($dep, $pname, 'required', $params);
                        if (is_array($ret)) {
                            $this->_downloadDeps[] = $ret;
                        }
                    }
                }
            }
            // get optional dependency group, if any
            if (isset($deps['optional'][$packagetype])) {
                $skipnames = array();
                if (!isset($deps['optional'][$packagetype][0])) {
                    $deps['optional'][$packagetype] = array($deps['optional'][$packagetype]);
                }
                foreach ($deps['optional'][$packagetype] as $dep) {
                    $skip = false;
                    if (!isset($options['alldeps'])) {
                        $dep['package'] = $dep['name'];
                        if (!isset($options['soft'])) {
                            $this->_downloader->log(3, 'Notice: package "' .
                              $this->_registry->parsedPackageNameToString($this->getParsedPackage(),
                                    true) . '" optional dependency "' .
                                $this->_registry->parsedPackageNameToString(array('package' =>
                                    $dep['name'], 'channel' => 'pear.php.net'), true) .
                                '" will not be automatically downloaded');
                        }
                        $skipnames[] = $this->_registry->parsedPackageNameToString($dep, true);
                        $skip = true;
                        unset($dep['package']);
                    }
                    if (!($ret = $this->_detect2Dep($dep, $pname, 'optional', $params))) {
                        $dep['package'] = $dep['name'];
                        $skip = count($skipnames) ?
                            $skipnames[count($skipnames) - 1] : '';
                        if ($skip ==
                              $this->_registry->parsedPackageNameToString($dep, true)) {
                            array_pop($skipnames);
                        }
                    }
                    if (!$skip && is_array($ret)) {
                        $this->_downloadDeps[] = $ret;
                    }
                }
                if (count($skipnames)) {
                    if (!isset($options['soft'])) {
                        $this->_downloader->log(1, 'Did not download optional dependencies: ' .
                            implode(', ', $skipnames) .
                            ', use --alldeps to download automatically');
                    }
                }
            }
            // get requested dependency group, if any
            $groupname = $this->getGroup();
            $explicit = $this->_explicitGroup;
            if (!$groupname) {
                if ($this->canDefault()) {
                    $groupname = 'default'; // try the default dependency group
                } else {
                    continue;
                }
            }
            if ($groupnotfound) {
                continue;
            }
            if (isset($deps['group'])) {
                if (isset($deps['group']['attribs'])) {
                    if (strtolower($deps['group']['attribs']['name']) == strtolower($groupname)) {
                        $group = $deps['group'];
                    } elseif ($explicit) {
                        if (!isset($options['soft'])) {
                            $this->_downloader->log(0, 'Warning: package "' .
                                $this->_registry->parsedPackageNameToString($pname, true) .
                                '" has no dependency ' . 'group named "' . $groupname . '"');
                        }
                        $groupnotfound = true;
                        continue;
                    }
                } else {
                    $found = false;
                    foreach ($deps['group'] as $group) {
                        if (strtolower($group['attribs']['name']) == strtolower($groupname)) {
                            $found = true;
                            break;
                        }
                    }
                    if (!$found) {
                        if ($explicit) {
                            if (!isset($options['soft'])) {
                                $this->_downloader->log(0, 'Warning: package "' .
                                    $this->_registry->parsedPackageNameToString($pname, true) .
                                    '" has no dependency ' . 'group named "' . $groupname . '"');
                            }
                        }
                        $groupnotfound = true;
                        continue;
                    }
                }
            }
            if (isset($group)) {
                if (isset($group[$packagetype])) {
                    if (isset($group[$packagetype][0])) {
                        foreach ($group[$packagetype] as $dep) {
                            $ret = $this->_detect2Dep($dep, $pname, 'dependency group "' .
                                $group['attribs']['name'] . '"', $params);
                            if (is_array($ret)) {
                                $this->_downloadDeps[] = $ret;
                            }
                        }
                    } else {
                        $ret = $this->_detect2Dep($group[$packagetype], $pname,
                            'dependency group "' .
                            $group['attribs']['name'] . '"', $params);
                        if (is_array($ret)) {
                            $this->_downloadDeps[] = $ret;
                        }
                    }
                }
            }
        }
    }

    function _detect2Dep($dep, $pname, $group, $params)
    {
        if (isset($dep['conflicts'])) {
            return true;
        }
        $options = $this->_downloader->getOptions();
        if (isset($dep['uri'])) {
            return array('uri' => $dep['uri'], 'dep' => $dep);;
        }
        $testdep = $dep;
        $testdep['package'] = $dep['name'];
        if (PEAR_Downloader_Package::willDownload($testdep, $params)) {
            $dep['package'] = $dep['name'];
            if (!isset($options['soft'])) {
                $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group .
                    ' dependency "' .
                    $this->_registry->parsedPackageNameToString($dep, true) .
                    '", will be installed');
            }
            return false;
        }
        $options = $this->_downloader->getOptions();
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        if ($this->_explicitState) {
            $pname['state'] = $this->_explicitState;
        }
        $url =
            $this->_downloader->_getDepPackageDownloadUrl($dep, $pname);
        if (PEAR::isError($url)) {
            PEAR::popErrorHandling();
            return $url;
        }
        $dep['package'] = $dep['name'];
        $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params, $group == 'optional' &&
            !isset($options['alldeps']), true);
        PEAR::popErrorHandling();
        if (PEAR::isError($ret)) {
            if (!isset($options['soft'])) {
                $this->_downloader->log(0, $ret->getMessage());
            }
            return false;
        } else {
            // check to see if a dep is already installed and is the same or newer
            if (!isset($dep['min']) && !isset($dep['max']) && !isset($dep['recommended'])) {
                $oper = 'has';
            } else {
                $oper = 'gt';
            }
            // do not try to move this before getDepPackageDownloadURL
            // we can't determine whether upgrade is necessary until we know what
            // version would be downloaded
            if (!isset($options['force']) && $this->isInstalled($ret, $oper)) {
                $version = $this->_installRegistry->packageInfo($dep['name'], 'version',
                    $dep['channel']);
                $dep['package'] = $dep['name'];
                if (!isset($options['soft'])) {
                    $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group .
                        ' dependency "' .
                    $this->_registry->parsedPackageNameToString($dep, true) .
                        '" version ' . $url['version'] . ', already installed as version ' .
                        $version);
                }
                return false;
            }
        }
        if (isset($dep['nodefault'])) {
            $ret['nodefault'] = true;
        }
        return $ret;
    }

    function _detect1($deps, $pname, $options, $params)
    {
        $this->_downloadDeps = array();
        $skipnames = array();
        foreach ($deps as $dep) {
            $nodownload = false;
            if ($dep['type'] == 'pkg') {
                $dep['channel'] = 'pear.php.net';
                $dep['package'] = $dep['name'];
                switch ($dep['rel']) {
                    case 'not' :
                        continue 2;
                    case 'ge' :
                    case 'eq' :
                    case 'gt' :
                    case 'has' :
                        $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
                            'required' :
                            'optional';
                        if (PEAR_Downloader_Package::willDownload($dep, $params)) {
                            $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group
                                . ' dependency "' .
                                $this->_registry->parsedPackageNameToString($dep, true) .
                                '", will be installed');
                            continue 2;
                        }
                        $fakedp = new PEAR_PackageFile_v1;
                        $fakedp->setPackage($dep['name']);
                        // skip internet check if we are not upgrading (bug #5810)
                        if (!isset($options['upgrade']) && $this->isInstalled(
                              $fakedp, $dep['rel'])) {
                            $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group
                                . ' dependency "' .
                                $this->_registry->parsedPackageNameToString($dep, true) .
                                '", is already installed');
                            continue 2;
                        }
                }
                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                if ($this->_explicitState) {
                    $pname['state'] = $this->_explicitState;
                }
                $url =
                    $this->_downloader->_getDepPackageDownloadUrl($dep, $pname);
                $chan = 'pear.php.net';
                if (PEAR::isError($url)) {
                    // check to see if this is a pecl package that has jumped
                    // from pear.php.net to pecl.php.net channel
                    if (!class_exists('PEAR_Dependency2')) {
                        require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Dependency2.php';
                    }
                    $newdep = PEAR_Dependency2::normalizeDep($dep);
                    $newdep = $newdep[0];
                    $newdep['channel'] = 'pecl.php.net';
                    $chan = 'pecl.php.net';
                    $url =
                        $this->_downloader->_getDepPackageDownloadUrl($newdep, $pname);
                    $obj = &$this->_installRegistry->getPackage($dep['name']);
                    if (PEAR::isError($url)) {
                        PEAR::popErrorHandling();
                        if ($obj !== null && $this->isInstalled($obj, $dep['rel'])) {
                            $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
                                'required' :
                                'optional';
                            $dep['package'] = $dep['name'];
                            if (!isset($options['soft'])) {
                                $this->_downloader->log(3, $this->getShortName() .
                                    ': Skipping ' . $group . ' dependency "' .
                                    $this->_registry->parsedPackageNameToString($dep, true) .
                                    '", already installed as version ' . $obj->getVersion());
                            }
                            $skip = count($skipnames) ?
                                $skipnames[count($skipnames) - 1] : '';
                            if ($skip ==
                                  $this->_registry->parsedPackageNameToString($dep, true)) {
                                array_pop($skipnames);
                            }
                            continue;
                        } else {
                            if (isset($dep['optional']) && $dep['optional'] == 'yes') {
                                $this->_downloader->log(2, $this->getShortName() .
                                    ': Skipping optional dependency "' .
                                    $this->_registry->parsedPackageNameToString($dep, true) .
                                    '", no releases exist');
                                continue;
                            } else {
                                return $url;
                            }
                        }
                    }
                }
                PEAR::popErrorHandling();
                if (!isset($options['alldeps'])) {
                    if (isset($dep['optional']) && $dep['optional'] == 'yes') {
                        if (!isset($options['soft'])) {
                            $this->_downloader->log(3, 'Notice: package "' .
                                $this->getShortName() .
                                '" optional dependency "' .
                                $this->_registry->parsedPackageNameToString(
                                    array('channel' => $chan, 'package' =>
                                    $dep['name']), true) .
                                '" will not be automatically downloaded');
                        }
                        $skipnames[] = $this->_registry->parsedPackageNameToString(
                                array('channel' => $chan, 'package' =>
                                $dep['name']), true);
                        $nodownload = true;
                    }
                }
                if (!isset($options['alldeps']) && !isset($options['onlyreqdeps'])) {
                    if (!isset($dep['optional']) || $dep['optional'] == 'no') {
                        if (!isset($options['soft'])) {
                            $this->_downloader->log(3, 'Notice: package "' .
                                $this->getShortName() .
                                '" required dependency "' .
                                $this->_registry->parsedPackageNameToString(
                                    array('channel' => $chan, 'package' =>
                                    $dep['name']), true) .
                                '" will not be automatically downloaded');
                        }
                        $skipnames[] = $this->_registry->parsedPackageNameToString(
                                array('channel' => $chan, 'package' =>
                                $dep['name']), true);
                        $nodownload = true;
                    }
                }
                // check to see if a dep is already installed
                // do not try to move this before getDepPackageDownloadURL
                // we can't determine whether upgrade is necessary until we know what
                // version would be downloaded
                if (!isset($options['force']) && $this->isInstalled(
                        $url, $dep['rel'])) {
                    $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
                        'required' :
                        'optional';
                    $dep['package'] = $dep['name'];
                    if (isset($newdep)) {
                        $version = $this->_installRegistry->packageInfo($newdep['name'], 'version',
                            $newdep['channel']);
                    } else {
                        $version = $this->_installRegistry->packageInfo($dep['name'], 'version');
                    }
                    $dep['version'] = $url['version'];
                    if (!isset($options['soft'])) {
                        $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group .
                            ' dependency "' .
                            $this->_registry->parsedPackageNameToString($dep, true) .
                            '", already installed as version ' . $version);
                    }
                    $skip = count($skipnames) ?
                        $skipnames[count($skipnames) - 1] : '';
                    if ($skip ==
                          $this->_registry->parsedPackageNameToString($dep, true)) {
                        array_pop($skipnames);
                    }
                    continue;
                }
                if ($nodownload) {
                    continue;
                }
                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                if (isset($newdep)) {
                    $dep = $newdep;
                }
                $dep['package'] = $dep['name'];
                $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params,
                    isset($dep['optional']) && $dep['optional'] == 'yes' &&
                    !isset($options['alldeps']), true);
                PEAR::popErrorHandling();
                if (PEAR::isError($ret)) {
                    if (!isset($options['soft'])) {
                        $this->_downloader->log(0, $ret->getMessage());
                    }
                    continue;
                }
                $this->_downloadDeps[] = $ret;
            }
        }
        if (count($skipnames)) {
            if (!isset($options['soft'])) {
                $this->_downloader->log(1, 'Did not download dependencies: ' .
                    implode(', ', $skipnames) .
                    ', use --alldeps or --onlyreqdeps to download automatically');
            }
        }
    }

    function setDownloadURL($pkg)
    {
        $this->_downloadURL = $pkg;
    }

    /**
     * Set the package.xml object for this downloaded package
     *
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 $pkg
     */
    function setPackageFile(&$pkg)
    {
        $this->_packagefile = &$pkg;
    }

    function getShortName()
    {
        return $this->_registry->parsedPackageNameToString(array('channel' => $this->getChannel(),
            'package' => $this->getPackage()), true);
    }

    function getParsedPackage()
    {
        if (isset($this->_packagefile) || isset($this->_parsedname)) {
            return array('channel' => $this->getChannel(),
                'package' => $this->getPackage(),
                'version' => $this->getVersion());
        }
        return false;
    }

    function getDownloadURL()
    {
        return $this->_downloadURL;
    }

    function canDefault()
    {
        if (isset($this->_downloadURL)) {
            if (isset($this->_downloadURL['nodefault'])) {
                return false;
            }
        }
        return true;
    }

    function getPackage()
    {
        if (isset($this->_packagefile)) {
            return $this->_packagefile->getPackage();
        } elseif (isset($this->_downloadURL['info'])) {
            return $this->_downloadURL['info']->getPackage();
        } else {
            return false;
        }
    }

    /**
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     */
    function isSubpackage(&$pf)
    {
        if (isset($this->_packagefile)) {
            return $this->_packagefile->isSubpackage($pf);
        } elseif (isset($this->_downloadURL['info'])) {
            return $this->_downloadURL['info']->isSubpackage($pf);
        } else {
            return false;
        }
    }

    function getPackageType()
    {
        if (isset($this->_packagefile)) {
            return $this->_packagefile->getPackageType();
        } elseif (isset($this->_downloadURL['info'])) {
            return $this->_downloadURL['info']->getPackageType();
        } else {
            return false;
        }
    }

    function isBundle()
    {
        if (isset($this->_packagefile)) {
            return $this->_packagefile->getPackageType() == 'bundle';
        } else {
            return false;
        }
    }

    function getPackageXmlVersion()
    {
        if (isset($this->_packagefile)) {
            return $this->_packagefile->getPackagexmlVersion();
        } elseif (isset($this->_downloadURL['info'])) {
            return $this->_downloadURL['info']->getPackagexmlVersion();
        } else {
            return '1.0';
        }
    }

    function getChannel()
    {
        if (isset($this->_packagefile)) {
            return $this->_packagefile->getChannel();
        } elseif (isset($this->_downloadURL['info'])) {
            return $this->_downloadURL['info']->getChannel();
        } else {
            return false;
        }
    }

    function getURI()
    {
        if (isset($this->_packagefile)) {
            return $this->_packagefile->getURI();
        } elseif (isset($this->_downloadURL['info'])) {
            return $this->_downloadURL['info']->getURI();
        } else {
            return false;
        }
    }

    function getVersion()
    {
        if (isset($this->_packagefile)) {
            return $this->_packagefile->getVersion();
        } elseif (isset($this->_downloadURL['version'])) {
            return $this->_downloadURL['version'];
        } else {
            return false;
        }
    }

    function isCompatible($pf)
    {
        if (isset($this->_packagefile)) {
            return $this->_packagefile->isCompatible($pf);
        } elseif (isset($this->_downloadURL['info'])) {
            return $this->_downloadURL['info']->isCompatible($pf);
        } else {
            return true;
        }
    }

    function setGroup($group)
    {
        $this->_parsedname['group'] = $group;
    }

    function getGroup()
    {
        if (isset($this->_parsedname['group'])) {
            return $this->_parsedname['group'];
        } else {
            return '';
        }
    }

    function isExtension($name)
    {
        if (isset($this->_packagefile)) {
            return $this->_packagefile->isExtension($name);
        } elseif (isset($this->_downloadURL['info'])) {
			if ($this->_downloadURL['info']->getPackagexmlVersion() == '2.0') {
				return $this->_downloadURL['info']->getProvidesExtension() == $name;
			} else {
				return false;
			}
        } else {
            return false;
        }
    }

    function getDeps()
    {
        if (isset($this->_packagefile)) {
            $ver = $this->_packagefile->getPackagexmlVersion();
            if (version_compare($ver, '2.0', '>=')) {
                return $this->_packagefile->getDeps(true);
            } else {
                return $this->_packagefile->getDeps();
            }
        } elseif (isset($this->_downloadURL['info'])) {
            $ver = $this->_downloadURL['info']->getPackagexmlVersion();
            if (version_compare($ver, '2.0', '>=')) {
                return $this->_downloadURL['info']->getDeps(true);
            } else {
                return $this->_downloadURL['info']->getDeps();
            }
        } else {
            return array();
        }
    }

    /**
     * @param array Parsed array from {@link PEAR_Registry::parsePackageName()} or a dependency
     *                     returned from getDepDownloadURL()
     */
    function isEqual($param)
    {
        if (is_object($param)) {
            $channel = $param->getChannel();
            $package = $param->getPackage();
            if ($param->getURI()) {
                $param = array(
                    'channel' => $param->getChannel(),
                    'package' => $param->getPackage(),
                    'version' => $param->getVersion(),
                    'uri' => $param->getURI(),
                );
            } else {
                $param = array(
                    'channel' => $param->getChannel(),
                    'package' => $param->getPackage(),
                    'version' => $param->getVersion(),
                );
            }
        } else {
            if (isset($param['uri'])) {
                if ($this->getChannel() != '__uri') {
                    return false;
                }
                return $param['uri'] == $this->getURI();
            }
            $package = isset($param['package']) ? $param['package'] :
                $param['info']->getPackage();
            $channel = isset($param['channel']) ? $param['channel'] :
                $param['info']->getChannel();
            if (isset($param['rel'])) {
                if (!class_exists('PEAR_Dependency2')) {
                    require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Dependency2.php';
                }
                $newdep = PEAR_Dependency2::normalizeDep($param);
                $newdep = $newdep[0];
            } elseif (isset($param['min'])) {
                $newdep = $param;
            }
        }
        if (isset($newdep)) {
            if (!isset($newdep['min'])) {
                $newdep['min'] = '0';
            }
            if (!isset($newdep['max'])) {
                $newdep['max'] = '100000000000000000000';
            }
            // use magic to support pecl packages suddenly jumping to the pecl channel
            // we need to support both dependency possibilities
            if ($channel == 'pear.php.net' && $this->getChannel() == 'pecl.php.net') {
                if ($package == $this->getPackage()) {
                    $channel = 'pecl.php.net';
                }
            }
            if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') {
                if ($package == $this->getPackage()) {
                    $channel = 'pear.php.net';
                }
            }
            return (strtolower($package) == strtolower($this->getPackage()) &&
                $channel == $this->getChannel() &&
                version_compare($newdep['min'], $this->getVersion(), '<=') &&
                version_compare($newdep['max'], $this->getVersion(), '>='));
        }
        // use magic to support pecl packages suddenly jumping to the pecl channel
        if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') {
            if (strtolower($package) == strtolower($this->getPackage())) {
                $channel = 'pear.php.net';
            }
        }
        if (isset($param['version'])) {
            return (strtolower($package) == strtolower($this->getPackage()) &&
                $channel == $this->getChannel() &&
                $param['version'] == $this->getVersion());
        } else {
            return strtolower($package) == strtolower($this->getPackage()) &&
                $channel == $this->getChannel();
        }
    }

    function isInstalled($dep, $oper = '==')
    {
        if (!$dep) {
            return false;
        }
        if ($oper != 'ge' && $oper != 'gt' && $oper != 'has' && $oper != '==') {
            return false;
        }
        if (is_object($dep)) {
            $package = $dep->getPackage();
            $channel = $dep->getChannel();
            if ($dep->getURI()) {
                $dep = array(
                    'uri' => $dep->getURI(),
                    'version' => $dep->getVersion(),
                );
            } else {
                $dep = array(
                    'version' => $dep->getVersion(),
                );
            }
        } else {
            if (isset($dep['uri'])) {
                $channel = '__uri';
                $package = $dep['dep']['name'];
            } else {
                $channel = $dep['info']->getChannel();
                $package = $dep['info']->getPackage();
            }
        }
        $options = $this->_downloader->getOptions();
        $test = $this->_installRegistry->packageExists($package, $channel);
        if (!$test && $channel == 'pecl.php.net') {
            // do magic to allow upgrading from old pecl packages to new ones
            $test = $this->_installRegistry->packageExists($package, 'pear.php.net');
            $channel = 'pear.php.net';
        }
        if ($test) {
            if (isset($dep['uri'])) {
                if ($this->_installRegistry->packageInfo($package, 'uri', '__uri') == $dep['uri']) {
                    return true;
                }
            }
            if (isset($options['upgrade'])) {
                if ($oper == 'has') {
                    if (version_compare($this->_installRegistry->packageInfo(
                          $package, 'version', $channel),
                          $dep['version'], '>=')) {
                        return true;
                    } else {
                        return false;
                    }
                } else {
                    if (version_compare($this->_installRegistry->packageInfo(
                          $package, 'version', $channel),
                          $dep['version'], '>=')) {
                        return true;
                    }
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    /**
     * Detect duplicate package names with differing versions
     *
     * If a user requests to install Date 1.4.6 and Date 1.4.7,
     * for instance, this is a logic error.  This method
     * detects this situation.
     *
     * @param array $params array of PEAR_Downloader_Package objects
     * @param array $errorparams empty array
     * @return array array of stupid duplicated packages in PEAR_Downloader_Package obejcts
     */
    function detectStupidDuplicates($params, &$errorparams)
    {
        $existing = array();
        foreach ($params as $i => $param) {
            $package = $param->getPackage();
            $channel = $param->getChannel();
            $group   = $param->getGroup();
            if (!isset($existing[$channel . '/' . $package])) {
                $existing[$channel . '/' . $package] = array();
            }
            if (!isset($existing[$channel . '/' . $package][$group])) {
                $existing[$channel . '/' . $package][$group] = array();
            }
            $existing[$channel . '/' . $package][$group][] = $i;
        }

        $indices = array();
        foreach ($existing as $package => $groups) {
            foreach ($groups as $group => $dupes) {
                if (count($dupes) > 1) {
                    $indices = $indices + $dupes;
                }
            }
        }

        $indices = array_unique($indices);
        foreach ($indices as $index) {
            $errorparams[] = $params[$index];
        }
        return count($errorparams);
    }

    /**
     * @param array
     * @param bool ignore install groups - for final removal of dupe packages
     * @static
     */
    function removeDuplicates(&$params, $ignoreGroups = false)
    {
        $pnames = array();
        foreach ($params as $i => $param) {
            if (!$param) {
                continue;
            }
            if ($param->getPackage()) {
                if ($ignoreGroups) {
                    $group = '';
                } else {
                    $group = $param->getGroup();
                }
                $pnames[$i] = $param->getChannel() . '/' .
                    $param->getPackage() . '-' . $param->getVersion() . '#' . $group;
            }
        }
        $pnames = array_unique($pnames);
        $unset = array_diff(array_keys($params), array_keys($pnames));
        $testp = array_flip($pnames);
        foreach ($params as $i => $param) {
            if (!$param) {
                $unset[] = $i;
                continue;
            }
            if (!is_a($param, 'PEAR_Downloader_Package')) {
                $unset[] = $i;
                continue;
            }
            if ($ignoreGroups) {
                $group = '';
            } else {
                $group = $param->getGroup();
            }
            if (!isset($testp[$param->getChannel() . '/' . $param->getPackage() . '-' .
                  $param->getVersion() . '#' . $group])) {
                $unset[] = $i;
            }
        }
        foreach ($unset as $i) {
            unset($params[$i]);
        }
        $ret = array();
        foreach ($params as $i => $param) {
            $ret[] = &$params[$i];
        }
        $params = array();
        foreach ($ret as $i => $param) {
            $params[] = &$ret[$i];
        }
    }

    function explicitState()
    {
        return $this->_explicitState;
    }

    function setExplicitState($s)
    {
        $this->_explicitState = $s;
    }

    /**
     * @static
     */
    function mergeDependencies(&$params)
    {
        $newparams = array();
        $bundles = array();
        foreach ($params as $i => $param) {
            if (!$param->isBundle()) {
                continue;
            }
            $bundles[] = $i;
            $pf = &$param->getPackageFile();
            $newdeps = array();
            $contents = $pf->getBundledPackages();
            if (!is_array($contents)) {
                $contents = array($contents);
            }
            foreach ($contents as $file) {
                $filecontents = $pf->getFileContents($file);
                $dl = &$param->getDownloader();
                $options = $dl->getOptions();
                if (PEAR::isError($dir = $dl->getDownloadDir())) {
                    return $dir;
                }
                $fp = @fopen($dir . DIRECTORY_SEPARATOR . $file, 'wb');
                if (!$fp) {
                    continue;
                }
                fwrite($fp, $filecontents, strlen($filecontents));
                fclose($fp);
                if ($s = $params[$i]->explicitState()) {
                    $obj->setExplicitState($s);
                }
                $obj = &new PEAR_Downloader_Package($params[$i]->getDownloader());
                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                if (PEAR::isError($dir = $dl->getDownloadDir())) {
                    PEAR::popErrorHandling();
                    return $dir;
                }
                $e = $obj->_fromFile($a = $dir . DIRECTORY_SEPARATOR . $file);
                PEAR::popErrorHandling();
                if (PEAR::isError($e)) {
                    if (!isset($options['soft'])) {
                        $dl->log(0, $e->getMessage());
                    }
                    continue;
                }
                $j = &$obj;
                if (!PEAR_Downloader_Package::willDownload($j,
                      array_merge($params, $newparams)) && !$param->isInstalled($j)) {
                    $newparams[] = &$j;
                }
            }
        }
        foreach ($bundles as $i) {
            unset($params[$i]); // remove bundles - only their contents matter for installation
        }
        PEAR_Downloader_Package::removeDuplicates($params); // strip any unset indices
        if (count($newparams)) { // add in bundled packages for install
            foreach ($newparams as $i => $unused) {
                $params[] = &$newparams[$i];
            }
            $newparams = array();
        }
        foreach ($params as $i => $param) {
            $newdeps = array();
            foreach ($param->_downloadDeps as $dep) {
                if (!PEAR_Downloader_Package::willDownload($dep,
                      array_merge($params, $newparams)) && !$param->isInstalled($dep)) {
                    $newdeps[] = $dep;
                } else {
                    // detect versioning conflicts here
                }
            }
            // convert the dependencies into PEAR_Downloader_Package objects for the next time
            // around
            $params[$i]->_downloadDeps = array();
            foreach ($newdeps as $dep) {
                $obj = &new PEAR_Downloader_Package($params[$i]->getDownloader());
                if ($s = $params[$i]->explicitState()) {
                    $obj->setExplicitState($s);
                }
                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                $e = $obj->fromDepURL($dep);
                PEAR::popErrorHandling();
                if (PEAR::isError($e)) {
                    if (!isset($options['soft'])) {
                        $obj->_downloader->log(0, $e->getMessage());
                    }
                    continue;
                }
                $e = $obj->detectDependencies($params);
                if (PEAR::isError($e)) {
                    if (!isset($options['soft'])) {
                        $obj->_downloader->log(0, $e->getMessage());
                    }
                }
                $j = &$obj;
                $newparams[] = &$j;
            }
        }
        if (count($newparams)) {
            foreach ($newparams as $i => $unused) {
                $params[] = &$newparams[$i];
            }
            return true;
        } else {
            return false;
        }
    }


    /**
     * @static
     */
    function willDownload($param, $params)
    {
        if (!is_array($params)) {
            return false;
        }
        foreach ($params as $obj) {
            if ($obj->isEqual($param)) {
                return true;
            }
        }
        return false;
    }

    /**
     * For simpler unit-testing
     * @param PEAR_Config
     * @param int
     * @param string
     */
    function &getPackagefileObject(&$c, $d, $t = false)
    {
        $a = &new PEAR_PackageFile($c, $d, $t);
        return $a;
    }


    /**
     * This will retrieve from a local file if possible, and parse out
     * a group name as well.  The original parameter will be modified to reflect this.
     * @param string|array can be a parsed package name as well
     * @access private
     */
    function _fromFile(&$param)
    {
        $saveparam = $param;
        if (is_string($param)) {
            if (!@file_exists($param)) {
                $test = explode('#', $param);
                $group = array_pop($test);
                if (@file_exists(implode('#', $test))) {
                    $this->setGroup($group);
                    $param = implode('#', $test);
                    $this->_explicitGroup = true;
                }
            }
            if (@is_file($param)) {
                $this->_type = 'local';
                $options = $this->_downloader->getOptions();
                if (isset($options['downloadonly'])) {
                    $pkg = &$this->getPackagefileObject($this->_config,
                        $this->_downloader->_debug);
                } else {
                    if (PEAR::isError($dir = $this->_downloader->getDownloadDir())) {
                        return $dir;
                    }
                    $pkg = &$this->getPackagefileObject($this->_config,
                        $this->_downloader->_debug, $dir);
                }
                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                $pf = &$pkg->fromAnyFile($param, PEAR_VALIDATE_INSTALLING);
                PEAR::popErrorHandling();
                if (PEAR::isError($pf)) {
                    $this->_valid = false;
                    $param = $saveparam;
                    return $pf;
                }
                $this->_packagefile = &$pf;
                if (!$this->getGroup()) {
                    $this->setGroup('default'); // install the default dependency group
                }
                return $this->_valid = true;
            }
        }
        $param = $saveparam;
        return $this->_valid = false;
    }

    function _fromUrl($param, $saveparam = '')
    {
        if (!is_array($param) &&
              (preg_match('#^(http|ftp)://#', $param))) {
            $options = $this->_downloader->getOptions();
            $this->_type = 'url';
            $callback = $this->_downloader->ui ?
                array(&$this->_downloader, '_downloadCallback') : null;
            $this->_downloader->pushErrorHandling(PEAR_ERROR_RETURN);
            if (PEAR::isError($dir = $this->_downloader->getDownloadDir())) {
                $this->_downloader->popErrorHandling();
                return $dir;
            }
            $this->_downloader->log(3, 'Downloading "' . $param . '"');
            $file = $this->_downloader->downloadHttp($param, $this->_downloader->ui,
                $dir, $callback, null, false, $this->getChannel());
            $this->_downloader->popErrorHandling();
            if (PEAR::isError($file)) {
                if (!empty($saveparam)) {
                    $saveparam = ", cannot download \"$saveparam\"";
                }
                $err = PEAR::raiseError('Could not download from "' . $param .
                    '"' . $saveparam . ' (' . $file->getMessage() . ')');
                    return $err;
            }
            if ($this->_rawpackagefile) {
                require_once 'phar://install-pear-nozlib.phar/' . 'Archive/Tar.php';
                $tar = &new Archive_Tar($file);
                $packagexml = $tar->extractInString('package2.xml');
                if (!$packagexml) {
                    $packagexml = $tar->extractInString('package.xml');
                }
                if (str_replace(array("\n", "\r"), array('',''), $packagexml) !=
                      str_replace(array("\n", "\r"), array('',''), $this->_rawpackagefile)) {
                    if ($this->getChannel() == 'pear.php.net') {
                        // be more lax for the existing PEAR packages that have not-ok
                        // characters in their package.xml
                        $this->_downloader->log(0, 'CRITICAL WARNING: The "' .
                            $this->getPackage() . '" package has invalid characters in its ' .
                            'package.xml.  The next version of PEAR may not be able to install ' .
                            'this package for security reasons.  Please open a bug report at ' .
                            'http://pear.php.net/package/' . $this->getPackage() . '/bugs');
                    } else {
                        return PEAR::raiseError('CRITICAL ERROR: package.xml downloaded does ' .
                            'not match value returned from xml-rpc');
                    }
                }
            }
            // whew, download worked!
            if (isset($options['downloadonly'])) {
                $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->debug);
            } else {
                if (PEAR::isError($dir = $this->_downloader->getDownloadDir())) {
                    return $dir;
                }
                $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->debug,
                    $dir);
            }
            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
            $pf = &$pkg->fromAnyFile($file, PEAR_VALIDATE_INSTALLING);
            PEAR::popErrorHandling();
            if (PEAR::isError($pf)) {
                if (is_array($pf->getUserInfo())) {
                    foreach ($pf->getUserInfo() as $err) {
                        if (is_array($err)) {
                            $err = $err['message'];
                        }
                        if (!isset($options['soft'])) {
                            $this->_downloader->log(0, "Validation Error: $err");
                        }
                    }
                }
                if (!isset($options['soft'])) {
                    $this->_downloader->log(0, $pf->getMessage());
                }
                $err = PEAR::raiseError('Download of "' . ($saveparam ? $saveparam :
                    $param) . '" succeeded, but it is not a valid package archive');
                $this->_valid = false;
                return $err;
            }
            $this->_packagefile = &$pf;
            $this->setGroup('default'); // install the default dependency group
            return $this->_valid = true;
        }
        return $this->_valid = false;
    }

    /**
     *
     * @param string|array pass in an array of format
     *                     array(
     *                      'package' => 'pname',
     *                     ['channel' => 'channame',]
     *                     ['version' => 'version',]
     *                     ['state' => 'state',])
     *                     or a string of format [channame/]pname[-version|-state]
     */
    function _fromString($param)
    {
        $options = $this->_downloader->getOptions();
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        $pname = $this->_registry->parsePackageName($param,
            $this->_config->get('default_channel'));
        PEAR::popErrorHandling();
        if (PEAR::isError($pname)) {
            if ($pname->getCode() == 'invalid') {
                $this->_valid = false;
                return false;
            }
            if ($pname->getCode() == 'channel') {
                $parsed = $pname->getUserInfo();
                if ($this->_downloader->discover($parsed['channel'])) {
                    if ($this->_config->get('auto_discover')) {
                        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                        $pname = $this->_registry->parsePackageName($param,
                            $this->_config->get('default_channel'));
                        PEAR::popErrorHandling();
                    } else {
                        if (!isset($options['soft'])) {
                            $this->_downloader->log(0, 'Channel "' . $parsed['channel'] .
                                '" is not initialized, use ' .
                                '"pear channel-discover ' . $parsed['channel'] . '" to initialize' .
                                'or pear config-set auto_discover 1');
                        }
                    }
                }
                if (PEAR::isError($pname)) {
                    if (!isset($options['soft'])) {
                        $this->_downloader->log(0, $pname->getMessage());
                    }
                    if (is_array($param)) {
                        $param = $this->_registry->parsedPackageNameToString($param);
                    }
                    $err = PEAR::raiseError('invalid package name/package file "' .
                        $param . '"');
                    $this->_valid = false;
                    return $err;
                }
            } else {
                if (!isset($options['soft'])) {
                    $this->_downloader->log(0, $pname->getMessage());
                }
                $err = PEAR::raiseError('invalid package name/package file "' .
                    $param . '"');
                $this->_valid = false;
                return $err;
            }
        }
        if (!isset($this->_type)) {
            $this->_type = 'xmlrpc';
        }
        $this->_parsedname = $pname;
        if (isset($pname['state'])) {
            $this->_explicitState = $pname['state'];
        } else {
            $this->_explicitState = false;
        }
        if (isset($pname['group'])) {
            $this->_explicitGroup = true;
        } else {
            $this->_explicitGroup = false;
        }
        $info = $this->_downloader->_getPackageDownloadUrl($pname);
        if (PEAR::isError($info)) {
            if ($info->getCode() != -976 && $pname['channel'] == 'pear.php.net') {
                // try pecl
                $pname['channel'] = 'pecl.php.net';
                if ($test = $this->_downloader->_getPackageDownloadUrl($pname)) {
                    if (!PEAR::isError($test)) {
                        $info = PEAR::raiseError($info->getMessage() . ' - package ' .
                            $this->_registry->parsedPackageNameToString($pname, true) .
                            ' can be installed with "pecl install ' . $pname['package'] .
                            '"');
                    } else {
                        $pname['channel'] = 'pear.php.net';
                    }
                } else {
                    $pname['channel'] = 'pear.php.net';
                }
            }
            return $info;
        }
        $this->_rawpackagefile = $info['raw'];
        $ret = $this->_analyzeDownloadURL($info, $param, $pname);
        if (PEAR::isError($ret)) {
            return $ret;
        }
        if ($ret) {
            $this->_downloadURL = $ret;
            return $this->_valid = (bool) $ret;
        }
    }

    /**
     * @param array output of package.getDownloadURL
     * @param string|array|object information for detecting packages to be downloaded, and
     *                            for errors
     * @param array name information of the package
     * @param array|null packages to be downloaded
     * @param bool is this an optional dependency?
     * @param bool is this any kind of dependency?
     * @access private
     */
    function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = false,
                                 $isdependency = false)
    {
        if (!is_string($param) && PEAR_Downloader_Package::willDownload($param, $params)) {
            return false;
        }
        if (!$info) {
            if (!is_string($param)) {
                $saveparam = ", cannot download \"$param\"";
            } else {
                $saveparam = '';
            }
            // no releases exist
            return PEAR::raiseError('No releases for package "' .
                $this->_registry->parsedPackageNameToString($pname, true) . '" exist' . $saveparam);
        }
        if (strtolower($info['info']->getChannel()) != strtolower($pname['channel'])) {
            $err = false;
            if ($pname['channel'] == 'pecl.php.net') {
                if ($info['info']->getChannel() != 'pear.php.net') {
                    $err = true;
                }
            } elseif ($info['info']->getChannel() == 'pecl.php.net') {
                if ($pname['channel'] != 'pear.php.net') {
                    $err = true;
                }
            } else {
                $err = true;
            }
            if ($err) {
                return PEAR::raiseError('SECURITY ERROR: package in channel "' . $pname['channel'] .
                    '" retrieved another channel\'s name for download! ("' .
                    $info['info']->getChannel() . '")');
            }
        }
        if (!isset($info['url'])) {
            if ($this->isInstalled($info)) {
                if ($isdependency && version_compare($info['version'],
                      $this->_registry->packageInfo($info['info']->getPackage(),
                            'version', $info['info']->getChannel()), '<=')) {
                    // ignore bogus errors of "failed to download dependency"
                    // if it is already installed and the one that would be
                    // downloaded is older or the same version (Bug #7219)
                    return false;
                }
            }
            $instead =  ', will instead download version ' . $info['version'] .
                        ', stability "' . $info['info']->getState() . '"';
            // releases exist, but we failed to get any
            if (isset($this->_downloader->_options['force'])) {
                if (isset($pname['version'])) {
                    $vs = ', version "' . $pname['version'] . '"';
                } elseif (isset($pname['state'])) {
                    $vs = ', stability "' . $pname['state'] . '"';
                } elseif ($param == 'dependency') {
                    if (!class_exists('PEAR_Common')) {
                        require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Common.php';
                    }
                    if (!in_array($info['info']->getState(),
                          PEAR_Common::betterStates($this->_config->get('preferred_state'), true))) {
                        if ($optional) {
                            // don't spit out confusing error message
                            return $this->_downloader->_getPackageDownloadUrl(
                                array('package' => $pname['package'],
                                      'channel' => $pname['channel'],
                                      'version' => $info['version']));
                        }
                        $vs = ' within preferred state "' . $this->_config->get('preferred_state') .
                            '"';
                    } else {
                        if (!class_exists('PEAR_Dependency2')) {
                            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Dependency2.php';
                        }
                        if ($optional) {
                            // don't spit out confusing error message
                            return $this->_downloader->_getPackageDownloadUrl(
                                array('package' => $pname['package'],
                                      'channel' => $pname['channel'],
                                      'version' => $info['version']));
                        }
                        $vs = PEAR_Dependency2::_getExtraString($pname);
                        $instead = '';
                    }
                } else {
                    $vs = ' within preferred state "' . $this->_config->get(
                        'preferred_state') . '"';
                }
                if (!isset($options['soft'])) {
                    $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
                        '/' . $pname['package'] . $vs . $instead);
                }
                // download the latest release
                return $this->_downloader->_getPackageDownloadUrl(
                    array('package' => $pname['package'],
                          'channel' => $pname['channel'],
                          'version' => $info['version']));
            } else {
                if (isset($info['php']) && $info['php']) {
                    $err = PEAR::raiseError('Failed to download ' .
                        $this->_registry->parsedPackageNameToString(
                            array('channel' => $pname['channel'],
                                  'package' => $pname['package']),
                                true) .
                        ', latest release is version ' . $info['php']['v'] .
                        ', but it requires PHP version "' .
                        $info['php']['m'] . '", use "' .
                        $this->_registry->parsedPackageNameToString(
                            array('channel' => $pname['channel'], 'package' => $pname['package'],
                            'version' => $info['php']['v'])) . '" to install',
                            PEAR_DOWNLOADER_PACKAGE_PHPVERSION);
                    return $err;
                }
                // construct helpful error message
                if (isset($pname['version'])) {
                    $vs = ', version "' . $pname['version'] . '"';
                } elseif (isset($pname['state'])) {
                    $vs = ', stability "' . $pname['state'] . '"';
                } elseif ($param == 'dependency') {
                    if (!class_exists('PEAR_Common')) {
                        require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Common.php';
                    }
                    if (!in_array($info['info']->getState(),
                          PEAR_Common::betterStates($this->_config->get('preferred_state'), true))) {
                        if ($optional) {
                            // don't spit out confusing error message, and don't die on
                            // optional dep failure!
                            return $this->_downloader->_getPackageDownloadUrl(
                                array('package' => $pname['package'],
                                      'channel' => $pname['channel'],
                                      'version' => $info['version']));
                        }
                        $vs = ' within preferred state "' . $this->_config->get('preferred_state') .
                            '"';
                    } else {
                        if (!class_exists('PEAR_Dependency2')) {
                            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Dependency2.php';
                        }
                        if ($optional) {
                            // don't spit out confusing error message, and don't die on
                            // optional dep failure!
                            return $this->_downloader->_getPackageDownloadUrl(
                                array('package' => $pname['package'],
                                      'channel' => $pname['channel'],
                                      'version' => $info['version']));
                        }
                        $vs = PEAR_Dependency2::_getExtraString($pname);
                    }
                } else {
                    $vs = ' within preferred state "' . $this->_downloader->config->get(
                        'preferred_state') . '"';
                }
                $options = $this->_downloader->getOptions();
                // this is only set by the "download-all" command
                if (isset($options['ignorepreferred_state'])) {
                    $err = PEAR::raiseError(
                        'Failed to download ' . $this->_registry->parsedPackageNameToString(
                            array('channel' => $pname['channel'], 'package' => $pname['package']),
                                true)
                         . $vs .
                        ', latest release is version ' . $info['version'] .
                        ', stability "' . $info['info']->getState() . '", use "' .
                        $this->_registry->parsedPackageNameToString(
                            array('channel' => $pname['channel'], 'package' => $pname['package'],
                            'version' => $info['version'])) . '" to install',
                            PEAR_DOWNLOADER_PACKAGE_STATE);
                    return $err;
                }
                $err = PEAR::raiseError(
                    'Failed to download ' . $this->_registry->parsedPackageNameToString(
                        array('channel' => $pname['channel'], 'package' => $pname['package']),
                            true)
                     . $vs .
                    ', latest release is version ' . $info['version'] .
                    ', stability "' . $info['info']->getState() . '", use "' .
                    $this->_registry->parsedPackageNameToString(
                        array('channel' => $pname['channel'], 'package' => $pname['package'],
                        'version' => $info['version'])) . '" to install');
                return $err;
            }
        }
        if (isset($info['deprecated']) && $info['deprecated']) {
            $this->_downloader->log(0,
                'WARNING: "' .
                    $this->_registry->parsedPackageNameToString(
                            array('channel' => $info['info']->getChannel(),
                                  'package' => $info['info']->getPackage()), true) .
                '" is deprecated in favor of "' .
                    $this->_registry->parsedPackageNameToString($info['deprecated'], true) .
                '"');
        }
        return $info;
    }
}
?>
<?php
/**
 * Error Stack Implementation
 * 
 * This is an incredibly simple implementation of a very complex error handling
 * facility.  It contains the ability
 * to track multiple errors from multiple packages simultaneously.  In addition,
 * it can track errors of many levels, save data along with the error, context
 * information such as the exact file, line number, class and function that
 * generated the error, and if necessary, it can raise a traditional PEAR_Error.
 * It has built-in support for PEAR::Log, to log errors as they occur
 * 
 * Since version 0.2alpha, it is also possible to selectively ignore errors,
 * through the use of an error callback, see {@link pushCallback()}
 * 
 * Since version 0.3alpha, it is possible to specify the exception class
 * returned from {@link push()}
 *
 * Since version PEAR1.3.2, ErrorStack no longer instantiates an exception class.  This can
 * still be done quite handily in an error callback or by manipulating the returned array
 * @category   Debugging
 * @package    PEAR_ErrorStack
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  2004-2008 Greg Beaver
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: ErrorStack.php,v 1.28 2008/01/03 20:26:35 cellog Exp $
 * @link       http://pear.php.net/package/PEAR_ErrorStack
 */

/**
 * Singleton storage
 * 
 * Format:
 * <pre>
 * array(
 *  'package1' => PEAR_ErrorStack object,
 *  'package2' => PEAR_ErrorStack object,
 *  ...
 * )
 * </pre>
 * @access private
 * @global array $GLOBALS['_PEAR_ERRORSTACK_SINGLETON']
 */
$GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array();

/**
 * Global error callback (default)
 * 
 * This is only used if set to non-false.  * is the default callback for
 * all packages, whereas specific packages may set a default callback
 * for all instances, regardless of whether they are a singleton or not.
 *
 * To exclude non-singletons, only set the local callback for the singleton
 * @see PEAR_ErrorStack::setDefaultCallback()
 * @access private
 * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']
 */
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = array(
    '*' => false,
);

/**
 * Global Log object (default)
 * 
 * This is only used if set to non-false.  Use to set a default log object for
 * all stacks, regardless of instantiation order or location
 * @see PEAR_ErrorStack::setDefaultLogger()
 * @access private
 * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
 */
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = false;

/**
 * Global Overriding Callback
 * 
 * This callback will override any error callbacks that specific loggers have set.
 * Use with EXTREME caution
 * @see PEAR_ErrorStack::staticPushCallback()
 * @access private
 * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
 */
$GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();

/**#@+
 * One of four possible return values from the error Callback
 * @see PEAR_ErrorStack::_errorCallback()
 */
/**
 * If this is returned, then the error will be both pushed onto the stack
 * and logged.
 */
define('PEAR_ERRORSTACK_PUSHANDLOG', 1);
/**
 * If this is returned, then the error will only be pushed onto the stack,
 * and not logged.
 */
define('PEAR_ERRORSTACK_PUSH', 2);
/**
 * If this is returned, then the error will only be logged, but not pushed
 * onto the error stack.
 */
define('PEAR_ERRORSTACK_LOG', 3);
/**
 * If this is returned, then the error is completely ignored.
 */
define('PEAR_ERRORSTACK_IGNORE', 4);
/**
 * If this is returned, then the error is logged and die() is called.
 */
define('PEAR_ERRORSTACK_DIE', 5);
/**#@-*/

/**
 * Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in
 * the singleton method.
 */
define('PEAR_ERRORSTACK_ERR_NONCLASS', 1);

/**
 * Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()}
 * that has no __toString() method
 */
define('PEAR_ERRORSTACK_ERR_OBJTOSTRING', 2);
/**
 * Error Stack Implementation
 *
 * Usage:
 * <code>
 * // global error stack
 * $global_stack = &PEAR_ErrorStack::singleton('MyPackage');
 * // local error stack
 * $local_stack = new PEAR_ErrorStack('MyPackage');
 * </code>
 * @author     Greg Beaver <cellog@php.net>
 * @version    1.7.2
 * @package    PEAR_ErrorStack
 * @category   Debugging
 * @copyright  2004-2008 Greg Beaver
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: ErrorStack.php,v 1.28 2008/01/03 20:26:35 cellog Exp $
 * @link       http://pear.php.net/package/PEAR_ErrorStack
 */
class PEAR_ErrorStack {
    /**
     * Errors are stored in the order that they are pushed on the stack.
     * @since 0.4alpha Errors are no longer organized by error level.
     * This renders pop() nearly unusable, and levels could be more easily
     * handled in a callback anyway
     * @var array
     * @access private
     */
    var $_errors = array();

    /**
     * Storage of errors by level.
     *
     * Allows easy retrieval and deletion of only errors from a particular level
     * @since PEAR 1.4.0dev
     * @var array
     * @access private
     */
    var $_errorsByLevel = array();

    /**
     * Package name this error stack represents
     * @var string
     * @access protected
     */
    var $_package;
    
    /**
     * Determines whether a PEAR_Error is thrown upon every error addition
     * @var boolean
     * @access private
     */
    var $_compat = false;
    
    /**
     * If set to a valid callback, this will be used to generate the error
     * message from the error code, otherwise the message passed in will be
     * used
     * @var false|string|array
     * @access private
     */
    var $_msgCallback = false;
    
    /**
     * If set to a valid callback, this will be used to generate the error
     * context for an error.  For PHP-related errors, this will be a file
     * and line number as retrieved from debug_backtrace(), but can be
     * customized for other purposes.  The error might actually be in a separate
     * configuration file, or in a database query.
     * @var false|string|array
     * @access protected
     */
    var $_contextCallback = false;
    
    /**
     * If set to a valid callback, this will be called every time an error
     * is pushed onto the stack.  The return value will be used to determine
     * whether to allow an error to be pushed or logged.
     * 
     * The return value must be one an PEAR_ERRORSTACK_* constant
     * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
     * @var false|string|array
     * @access protected
     */
    var $_errorCallback = array();
    
    /**
     * PEAR::Log object for logging errors
     * @var false|Log
     * @access protected
     */
    var $_logger = false;
    
    /**
     * Error messages - designed to be overridden
     * @var array
     * @abstract
     */
    var $_errorMsgs = array();
    
    /**
     * Set up a new error stack
     * 
     * @param string   $package name of the package this error stack represents
     * @param callback $msgCallback callback used for error message generation
     * @param callback $contextCallback callback used for context generation,
     *                 defaults to {@link getFileLine()}
     * @param boolean  $throwPEAR_Error
     */
    function PEAR_ErrorStack($package, $msgCallback = false, $contextCallback = false,
                         $throwPEAR_Error = false)
    {
        $this->_package = $package;
        $this->setMessageCallback($msgCallback);
        $this->setContextCallback($contextCallback);
        $this->_compat = $throwPEAR_Error;
    }
    
    /**
     * Return a single error stack for this package.
     * 
     * Note that all parameters are ignored if the stack for package $package
     * has already been instantiated
     * @param string   $package name of the package this error stack represents
     * @param callback $msgCallback callback used for error message generation
     * @param callback $contextCallback callback used for context generation,
     *                 defaults to {@link getFileLine()}
     * @param boolean  $throwPEAR_Error
     * @param string   $stackClass class to instantiate
     * @static
     * @return PEAR_ErrorStack
     */
    function &singleton($package, $msgCallback = false, $contextCallback = false,
                         $throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack')
    {
        if (isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
            return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package];
        }
        if (!class_exists($stackClass)) {
            if (function_exists('debug_backtrace')) {
                $trace = debug_backtrace();
            }
            PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_NONCLASS,
                'exception', array('stackclass' => $stackClass),
                'stack class "%stackclass%" is not a valid class name (should be like PEAR_ErrorStack)',
                false, $trace);
        }
        $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package] =
            new $stackClass($package, $msgCallback, $contextCallback, $throwPEAR_Error);

        return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package];
    }

    /**
     * Internal error handler for PEAR_ErrorStack class
     * 
     * Dies if the error is an exception (and would have died anyway)
     * @access private
     */
    function _handleError($err)
    {
        if ($err['level'] == 'exception') {
            $message = $err['message'];
            if (isset($_SERVER['REQUEST_URI'])) {
                echo '<br />';
            } else {
                echo "\n";
            }
            var_dump($err['context']);
            die($message);
        }
    }
    
    /**
     * Set up a PEAR::Log object for all error stacks that don't have one
     * @param Log $log 
     * @static
     */
    function setDefaultLogger(&$log)
    {
        if (is_object($log) && method_exists($log, 'log') ) {
            $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
        } elseif (is_callable($log)) {
            $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
	}
    }
    
    /**
     * Set up a PEAR::Log object for this error stack
     * @param Log $log 
     */
    function setLogger(&$log)
    {
        if (is_object($log) && method_exists($log, 'log') ) {
            $this->_logger = &$log;
        } elseif (is_callable($log)) {
            $this->_logger = &$log;
        }
    }
    
    /**
     * Set an error code => error message mapping callback
     * 
     * This method sets the callback that can be used to generate error
     * messages for any instance
     * @param array|string Callback function/method
     */
    function setMessageCallback($msgCallback)
    {
        if (!$msgCallback) {
            $this->_msgCallback = array(&$this, 'getErrorMessage');
        } else {
            if (is_callable($msgCallback)) {
                $this->_msgCallback = $msgCallback;
            }
        }
    }
    
    /**
     * Get an error code => error message mapping callback
     * 
     * This method returns the current callback that can be used to generate error
     * messages
     * @return array|string|false Callback function/method or false if none
     */
    function getMessageCallback()
    {
        return $this->_msgCallback;
    }
    
    /**
     * Sets a default callback to be used by all error stacks
     * 
     * This method sets the callback that can be used to generate error
     * messages for a singleton
     * @param array|string Callback function/method
     * @param string Package name, or false for all packages
     * @static
     */
    function setDefaultCallback($callback = false, $package = false)
    {
        if (!is_callable($callback)) {
            $callback = false;
        }
        $package = $package ? $package : '*';
        $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$package] = $callback;
    }
    
    /**
     * Set a callback that generates context information (location of error) for an error stack
     * 
     * This method sets the callback that can be used to generate context
     * information for an error.  Passing in NULL will disable context generation
     * and remove the expensive call to debug_backtrace()
     * @param array|string|null Callback function/method
     */
    function setContextCallback($contextCallback)
    {
        if ($contextCallback === null) {
            return $this->_contextCallback = false;
        }
        if (!$contextCallback) {
            $this->_contextCallback = array(&$this, 'getFileLine');
        } else {
            if (is_callable($contextCallback)) {
                $this->_contextCallback = $contextCallback;
            }
        }
    }
    
    /**
     * Set an error Callback
     * If set to a valid callback, this will be called every time an error
     * is pushed onto the stack.  The return value will be used to determine
     * whether to allow an error to be pushed or logged.
     * 
     * The return value must be one of the ERRORSTACK_* constants.
     * 
     * This functionality can be used to emulate PEAR's pushErrorHandling, and
     * the PEAR_ERROR_CALLBACK mode, without affecting the integrity of
     * the error stack or logging
     * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
     * @see popCallback()
     * @param string|array $cb
     */
    function pushCallback($cb)
    {
        array_push($this->_errorCallback, $cb);
    }
    
    /**
     * Remove a callback from the error callback stack
     * @see pushCallback()
     * @return array|string|false
     */
    function popCallback()
    {
        if (!count($this->_errorCallback)) {
            return false;
        }
        return array_pop($this->_errorCallback);
    }
    
    /**
     * Set a temporary overriding error callback for every package error stack
     *
     * Use this to temporarily disable all existing callbacks (can be used
     * to emulate the @ operator, for instance)
     * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
     * @see staticPopCallback(), pushCallback()
     * @param string|array $cb
     * @static
     */
    function staticPushCallback($cb)
    {
        array_push($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'], $cb);
    }
    
    /**
     * Remove a temporary overriding error callback
     * @see staticPushCallback()
     * @return array|string|false
     * @static
     */
    function staticPopCallback()
    {
        $ret = array_pop($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK']);
        if (!is_array($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'])) {
            $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
        }
        return $ret;
    }
    
    /**
     * Add an error to the stack
     * 
     * If the message generator exists, it is called with 2 parameters.
     *  - the current Error Stack object
     *  - an array that is in the same format as an error.  Available indices
     *    are 'code', 'package', 'time', 'params', 'level', and 'context'
     * 
     * Next, if the error should contain context information, this is
     * handled by the context grabbing method.
     * Finally, the error is pushed onto the proper error stack
     * @param int    $code      Package-specific error code
     * @param string $level     Error level.  This is NOT spell-checked
     * @param array  $params    associative array of error parameters
     * @param string $msg       Error message, or a portion of it if the message
     *                          is to be generated
     * @param array  $repackage If this error re-packages an error pushed by
     *                          another package, place the array returned from
     *                          {@link pop()} in this parameter
     * @param array  $backtrace Protected parameter: use this to pass in the
     *                          {@link debug_backtrace()} that should be used
     *                          to find error context
     * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also
     * thrown.  If a PEAR_Error is returned, the userinfo
     * property is set to the following array:
     * 
     * <code>
     * array(
     *    'code' => $code,
     *    'params' => $params,
     *    'package' => $this->_package,
     *    'level' => $level,
     *    'time' => time(),
     *    'context' => $context,
     *    'message' => $msg,
     * //['repackage' => $err] repackaged error array/Exception class
     * );
     * </code>
     * 
     * Normally, the previous array is returned.
     */
    function push($code, $level = 'error', $params = array(), $msg = false,
                  $repackage = false, $backtrace = false)
    {
        $context = false;
        // grab error context
        if ($this->_contextCallback) {
            if (!$backtrace) {
                $backtrace = debug_backtrace();
            }
            $context = call_user_func($this->_contextCallback, $code, $params, $backtrace);
        }
        
        // save error
        $time = explode(' ', microtime());
        $time = $time[1] + $time[0];
        $err = array(
                'code' => $code,
                'params' => $params,
                'package' => $this->_package,
                'level' => $level,
                'time' => $time,
                'context' => $context,
                'message' => $msg,
               );

        if ($repackage) {
            $err['repackage'] = $repackage;
        }

        // set up the error message, if necessary
        if ($this->_msgCallback) {
            $msg = call_user_func_array($this->_msgCallback,
                                        array(&$this, $err));
            $err['message'] = $msg;
        }        
        $push = $log = true;
        $die = false;
        // try the overriding callback first
        $callback = $this->staticPopCallback();
        if ($callback) {
            $this->staticPushCallback($callback);
        }
        if (!is_callable($callback)) {
            // try the local callback next
            $callback = $this->popCallback();
            if (is_callable($callback)) {
                $this->pushCallback($callback);
            } else {
                // try the default callback
                $callback = isset($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package]) ?
                    $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package] :
                    $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']['*'];
            }
        }
        if (is_callable($callback)) {
            switch(call_user_func($callback, $err)){
            	case PEAR_ERRORSTACK_IGNORE: 
            		return $err;
        		break;
            	case PEAR_ERRORSTACK_PUSH: 
            		$log = false;
        		break;
            	case PEAR_ERRORSTACK_LOG: 
            		$push = false;
        		break;
            	case PEAR_ERRORSTACK_DIE: 
            		$die = true;
        		break;
                // anything else returned has the same effect as pushandlog
            }
        }
        if ($push) {
            array_unshift($this->_errors, $err);
            if (!isset($this->_errorsByLevel[$err['level']])) {
                $this->_errorsByLevel[$err['level']] = array();
            }
            $this->_errorsByLevel[$err['level']][] = &$this->_errors[0];
        }
        if ($log) {
            if ($this->_logger || $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']) {
                $this->_log($err);
            }
        }
        if ($die) {
            die();
        }
        if ($this->_compat && $push) {
            return $this->raiseError($msg, $code, null, null, $err);
        }
        return $err;
    }
    
    /**
     * Static version of {@link push()}
     * 
     * @param string $package   Package name this error belongs to
     * @param int    $code      Package-specific error code
     * @param string $level     Error level.  This is NOT spell-checked
     * @param array  $params    associative array of error parameters
     * @param string $msg       Error message, or a portion of it if the message
     *                          is to be generated
     * @param array  $repackage If this error re-packages an error pushed by
     *                          another package, place the array returned from
     *                          {@link pop()} in this parameter
     * @param array  $backtrace Protected parameter: use this to pass in the
     *                          {@link debug_backtrace()} that should be used
     *                          to find error context
     * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also
     *                          thrown.  see docs for {@link push()}
     * @static
     */
    function staticPush($package, $code, $level = 'error', $params = array(),
                        $msg = false, $repackage = false, $backtrace = false)
    {
        $s = &PEAR_ErrorStack::singleton($package);
        if ($s->_contextCallback) {
            if (!$backtrace) {
                if (function_exists('debug_backtrace')) {
                    $backtrace = debug_backtrace();
                }
            }
        }
        return $s->push($code, $level, $params, $msg, $repackage, $backtrace);
    }
    
    /**
     * Log an error using PEAR::Log
     * @param array $err Error array
     * @param array $levels Error level => Log constant map
     * @access protected
     */
    function _log($err)
    {
        if ($this->_logger) {
            $logger = &$this->_logger;
        } else {
            $logger = &$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'];
        }
        if (is_a($logger, 'Log')) {
            $levels = array(
                'exception' => PEAR_LOG_CRIT,
                'alert' => PEAR_LOG_ALERT,
                'critical' => PEAR_LOG_CRIT,
                'error' => PEAR_LOG_ERR,
                'warning' => PEAR_LOG_WARNING,
                'notice' => PEAR_LOG_NOTICE,
                'info' => PEAR_LOG_INFO,
                'debug' => PEAR_LOG_DEBUG);
            if (isset($levels[$err['level']])) {
                $level = $levels[$err['level']];
            } else {
                $level = PEAR_LOG_INFO;
            }
            $logger->log($err['message'], $level, $err);
        } else { // support non-standard logs
            call_user_func($logger, $err);
        }
    }

    
    /**
     * Pop an error off of the error stack
     * 
     * @return false|array
     * @since 0.4alpha it is no longer possible to specify a specific error
     * level to return - the last error pushed will be returned, instead
     */
    function pop()
    {
        $err = @array_shift($this->_errors);
        if (!is_null($err)) {
            @array_pop($this->_errorsByLevel[$err['level']]);
            if (!count($this->_errorsByLevel[$err['level']])) {
                unset($this->_errorsByLevel[$err['level']]);
            }
        }
        return $err;
    }

    /**
     * Pop an error off of the error stack, static method
     *
     * @param string package name
     * @return boolean
     * @since PEAR1.5.0a1
     */
    function staticPop($package)
    {
        if ($package) {
            if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
                return false;
            }
            return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->pop();
        }
    }

    /**
     * Determine whether there are any errors on the stack
     * @param string|array Level name.  Use to determine if any errors
     * of level (string), or levels (array) have been pushed
     * @return boolean
     */
    function hasErrors($level = false)
    {
        if ($level) {
            return isset($this->_errorsByLevel[$level]);
        }
        return count($this->_errors);
    }
    
    /**
     * Retrieve all errors since last purge
     * 
     * @param boolean set in order to empty the error stack
     * @param string level name, to return only errors of a particular severity
     * @return array
     */
    function getErrors($purge = false, $level = false)
    {
        if (!$purge) {
            if ($level) {
                if (!isset($this->_errorsByLevel[$level])) {
                    return array();
                } else {
                    return $this->_errorsByLevel[$level];
                }
            } else {
                return $this->_errors;
            }
        }
        if ($level) {
            $ret = $this->_errorsByLevel[$level];
            foreach ($this->_errorsByLevel[$level] as $i => $unused) {
                // entries are references to the $_errors array
                $this->_errorsByLevel[$level][$i] = false;
            }
            // array_filter removes all entries === false
            $this->_errors = array_filter($this->_errors);
            unset($this->_errorsByLevel[$level]);
            return $ret;
        }
        $ret = $this->_errors;
        $this->_errors = array();
        $this->_errorsByLevel = array();
        return $ret;
    }
    
    /**
     * Determine whether there are any errors on a single error stack, or on any error stack
     *
     * The optional parameter can be used to test the existence of any errors without the need of
     * singleton instantiation
     * @param string|false Package name to check for errors
     * @param string Level name to check for a particular severity
     * @return boolean
     * @static
     */
    function staticHasErrors($package = false, $level = false)
    {
        if ($package) {
            if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
                return false;
            }
            return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->hasErrors($level);
        }
        foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
            if ($obj->hasErrors($level)) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * Get a list of all errors since last purge, organized by package
     * @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be
     * @param boolean $purge Set to purge the error stack of existing errors
     * @param string  $level Set to a level name in order to retrieve only errors of a particular level
     * @param boolean $merge Set to return a flat array, not organized by package
     * @param array   $sortfunc Function used to sort a merged array - default
     *        sorts by time, and should be good for most cases
     * @static
     * @return array 
     */
    function staticGetErrors($purge = false, $level = false, $merge = false,
                             $sortfunc = array('PEAR_ErrorStack', '_sortErrors'))
    {
        $ret = array();
        if (!is_callable($sortfunc)) {
            $sortfunc = array('PEAR_ErrorStack', '_sortErrors');
        }
        foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
            $test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge, $level);
            if ($test) {
                if ($merge) {
                    $ret = array_merge($ret, $test);
                } else {
                    $ret[$package] = $test;
                }
            }
        }
        if ($merge) {
            usort($ret, $sortfunc);
        }
        return $ret;
    }
    
    /**
     * Error sorting function, sorts by time
     * @access private
     */
    function _sortErrors($a, $b)
    {
        if ($a['time'] == $b['time']) {
            return 0;
        }
        if ($a['time'] < $b['time']) {
            return 1;
        }
        return -1;
    }

    /**
     * Standard file/line number/function/class context callback
     *
     * This function uses a backtrace generated from {@link debug_backtrace()}
     * and so will not work at all in PHP < 4.3.0.  The frame should
     * reference the frame that contains the source of the error.
     * @return array|false either array('file' => file, 'line' => line,
     *         'function' => function name, 'class' => class name) or
     *         if this doesn't work, then false
     * @param unused
     * @param integer backtrace frame.
     * @param array Results of debug_backtrace()
     * @static
     */
    function getFileLine($code, $params, $backtrace = null)
    {
        if ($backtrace === null) {
            return false;
        }
        $frame = 0;
        $functionframe = 1;
        if (!isset($backtrace[1])) {
            $functionframe = 0;
        } else {
            while (isset($backtrace[$functionframe]['function']) &&
                  $backtrace[$functionframe]['function'] == 'eval' &&
                  isset($backtrace[$functionframe + 1])) {
                $functionframe++;
            }
        }
        if (isset($backtrace[$frame])) {
            if (!isset($backtrace[$frame]['file'])) {
                $frame++;
            }
            $funcbacktrace = $backtrace[$functionframe];
            $filebacktrace = $backtrace[$frame];
            $ret = array('file' => $filebacktrace['file'],
                         'line' => $filebacktrace['line']);
            // rearrange for eval'd code or create function errors
            if (strpos($filebacktrace['file'], '(') && 
            	  preg_match(';^(.*?)\((\d+)\) : (.*?)\\z;', $filebacktrace['file'],
                  $matches)) {
                $ret['file'] = $matches[1];
                $ret['line'] = $matches[2] + 0;
            }
            if (isset($funcbacktrace['function']) && isset($backtrace[1])) {
                if ($funcbacktrace['function'] != 'eval') {
                    if ($funcbacktrace['function'] == '__lambda_func') {
                        $ret['function'] = 'create_function() code';
                    } else {
                        $ret['function'] = $funcbacktrace['function'];
                    }
                }
            }
            if (isset($funcbacktrace['class']) && isset($backtrace[1])) {
                $ret['class'] = $funcbacktrace['class'];
            }
            return $ret;
        }
        return false;
    }
    
    /**
     * Standard error message generation callback
     * 
     * This method may also be called by a custom error message generator
     * to fill in template values from the params array, simply
     * set the third parameter to the error message template string to use
     * 
     * The special variable %__msg% is reserved: use it only to specify
     * where a message passed in by the user should be placed in the template,
     * like so:
     * 
     * Error message: %msg% - internal error
     * 
     * If the message passed like so:
     * 
     * <code>
     * $stack->push(ERROR_CODE, 'error', array(), 'server error 500');
     * </code>
     * 
     * The returned error message will be "Error message: server error 500 -
     * internal error"
     * @param PEAR_ErrorStack
     * @param array
     * @param string|false Pre-generated error message template
     * @static
     * @return string
     */
    function getErrorMessage(&$stack, $err, $template = false)
    {
        if ($template) {
            $mainmsg = $template;
        } else {
            $mainmsg = $stack->getErrorMessageTemplate($err['code']);
        }
        $mainmsg = str_replace('%__msg%', $err['message'], $mainmsg);
        if (is_array($err['params']) && count($err['params'])) {
            foreach ($err['params'] as $name => $val) {
                if (is_array($val)) {
                    // @ is needed in case $val is a multi-dimensional array
                    $val = @implode(', ', $val);
                }
                if (is_object($val)) {
                    if (method_exists($val, '__toString')) {
                        $val = $val->__toString();
                    } else {
                        PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING,
                            'warning', array('obj' => get_class($val)),
                            'object %obj% passed into getErrorMessage, but has no __toString() method');
                        $val = 'Object';
                    }
                }
                $mainmsg = str_replace('%' . $name . '%', $val, $mainmsg);
            }
        }
        return $mainmsg;
    }
    
    /**
     * Standard Error Message Template generator from code
     * @return string
     */
    function getErrorMessageTemplate($code)
    {
        if (!isset($this->_errorMsgs[$code])) {
            return '%__msg%';
        }
        return $this->_errorMsgs[$code];
    }
    
    /**
     * Set the Error Message Template array
     * 
     * The array format must be:
     * <pre>
     * array(error code => 'message template',...)
     * </pre>
     * 
     * Error message parameters passed into {@link push()} will be used as input
     * for the error message.  If the template is 'message %foo% was %bar%', and the
     * parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will
     * be 'message one was six'
     * @return string
     */
    function setErrorMessageTemplate($template)
    {
        $this->_errorMsgs = $template;
    }
    
    
    /**
     * emulate PEAR::raiseError()
     * 
     * @return PEAR_Error
     */
    function raiseError()
    {
        require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
        $args = func_get_args();
        return call_user_func_array(array('PEAR', 'raiseError'), $args);
    }
}
$stack = &PEAR_ErrorStack::singleton('PEAR_ErrorStack');
$stack->pushCallback(array('PEAR_ErrorStack', '_handleError'));
?>
<?php
/**
 * PEAR_Frontend, the singleton-based frontend for user input/output
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Frontend.php,v 1.13 2008/01/03 20:26:35 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * Which user interface class is being used.
 * @var string class name
 */
$GLOBALS['_PEAR_FRONTEND_CLASS'] = 'PEAR_Frontend_CLI';

/**
 * Instance of $_PEAR_Command_uiclass.
 * @var object
 */
$GLOBALS['_PEAR_FRONTEND_SINGLETON'] = null;

/**
 * Singleton-based frontend for PEAR user input/output
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Frontend extends PEAR
{
    /**
     * Retrieve the frontend object
     * @return PEAR_Frontend_CLI|PEAR_Frontend_Web|PEAR_Frontend_Gtk
     * @static
     */
    function &singleton($type = null)
    {
        if ($type === null) {
            if (!isset($GLOBALS['_PEAR_FRONTEND_SINGLETON'])) {
                $a = false;
                return $a;
            }
            return $GLOBALS['_PEAR_FRONTEND_SINGLETON'];
        } else {
            $a = PEAR_Frontend::setFrontendClass($type);
            return $a;
        }
    }

    /**
     * Set the frontend class that will be used by calls to {@link singleton()}
     *
     * Frontends are expected to conform to the PEAR naming standard of
     * _ => DIRECTORY_SEPARATOR (PEAR_Frontend_CLI is in PEAR/Frontend/CLI.php)
     * @param string $uiclass full class name
     * @return PEAR_Frontend
     * @static
     */
    function &setFrontendClass($uiclass)
    {
        if (is_object($GLOBALS['_PEAR_FRONTEND_SINGLETON']) &&
              is_a($GLOBALS['_PEAR_FRONTEND_SINGLETON'], $uiclass)) {
            return $GLOBALS['_PEAR_FRONTEND_SINGLETON'];
        }
        if (!class_exists($uiclass)) {
            $file = 'phar://install-pear-nozlib.phar/' . str_replace('_', '/', $uiclass) . '.php';
            if (PEAR_Frontend::isIncludeable($file)) {
                include_once $file;
            }
        }
        if (class_exists($uiclass)) {
            $obj = &new $uiclass;
            // quick test to see if this class implements a few of the most
            // important frontend methods
            if (is_a($obj, 'PEAR_Frontend')) {
                $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = &$obj;
                $GLOBALS['_PEAR_FRONTEND_CLASS'] = $uiclass;
                return $obj;
            } else {
                $err = PEAR::raiseError("not a frontend class: $uiclass");
                return $err;
            }
        }
        $err = PEAR::raiseError("no such class: $uiclass");
        return $err;
    }

    /**
     * Set the frontend class that will be used by calls to {@link singleton()}
     *
     * Frontends are expected to be a descendant of PEAR_Frontend
     * @param PEAR_Frontend
     * @return PEAR_Frontend
     * @static
     */
    function &setFrontendObject($uiobject)
    {
        if (is_object($GLOBALS['_PEAR_FRONTEND_SINGLETON']) &&
              is_a($GLOBALS['_PEAR_FRONTEND_SINGLETON'], get_class($uiobject))) {
            return $GLOBALS['_PEAR_FRONTEND_SINGLETON'];
        }
        if (!is_a($uiobject, 'PEAR_Frontend')) {
            $err = PEAR::raiseError('not a valid frontend class: (' .
                get_class($uiobject) . ')');
            return $err;
        }
        $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = &$uiobject;
        $GLOBALS['_PEAR_FRONTEND_CLASS'] = get_class($uiobject);
        return $uiobject;
    }

    /**
     * @param string $path relative or absolute include path
     * @return boolean
     * @static
     */
    function isIncludeable($path)
    {
        if (file_exists($path) && is_readable($path)) {
            return true;
        }
        $fp = @fopen($path, 'r', true);
        if ($fp) {
            fclose($fp);
            return true;
        }
        return false;
    }

    /**
     * @param PEAR_Config
     */
    function setConfig(&$config)
    {
    }

    /**
     * This can be overridden to allow session-based temporary file management
     *
     * By default, all files are deleted at the end of a session.  The web installer
     * needs to be able to sustain a list over many sessions in order to support
     * user interaction with install scripts
     */
    function addTempFile($file)
    {
        $GLOBALS['_PEAR_Common_tempfiles'][] = $file;
    }

    /**
     * Log an action
     *
     * @param string $msg the message to log
     * @param boolean $append_crlf
     * @return boolean true
     * @abstract
     */
    function log($msg, $append_crlf = true)
    {
    }

    /**
     * Run a post-installation script
     *
     * @param array $scripts array of post-install scripts
     * @abstract
     */
    function runPostinstallScripts(&$scripts)
    {
    }

    /**
     * Display human-friendly output formatted depending on the
     * $command parameter.
     *
     * This should be able to handle basic output data with no command
     * @param mixed  $data    data structure containing the information to display
     * @param string $command command from which this method was called
     * @abstract
     */
    function outputData($data, $command = '_default')
    {
    }

    /**
     * Display a modal form dialog and return the given input
     *
     * A frontend that requires multiple requests to retrieve and process
     * data must take these needs into account, and implement the request
     * handling code.
     * @param string $command  command from which this method was called
     * @param array  $prompts  associative array. keys are the input field names
     *                         and values are the description
     * @param array  $types    array of input field types (text, password,
     *                         etc.) keys have to be the same like in $prompts
     * @param array  $defaults array of default values. again keys have
     *                         to be the same like in $prompts.  Do not depend
     *                         on a default value being set.
     * @return array input sent by the user
     * @abstract
     */
    function userDialog($command, $prompts, $types = array(), $defaults = array())
    {
    }
}
?><?php
/**
 * PEAR_Frontend_CLI
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: CLI.php,v 1.68 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */
/**
 * base class
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Frontend.php';

/**
 * Command-line Frontend for the PEAR Installer
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */
class PEAR_Frontend_CLI extends PEAR_Frontend
{
    // {{{ properties

    /**
     * What type of user interface this frontend is for.
     * @var string
     * @access public
     */
    var $type = 'CLI';
    var $lp = ''; // line prefix

    var $params = array();
    var $term = array(
        'bold' => '',
        'normal' => '',
        );

    // }}}

    // {{{ constructor

    function PEAR_Frontend_CLI()
    {
        parent::PEAR();
        $term = getenv('TERM'); //(cox) $_ENV is empty for me in 4.1.1
        if (function_exists('posix_isatty') && !posix_isatty(1)) {
            // output is being redirected to a file or through a pipe
        } elseif ($term) {
            // XXX can use ncurses extension here, if available
            if (preg_match('/^(xterm|vt220|linux)/', $term)) {
                $this->term['bold'] = sprintf("%c%c%c%c", 27, 91, 49, 109);
                $this->term['normal']=sprintf("%c%c%c", 27, 91, 109);
            } elseif (preg_match('/^vt100/', $term)) {
                $this->term['bold'] = sprintf("%c%c%c%c%c%c", 27, 91, 49, 109, 0, 0);
                $this->term['normal']=sprintf("%c%c%c%c%c", 27, 91, 109, 0, 0);
            }
        } elseif (OS_WINDOWS) {
            // XXX add ANSI codes here
        }
    }

    // }}}

    // {{{ displayLine(text)

    function displayLine($text)
    {
        trigger_error("PEAR_Frontend_CLI::displayLine deprecated", E_USER_ERROR);
    }

    function _displayLine($text)
    {
        print "$this->lp$text\n";
    }

    // }}}
    // {{{ display(text)

    function display($text)
    {
        trigger_error("PEAR_Frontend_CLI::display deprecated", E_USER_ERROR);
    }

    function _display($text)
    {
        print $text;
    }

    // }}}
    // {{{ displayError(eobj)

    /**
     * @param object PEAR_Error object
     */
    function displayError($eobj)
    {
        return $this->_displayLine($eobj->getMessage());
    }

    // }}}
    // {{{ displayFatalError(eobj)

    /**
     * @param object PEAR_Error object
     */
    function displayFatalError($eobj)
    {
        $this->displayError($eobj);
        if (class_exists('PEAR_Config')) {
            $config = &PEAR_Config::singleton();
            if ($config->get('verbose') > 5) {
                if (function_exists('debug_print_backtrace')) {
                    debug_print_backtrace();
                } elseif (function_exists('debug_backtrace')) {
                    $trace = debug_backtrace();
                    $raised = false;
                    foreach ($trace as $i => $frame) {
                        if (!$raised) {
                            if (isset($frame['class']) && strtolower($frame['class']) ==
                                  'pear' && strtolower($frame['function']) == 'raiseerror') {
                                $raised = true;
                            } else {
                                continue;
                            }
                        }
                        if (!isset($frame['class'])) {
                            $frame['class'] = '';
                        }
                        if (!isset($frame['type'])) {
                            $frame['type'] = '';
                        }
                        if (!isset($frame['function'])) {
                            $frame['function'] = '';
                        }
                        if (!isset($frame['line'])) {
                            $frame['line'] = '';
                        }
                        $this->_displayLine("#$i: $frame[class]$frame[type]$frame[function] $frame[line]");
                    }
                }
            }
        }
        exit(1);
    }

    // }}}
    // {{{ displayHeading(title)

    function displayHeading($title)
    {
        trigger_error("PEAR_Frontend_CLI::displayHeading deprecated", E_USER_ERROR);
    }

    function _displayHeading($title)
    {
        print $this->lp.$this->bold($title)."\n";
        print $this->lp.str_repeat("=", strlen($title))."\n";
    }

    // }}}

    /**
     * Instruct the runInstallScript method to skip a paramgroup that matches the
     * id value passed in.
     *
     * This method is useful for dynamically configuring which sections of a post-install script
     * will be run based on the user's setup, which is very useful for making flexible
     * post-install scripts without losing the cross-Frontend ability to retrieve user input
     * @param string
     */
    function skipParamgroup($id)
    {
        $this->_skipSections[$id] = true;
    }

    function runPostinstallScripts(&$scripts)
    {
        foreach ($scripts as $i => $script) {
            $this->runInstallScript($scripts[$i]->_params, $scripts[$i]->_obj);
        }
    }

    /**
     * @param array $xml contents of postinstallscript tag
     * @param object $script post-installation script
     * @param string install|upgrade
     */
    function runInstallScript($xml, &$script)
    {
        $this->_skipSections = array();
        if (!is_array($xml) || !isset($xml['paramgroup'])) {
            $script->run(array(), '_default');
        } else {
            $completedPhases = array();
            if (!isset($xml['paramgroup'][0])) {
                $xml['paramgroup'] = array($xml['paramgroup']);
            }
            foreach ($xml['paramgroup'] as $group) {
                if (isset($this->_skipSections[$group['id']])) {
                    // the post-install script chose to skip this section dynamically
                    continue;
                }
                if (isset($group['name'])) {
                    $paramname = explode('::', $group['name']);
                    if ($lastgroup['id'] != $paramname[0]) {
                        continue;
                    }
                    $group['name'] = $paramname[1];
                    if (isset($answers)) {
                        if (isset($answers[$group['name']])) {
                            switch ($group['conditiontype']) {
                                case '=' :
                                    if ($answers[$group['name']] != $group['value']) {
                                        continue 2;
                                    }
                                break;
                                case '!=' :
                                    if ($answers[$group['name']] == $group['value']) {
                                        continue 2;
                                    }
                                break;
                                case 'preg_match' :
                                    if (!@preg_match('/' . $group['value'] . '/',
                                          $answers[$group['name']])) {
                                        continue 2;
                                    }
                                break;
                                default :
                                return;
                            }
                        }
                    } else {
                        return;
                    }
                }
                $lastgroup = $group;
                if (isset($group['instructions'])) {
                    $this->_display($group['instructions']);
                }
                if (!isset($group['param'][0])) {
                    $group['param'] = array($group['param']);
                }
                if (isset($group['param'])) {
                    if (method_exists($script, 'postProcessPrompts')) {
                        $prompts = $script->postProcessPrompts($group['param'], $group['id']);
                        if (!is_array($prompts) || count($prompts) != count($group['param'])) {
                            $this->outputData('postinstall', 'Error: post-install script did not ' .
                                'return proper post-processed prompts');
                            $prompts = $group['param'];
                        } else {
                            foreach ($prompts as $i => $var) {
                                if (!is_array($var) || !isset($var['prompt']) ||
                                      !isset($var['name']) ||
                                      ($var['name'] != $group['param'][$i]['name']) ||
                                      ($var['type'] != $group['param'][$i]['type'])) {
                                    $this->outputData('postinstall', 'Error: post-install script ' .
                                        'modified the variables or prompts, severe security risk. ' .
                                        'Will instead use the defaults from the package.xml');
                                    $prompts = $group['param'];
                                }
                            }
                        }
                        $answers = $this->confirmDialog($prompts);
                    } else {
                        $answers = $this->confirmDialog($group['param']);
                    }
                }
                if ((isset($answers) && $answers) || !isset($group['param'])) {
                    if (!isset($answers)) {
                        $answers = array();
                    }
                    array_unshift($completedPhases, $group['id']);
                    if (!$script->run($answers, $group['id'])) {
                        $script->run($completedPhases, '_undoOnError');
                        return;
                    }
                } else {
                    $script->run($completedPhases, '_undoOnError');
                    return;
                }
            }
        }
    }

    /**
     * Ask for user input, confirm the answers and continue until the user is satisfied
     * @param array an array of arrays, format array('name' => 'paramname', 'prompt' =>
     *              'text to display', 'type' => 'string'[, default => 'default value'])
     * @return array
     */
    function confirmDialog($params)
    {
        $answers = array();
        $prompts = $types = array();
        foreach ($params as $param) {
            $prompts[$param['name']] = $param['prompt'];
            $types[$param['name']] = $param['type'];
            if (isset($param['default'])) {
                $answers[$param['name']] = $param['default'];
            } else {
                $answers[$param['name']] = '';
            }
        }
        $tried = false;
        do {
            if ($tried) {
                $i = 1;
                foreach ($answers as $var => $value) {
                    if (!strlen($value)) {
                        echo $this->bold("* Enter an answer for #" . $i . ": ({$prompts[$var]})\n");
                    }
                    $i++;
                }
            }
            $answers = $this->userDialog('', $prompts, $types, $answers);
            $tried = true;
        } while (is_array($answers) && count(array_filter($answers)) != count($prompts));
        return $answers;
    }
    // {{{ userDialog(prompt, [type], [default])

    function userDialog($command, $prompts, $types = array(), $defaults = array(),
                        $screensize = 20)
    {
        if (!is_array($prompts)) {
            return array();
        }
        $testprompts = array_keys($prompts);
        $result = $defaults;
        if (!defined('STDIN')) {
            $fp = fopen('php://stdin', 'r');
        } else {
            $fp = STDIN;
        }
        reset($prompts);
        if (count($prompts) == 1 && $types[key($prompts)] == 'yesno') {
            foreach ($prompts as $key => $prompt) {
                $type = $types[$key];
                $default = @$defaults[$key];
                print "$prompt ";
                if ($default) {
                    print "[$default] ";
                }
                print ": ";
                if (version_compare(phpversion(), '5.0.0', '<')) {
                    $line = fgets($fp, 2048);
                } else {
                    if (!defined('STDIN')) {
                        define('STDIN', fopen('php://stdin', 'r'));
                    }
                    $line = fgets(STDIN, 2048);
                }
                if ($default && trim($line) == "") {
                    $result[$key] = $default;
                } else {
                    $result[$key] = trim($line);
                }
            }
            return $result;
        }
        while (true) {
            $descLength = max(array_map('strlen', $prompts));
            $descFormat = "%-{$descLength}s";
            $last = count($prompts);

            $i = 0;
            foreach ($prompts as $n => $var) {
                printf("%2d. $descFormat : %s\n", ++$i, $prompts[$n], isset($result[$n]) ?
                    $result[$n] : null);
            }

            print "\n1-$last, 'all', 'abort', or Enter to continue: ";
            $tmp = trim(fgets($fp, 1024));
            if (empty($tmp)) {
                break;
            }
            if ($tmp == 'abort') {
                return false;
            }
            if (isset($testprompts[(int)$tmp - 1])) {
                $var = $testprompts[(int)$tmp - 1];
                $desc = $prompts[$var];
                $current = @$result[$var];
                print "$desc [$current] : ";
                $tmp = trim(fgets($fp, 1024));
                if (trim($tmp) !== '') {
                    $result[$var] = trim($tmp);
                }
            } elseif ($tmp == 'all') {
                foreach ($prompts as $var => $desc) {
                    $current = $result[$var];
                    print "$desc [$current] : ";
                    $tmp = trim(fgets($fp, 1024));
                    if (trim($tmp) !== '') {
                        $result[$var] = trim($tmp);
                    }
                }
            }
        }
        if (!defined('STDIN')) {
            fclose($fp);
        }
        return $result;
    }

    // }}}
    // {{{ userConfirm(prompt, [default])

    function userConfirm($prompt, $default = 'yes')
    {
        trigger_error("PEAR_Frontend_CLI::userConfirm not yet converted", E_USER_ERROR);
        static $positives = array('y', 'yes', 'on', '1');
        static $negatives = array('n', 'no', 'off', '0');
        print "$this->lp$prompt [$default] : ";
        $fp = fopen("php://stdin", "r");
        $line = fgets($fp, 2048);
        fclose($fp);
        $answer = strtolower(trim($line));
        if (empty($answer)) {
            $answer = $default;
        }
        if (in_array($answer, $positives)) {
            return true;
        }
        if (in_array($answer, $negatives)) {
            return false;
        }
        if (in_array($default, $positives)) {
            return true;
        }
        return false;
    }

    // }}}
    // {{{ startTable([params])

    function startTable($params = array())
    {
        trigger_error("PEAR_Frontend_CLI::startTable deprecated", E_USER_ERROR);
    }

    function _startTable($params = array())
    {
        $params['table_data'] = array();
        $params['widest'] = array();  // indexed by column
        $params['highest'] = array(); // indexed by row
        $params['ncols'] = 0;
        $this->params = $params;
    }

    // }}}
    // {{{ tableRow(columns, [rowparams], [colparams])

    function tableRow($columns, $rowparams = array(), $colparams = array())
    {
        trigger_error("PEAR_Frontend_CLI::tableRow deprecated", E_USER_ERROR);
    }

    function _tableRow($columns, $rowparams = array(), $colparams = array())
    {
        $highest = 1;
        for ($i = 0; $i < sizeof($columns); $i++) {
            $col = &$columns[$i];
            if (isset($colparams[$i]) && !empty($colparams[$i]['wrap'])) {
                $col = wordwrap($col, $colparams[$i]['wrap'], "\n", 0);
            }
            if (strpos($col, "\n") !== false) {
                $multiline = explode("\n", $col);
                $w = 0;
                foreach ($multiline as $n => $line) {
                    if (strlen($line) > $w) {
                        $w = strlen($line);
                    }
                }
                $lines = sizeof($multiline);
            } else {
                $w = strlen($col);
            }

            if (isset($this->params['widest'][$i])) {
                if ($w > $this->params['widest'][$i]) {
                    $this->params['widest'][$i] = $w;
                }
            } else {
                $this->params['widest'][$i] = $w;
            }
            $tmp = count_chars($columns[$i], 1);
            // handle unix, mac and windows formats
            $lines = (isset($tmp[10]) ? $tmp[10] : (isset($tmp[13]) ? $tmp[13] : 0)) + 1;
            if ($lines > $highest) {
                $highest = $lines;
            }
        }
        if (sizeof($columns) > $this->params['ncols']) {
            $this->params['ncols'] = sizeof($columns);
        }
        $new_row = array(
            'data' => $columns,
            'height' => $highest,
            'rowparams' => $rowparams,
            'colparams' => $colparams,
            );
        $this->params['table_data'][] = $new_row;
    }

    // }}}
    // {{{ endTable()

    function endTable()
    {
        trigger_error("PEAR_Frontend_CLI::endTable deprecated", E_USER_ERROR);
    }

    function _endTable()
    {
        extract($this->params);
        if (!empty($caption)) {
            $this->_displayHeading($caption);
        }
        if (count($table_data) == 0) {
            return;
        }
        if (!isset($width)) {
            $width = $widest;
        } else {
            for ($i = 0; $i < $ncols; $i++) {
                if (!isset($width[$i])) {
                    $width[$i] = $widest[$i];
                }
            }
        }
        $border = false;
        if (empty($border)) {
            $cellstart = '';
            $cellend = ' ';
            $rowend = '';
            $padrowend = false;
            $borderline = '';
        } else {
            $cellstart = '| ';
            $cellend = ' ';
            $rowend = '|';
            $padrowend = true;
            $borderline = '+';
            foreach ($width as $w) {
                $borderline .= str_repeat('-', $w + strlen($cellstart) + strlen($cellend) - 1);
                $borderline .= '+';
            }
        }
        if ($borderline) {
            $this->_displayLine($borderline);
        }
        for ($i = 0; $i < sizeof($table_data); $i++) {
            extract($table_data[$i]);
            if (!is_array($rowparams)) {
                $rowparams = array();
            }
            if (!is_array($colparams)) {
                $colparams = array();
            }
            $rowlines = array();
            if ($height > 1) {
                for ($c = 0; $c < sizeof($data); $c++) {
                    $rowlines[$c] = preg_split('/(\r?\n|\r)/', $data[$c]);
                    if (sizeof($rowlines[$c]) < $height) {
                        $rowlines[$c] = array_pad($rowlines[$c], $height, '');
                    }
                }
            } else {
                for ($c = 0; $c < sizeof($data); $c++) {
                    $rowlines[$c] = array($data[$c]);
                }
            }
            for ($r = 0; $r < $height; $r++) {
                $rowtext = '';
                for ($c = 0; $c < sizeof($data); $c++) {
                    if (isset($colparams[$c])) {
                        $attribs = array_merge($rowparams, $colparams);
                    } else {
                        $attribs = $rowparams;
                    }
                    $w = isset($width[$c]) ? $width[$c] : 0;
                    //$cell = $data[$c];
                    $cell = $rowlines[$c][$r];
                    $l = strlen($cell);
                    if ($l > $w) {
                        $cell = substr($cell, 0, $w);
                    }
                    if (isset($attribs['bold'])) {
                        $cell = $this->bold($cell);
                    }
                    if ($l < $w) {
                        // not using str_pad here because we may
                        // add bold escape characters to $cell
                        $cell .= str_repeat(' ', $w - $l);
                    }

                    $rowtext .= $cellstart . $cell . $cellend;
                }
                if (!$border) {
                    $rowtext = rtrim($rowtext);
                }
                $rowtext .= $rowend;
                $this->_displayLine($rowtext);
            }
        }
        if ($borderline) {
            $this->_displayLine($borderline);
        }
    }

    // }}}
    // {{{ outputData()

    function outputData($data, $command = '_default')
    {
        switch ($command) {
            case 'channel-info':
                foreach ($data as $type => $section) {
                    if ($type == 'main') {
                        $section['data'] = array_values($section['data']);
                    }
                    $this->outputData($section);
                }
                break;
            case 'install':
            case 'upgrade':
            case 'upgrade-all':
                if (isset($data['release_warnings'])) {
                    $this->_displayLine('');
                    $this->_startTable(array(
                        'border' => false,
                        'caption' => 'Release Warnings'
                        ));
                    $this->_tableRow(array($data['release_warnings']), null, array(1 => array('wrap' => 55)));
                    $this->_endTable();
                    $this->_displayLine('');
                }
                $this->_displayLine($data['data']);
                break;
            case 'search':
                $this->_startTable($data);
                if (isset($data['headline']) && is_array($data['headline'])) {
                    $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55)));
                }

                foreach($data['data'] as $category) {
                    foreach($category as $pkg) {
                        $this->_tableRow($pkg, null, array(1 => array('wrap' => 55)));
                    }
                };
                $this->_endTable();
                break;
            case 'list-all':
                if (!isset($data['data'])) {
                      $this->_displayLine('No packages in channel');
                      break;
                }
                $this->_startTable($data);
                if (isset($data['headline']) && is_array($data['headline'])) {
                    $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55)));
                }

                foreach($data['data'] as $category) {
                    foreach($category as $pkg) {
                        unset($pkg[4]);
                        unset($pkg[5]);
                        $this->_tableRow($pkg, null, array(1 => array('wrap' => 55)));
                    }
                };
                $this->_endTable();
                break;
            case 'config-show':
                $data['border'] = false;
                $opts = array(0 => array('wrap' => 30),
                              1 => array('wrap' => 20),
                              2 => array('wrap' => 35));
                $this->_startTable($data);
                if (isset($data['headline']) && is_array($data['headline'])) {
                    $this->_tableRow($data['headline'],
                                     array('bold' => true),
                                     $opts);
                }
                foreach($data['data'] as $group) {
                    foreach($group as $value) {
                        if ($value[2] == '') {
                            $value[2] = "<not set>";
                        }
                        $this->_tableRow($value, null, $opts);
                    }
                }
                $this->_endTable();
                break;
            case 'remote-info':
                $d = $data;
                $data = array(
                    'caption' => 'Package details:',
                    'border' => false,
                    'data' => array(
                        array("Latest",    $data['stable']),
                        array("Installed", $data['installed']),
                        array("Package",   $data['name']),
                        array("License",   $data['license']),
                        array("Category",  $data['category']),
                        array("Summary",   $data['summary']),
                        array("Description", $data['description']),
                        ),
                    );
                    if (isset($d['deprecated']) && $d['deprecated']) {
                        $conf = &PEAR_Config::singleton();
                        $reg = $conf->getRegistry();
                        $name = $reg->parsedPackageNameToString($d['deprecated'], true);
                        $data['data'][] = array('Deprecated! use', $name);
                    }
            default: {
                if (is_array($data)) {
                    $this->_startTable($data);
                    $count = count($data['data'][0]);
                    if ($count == 2) {
                        $opts = array(0 => array('wrap' => 25),
                                      1 => array('wrap' => 48)
                        );
                    } elseif ($count == 3) {
                        $opts = array(0 => array('wrap' => 30),
                                      1 => array('wrap' => 20),
                                      2 => array('wrap' => 35)
                        );
                    } else {
                        $opts = null;
                    }
                    if (isset($data['headline']) && is_array($data['headline'])) {
                        $this->_tableRow($data['headline'],
                                         array('bold' => true),
                                         $opts);
                    }
                    foreach($data['data'] as $row) {
                        $this->_tableRow($row, null, $opts);
                    }
                    $this->_endTable();
                } else {
                    $this->_displayLine($data);
                }
            }
        }
    }

    // }}}
    // {{{ log(text)


    function log($text, $append_crlf = true)
    {
        if ($append_crlf) {
            return $this->_displayLine($text);
        }
        return $this->_display($text);
    }


    // }}}
    // {{{ bold($text)

    function bold($text)
    {
        if (empty($this->term['bold'])) {
            return strtoupper($text);
        }
        return $this->term['bold'] . $text . $this->term['normal'];
    }

    // }}}
}

?>
<?php
/**
 * PEAR_Installer
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V.V. Cox <cox@idecnet.com>
 * @author     Martin Jansen <mj@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Installer.php,v 1.253 2008/05/13 22:46:07 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * Used for installation groups in package.xml 2.0 and platform exceptions
 */
require_once 'phar://install-pear-nozlib.phar/' . 'OS/Guess.php';
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Downloader.php';

define('PEAR_INSTALLER_NOBINARY', -240);
/**
 * Administration class used to install PEAR packages and maintain the
 * installed package database.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V.V. Cox <cox@idecnet.com>
 * @author     Martin Jansen <mj@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */
class PEAR_Installer extends PEAR_Downloader
{
    // {{{ 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 PEAR_Registry
     */
    var $registry;

    /**
     * array of PEAR_Downloader_Packages
     * @var array
     */
    var $_downloadedPackages;

    /** 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');
    }

    function setOptions($options)
    {
        $this->_options = $options;
    }

    function setConfig(&$config)
    {
        $this->config = &$config;
        $this->_registry = &$config->getRegistry();
    }

    // }}}

    function _removeBackups($files)
    {
        foreach ($files as $path) {
            $this->addFileOperation('removebackup', array($path));
        }
    }

    // {{{ _deletePackageFiles()

    /**
     * Delete a package's installed files, does not remove empty directories.
     *
     * @param string package name
     * @param string channel name
     * @param bool if true, then files are backed up first
     * @return bool TRUE on success, or a PEAR error on failure
     * @access protected
     */
    function _deletePackageFiles($package, $channel = false, $backup = false)
    {
        if (!$channel) {
            $channel = 'pear.php.net';
        }
        if (!strlen($package)) {
            return $this->raiseError("No package to uninstall given");
        }
        if (strtolower($package) == 'pear' && $channel == 'pear.php.net') {
            // to avoid race conditions, include all possible needed files
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Common.php';
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Replace.php';
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Unixeol.php';
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Windowseol.php';
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v1.php';
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2.php';
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/Generator/v1.php';
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/Generator/v2.php';
        }
        $filelist = $this->_registry->packageInfo($package, 'filelist', $channel);
        if ($filelist == null) {
            return $this->raiseError("$channel/$package not installed");
        }
        $ret = array();
        foreach ($filelist as $file => $props) {
            if (empty($props['installed_as'])) {
                continue;
            }
            $path = $props['installed_as'];
            if ($backup) {
                $this->addFileOperation('backup', array($path));
                $ret[] = $path;
            }
            $this->addFileOperation('delete', array($path));
        }
        if ($backup) {
            return $ret;
        }
        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($this->_registry)) {
            $this->_registry = &$this->config->getRegistry();
        }
        if (isset($atts['platform'])) {
            if (empty($os)) {
                $os = new OS_Guess();
            }
            if (strlen($atts['platform']) && $atts['platform']{0} == '!') {
                $negate = true;
                $platform = substr($atts['platform'], 1);
            } else {
                $negate = false;
                $platform = $atts['platform'];
            }
            if ((bool) $os->matchSignature($platform) === $negate) {
                $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")");
                return PEAR_INSTALLER_SKIPPED;
            }
        }
        // }}}

        $channel = $this->pkginfo->getChannel();
        // {{{ assemble the destination paths
        switch ($atts['role']) {
            case 'src':
            case 'extsrc':
                $this->source_files++;
                return;
            case 'doc':
            case 'data':
            case 'test':
                $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel) .
                            DIRECTORY_SEPARATOR . $this->pkginfo->getPackage();
                unset($atts['baseinstalldir']);
                break;
            case 'ext':
            case 'php':
                $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel);
                break;
            case 'script':
                $dest_dir = $this->config->get('bin_dir', null, $channel);
                break;
            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+!"),
                                                    array(DIRECTORY_SEPARATOR,
                                                          DIRECTORY_SEPARATOR,
                                                          DIRECTORY_SEPARATOR),
                                                    array($dest_file, $orig_file));
        $final_dest_file = $installed_as = $dest_file;
        if (isset($this->_options['packagingroot'])) {
            $installedas_dest_dir = dirname($final_dest_file);
            $installedas_dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
            $final_dest_file = $this->_prependPath($final_dest_file,
                $this->_options['packagingroot']);
        } else {
            $installedas_dest_dir = dirname($final_dest_file);
            $installedas_dest_file = $installedas_dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
        }
        $dest_dir = dirname($final_dest_file);
        $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
        if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) {
            return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED);
        }
        // }}}

        if (empty($this->_options['register-only']) &&
              (!file_exists($dest_dir) || !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");
        }
        // pretty much nothing happens if we are only registering the install
        if (empty($this->_options['register-only'])) {
            if (empty($atts['replacements'])) {
                if (!file_exists($orig_file)) {
                    return $this->raiseError("file $orig_file does not exist",
                                             PEAR_INSTALLER_FAILED);
                }
                if (!@copy($orig_file, $dest_file)) {
                    return $this->raiseError("failed to write $dest_file: $php_errormsg",
                                             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);
                }
                $contents = file_get_contents($orig_file);
                if ($contents === false) {
                    $contents = '';
                }
                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_]+\\z/i', $a['to'])) {
                            eval("\$to = $a[to];");
                        } else {
                            if (!isset($options['soft'])) {
                                $this->log(0, "invalid php-const replacement: $a[to]");
                            }
                            continue;
                        }
                    } elseif ($a['type'] == 'pear-config') {
                        if ($a['to'] == 'master_server') {
                            $chan = $this->_registry->getChannel($channel);
                            if (!PEAR::isError($chan)) {
                                $to = $chan->getServer();
                            } else {
                                $to = $this->config->get($a['to'], null, $channel);
                            }
                        } else {
                            $to = $this->config->get($a['to'], null, $channel);
                        }
                        if (is_null($to)) {
                            if (!isset($options['soft'])) {
                                $this->log(0, "invalid pear-config replacement: $a[to]");
                            }
                            continue;
                        }
                    } elseif ($a['type'] == 'package-info') {
                        if ($t = $this->pkginfo->packageInfo($a['to'])) {
                            $to = $t;
                        } else {
                            if (!isset($options['soft'])) {
                                $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) === false) {
                    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
                        if (file_exists($dest_file)) {
                            unlink($dest_file);
                        }
                        if (!isset($options['ignore-errors'])) {
                            return $this->raiseError("bad md5sum for file $final_dest_file",
                                                 PEAR_INSTALLER_FAILED);
                        } else {
                            if (!isset($options['soft'])) {
                                $this->log(0, "warning : bad md5sum for file $final_dest_file");
                            }
                        }
                    } else {
                        if (!isset($options['soft'])) {
                            $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'));
                }
                if ($atts['role'] != 'src') {
                    $this->addFileOperation("chmod", array($mode, $dest_file));
                    if (!@chmod($dest_file, $mode)) {
                        if (!isset($options['soft'])) {
                            $this->log(0, "failed to change mode of $dest_file: $php_errormsg");
                        }
                    }
                }
            }
            // }}}
            if ($atts['role'] == 'src') {
                rename($dest_file, $final_dest_file);
                $this->log(2, "renamed source file $dest_file to $final_dest_file");
            } else {
                $this->addFileOperation("rename", array($dest_file, $final_dest_file,
                    $atts['role'] == 'ext'));
            }
        }
        // Store the full path where the file was installed for easy unistall
        if ($atts['role'] != 'script') {
            $loc = $this->config->get($atts['role'] . '_dir');
        } else {
            $loc = $this->config->get('bin_dir');
        }
        if ($atts['role'] != 'src') {
            $this->addFileOperation("installed_as", array($file, $installed_as,
                                    $loc,
                                    dirname(substr($installedas_dest_file, strlen($loc)))));
        }

        //$this->log(2, "installed: $dest_file");
        return PEAR_INSTALLER_OK;
    }

    // }}}
    // {{{ _installFile2()

    /**
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @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 _installFile2(&$pkg, $file, &$real_atts, $tmp_path, $options)
    {
        $atts = $real_atts;
        if (!isset($this->_registry)) {
            $this->_registry = &$this->config->getRegistry();
        }

        $channel = $pkg->getChannel();
        // {{{ assemble the destination paths
        if (!in_array($atts['attribs']['role'],
              PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) {
            return $this->raiseError('Invalid role `' . $atts['attribs']['role'] .
                    "' for file $file");
        }
        $role = &PEAR_Installer_Role::factory($pkg, $atts['attribs']['role'], $this->config);
        $err = $role->setup($this, $pkg, $atts['attribs'], $file);
        if (PEAR::isError($err)) {
            return $err;
        }
        if (!$role->isInstallable()) {
            return;
        }
        $info = $role->processInstallation($pkg, $atts['attribs'], $file, $tmp_path);
        if (PEAR::isError($info)) {
            return $info;
        } else {
            list($save_destdir, $dest_dir, $dest_file, $orig_file) = $info;
        }
        if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) {
            return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED);
        }
        $final_dest_file = $installed_as = $dest_file;
        if (isset($this->_options['packagingroot'])) {
            $final_dest_file = $this->_prependPath($final_dest_file,
                $this->_options['packagingroot']);
        }
        $dest_dir = dirname($final_dest_file);
        $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
        // }}}

        if (empty($this->_options['register-only'])) {
            if (!file_exists($dest_dir) || !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");
            }
        }
        $attribs = $atts['attribs'];
        unset($atts['attribs']);
        // pretty much nothing happens if we are only registering the install
        if (empty($this->_options['register-only'])) {
            if (!count($atts)) { // no tasks
                if (!file_exists($orig_file)) {
                    return $this->raiseError("file $orig_file does not exist",
                                             PEAR_INSTALLER_FAILED);
                }
                if (!@copy($orig_file, $dest_file)) {
                    return $this->raiseError("failed to write $dest_file: $php_errormsg",
                                             PEAR_INSTALLER_FAILED);
                }
                $this->log(3, "+ cp $orig_file $dest_file");
                if (isset($attribs['md5sum'])) {
                    $md5sum = md5_file($dest_file);
                }
            } else { // file with tasks
                if (!file_exists($orig_file)) {
                    return $this->raiseError("file $orig_file does not exist",
                                             PEAR_INSTALLER_FAILED);
                }
                $contents = file_get_contents($orig_file);
                if ($contents === false) {
                    $contents = '';
                }
                if (isset($attribs['md5sum'])) {
                    $md5sum = md5($contents);
                }
                foreach ($atts as $tag => $raw) {
                    $tag = str_replace(array($pkg->getTasksNs() . ':', '-'), 
                        array('', '_'), $tag);
                    $task = "PEAR_Task_$tag";
                    $task = &new $task($this->config, $this, PEAR_TASK_INSTALL);
                    if (!$task->isScript()) { // scripts are only handled after installation
                        $task->init($raw, $attribs, $pkg->getLastInstalledVersion());
                        $res = $task->startSession($pkg, $contents, $final_dest_file);
                        if ($res === false) {
                            continue; // skip this file
                        }
                        if (PEAR::isError($res)) {
                            return $res;
                        }
                        $contents = $res; // save changes
                    }
                    $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) === false) {
                        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($attribs['md5sum'])) {
                    $this->log(2, "md5sum ok: $final_dest_file");
                } else {
                    if (empty($options['force'])) {
                        // delete the file
                        if (file_exists($dest_file)) {
                            unlink($dest_file);
                        }
                        if (!isset($options['ignore-errors'])) {
                            return $this->raiseError("bad md5sum for file $final_dest_file",
                                                     PEAR_INSTALLER_FAILED);
                        } else {
                            if (!isset($options['soft'])) {
                                $this->log(0, "warning : bad md5sum for file $final_dest_file");
                            }
                        }
                    } else {
                        if (!isset($options['soft'])) {
                            $this->log(0, "warning : bad md5sum for file $final_dest_file");
                        }
                    }
                }
            } else {
                $real_atts['attribs']['md5sum'] = md5_file($dest_file);
            }
            // }}}
            // {{{ set file permissions
            if (!OS_WINDOWS) {
                if ($role->isExecutable()) {
                    $mode = 0777 & ~(int)octdec($this->config->get('umask'));
                    $this->log(3, "+ chmod +x $dest_file");
                } else {
                    $mode = 0666 & ~(int)octdec($this->config->get('umask'));
                }
                if ($attribs['role'] != 'src') {
                    $this->addFileOperation("chmod", array($mode, $dest_file));
                    if (!@chmod($dest_file, $mode)) {
                        if (!isset($options['soft'])) {
                            $this->log(0, "failed to change mode of $dest_file: $php_errormsg");
                        }
                    }
                }
            }
            // }}}
            if ($attribs['role'] == 'src') {
                rename($dest_file, $final_dest_file);
                $this->log(2, "renamed source file $dest_file to $final_dest_file");
            } else {
                $this->addFileOperation("rename", array($dest_file, $final_dest_file, $role->isExtension()));
            }
        }
        // Store the full path where the file was installed for easy uninstall
        if ($attribs['role'] != 'src') {
            $loc = $this->config->get($role->getLocationConfig(), null, $channel);
            $this->addFileOperation("installed_as", array($file, $installed_as,
                                $loc,
                                dirname(substr($installed_as, strlen($loc)))));
        }

        //$this->log(2, "installed: $dest_file");
        return PEAR_INSTALLER_OK;
    }

    // }}}
    // {{{ addFileOperation()

    /**
     * Add a file operation to the current file transaction.
     *
     * @see startFileTransaction()
     * @param string $type This can be one of:
     *    - rename:  rename a file ($data has 3 values)
     *    - backup:  backup an existing file ($data has 1 value)
     *    - removebackup:  clean up backups created during install ($data has 1 value)
     *    - 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).
     * @param 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 third whether this is a PHP extension.
     *
     *    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])) {
                        if (!is_writable(dirname($data[0]))) {
                            $errors[] = "permission denied ($type): $data[0]";
                        } else {
                            // make sure the file to be deleted can be opened for writing
                            $fp = false;
                            if (!is_dir($data[0]) &&
                                  (!is_writable($data[0]) || !($fp = @fopen($data[0], 'a')))) {
                                $errors[] = "permission denied ($type): $data[0]";
                            } elseif ($fp) {
                                fclose($fp);
                            }
                        }
                    }
                    break;
            }

        }
        // }}}
        $m = sizeof($errors);
        if ($m > 0) {
            foreach ($errors as $error) {
                if (!isset($this->_options['soft'])) {
                    $this->log(1, $error);
                }
            }
            if (!isset($this->_options['ignore-errors'])) {
                return false;
            }
        }
        $this->_dirtree = array();
        // {{{ really commit the transaction
        foreach ($this->file_operations as $i => $tr) {
            if (!$tr) {
                // support removal of non-existing backups
                continue;
            }
            list($type, $data) = $tr;
            switch ($type) {
                case 'backup':
                    if (!file_exists($data[0])) {
                        $this->file_operations[$i] = false;
                        break;
                    }
                    if (!@copy($data[0], $data[0] . '.bak')) {
                        $this->log(1, 'Could not copy ' . $data[0] . ' to ' . $data[0] .
                            '.bak ' . $php_errormsg);
                        return false;
                    }
                    $this->log(3, "+ backup $data[0] to $data[0].bak");
                    break;
                case 'removebackup':
                    if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) {
                        unlink($data[0] . '.bak');
                        $this->log(3, "+ rm backup of $data[0] ($data[0].bak)");
                    }
                    break;
                case 'rename':
                    if (file_exists($data[1])) {
                        $test = @unlink($data[1]);
                    } else {
                        $test = null;
                    }
                    if (!$test && file_exists($data[1])) {
                        if ($data[2]) {
                            $extra = ', this extension must be installed manually.  Rename to "' .
                                basename($data[1]) . '"';
                        } else {
                            $extra = '';
                        }
                        if (!isset($this->_options['soft'])) {
                            $this->log(1, 'Could not delete ' . $data[1] . ', cannot rename ' .
                                $data[0] . $extra);
                        }
                        if (!isset($this->_options['ignore-errors'])) {
                            return false;
                        }
                    }
                    // permissions issues with rename - copy() is far superior
                    $perms = @fileperms($data[0]);
                    if (!@copy($data[0], $data[1])) {
                        $this->log(1, 'Could not rename ' . $data[0] . ' to ' . $data[1] .
                            ' ' . $php_errormsg);
                        return false;
                    }
                    // copy over permissions, otherwise they are lost
                    @chmod($data[1], $perms);
                    @unlink($data[0]);
                    $this->log(3, "+ mv $data[0] $data[1]");
                    break;
                case 'chmod':
                    if (!@chmod($data[1], $data[0])) {
                        $this->log(1, 'Could not chmod ' . $data[1] . ' to ' .
                            decoct($data[0]) . ' ' . $php_errormsg);
                        return false;
                    }
                    $octmode = decoct($data[0]);
                    $this->log(3, "+ chmod $octmode $data[1]");
                    break;
                case 'delete':
                    if (file_exists($data[0])) {
                        if (!@unlink($data[0])) {
                            $this->log(1, 'Could not delete ' . $data[0] . ' ' .
                                $php_errormsg);
                            return false;
                        }
                        $this->log(3, "+ rm $data[0]");
                    }
                    break;
                case 'rmdir':
                    if (file_exists($data[0])) {
                        do {
                            $testme = opendir($data[0]);
                            while (false !== ($entry = readdir($testme))) {
                                if ($entry == '.' || $entry == '..') {
                                    continue;
                                }
                                closedir($testme);
                                break 2; // this directory is not empty and can't be
                                         // deleted
                            }
                            closedir($testme);
                            if (!@rmdir($data[0])) {
                                $this->log(1, 'Could not rmdir ' . $data[0] . ' ' .
                                    $php_errormsg);
                                return false;
                            }
                            $this->log(3, "+ rmdir $data[0]");
                        } while (false);
                    }
                    break;
                case 'installed_as':
                    $this->pkginfo->setInstalledAs($data[0], $data[1]);
                    if (!isset($this->_dirtree[dirname($data[1])])) {
                        $this->_dirtree[dirname($data[1])] = true;
                        $this->pkginfo->setDirtree(dirname($data[1]));

                        while(!empty($data[3]) && dirname($data[3]) != $data[3] &&
                                $data[3] != '/' && $data[3] != '\\') {
                            $this->pkginfo->setDirtree($pp =
                                $this->_prependPath($data[3], $data[2]));
                            $this->_dirtree[$pp] = 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 'backup':
                    if (file_exists($data[0] . '.bak')) {
                        if (file_exists($data[0] && is_writable($data[0]))) {
                            unlink($data[0]);
                        }
                        @copy($data[0] . '.bak', $data[0]);
                        $this->log(3, "+ restore $data[0] from $data[0].bak");
                    }
                    break;
                case 'removebackup':
                    if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) {
                        unlink($data[0] . '.bak');
                        $this->log(3, "+ rm backup of $data[0] ($data[0].bak)");
                    }
                    break;
                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':
                    $this->pkginfo->setInstalledAs($data[0], false);
                    break;
            }
        }
        $this->pkginfo->resetDirtree();
        $this->file_operations = array();
    }

    // }}}
    // {{{ mkDirHier($dir)

    function mkDirHier($dir)
    {
        $this->addFileOperation('mkdir', array($dir));
        return parent::mkDirHier($dir);
    }

    // }}}
    // {{{ 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
     * @deprecated in favor of PEAR_Downloader
     */
    function download($packages, $options, &$config, &$installpackages,
                      &$errors, $installed = false, $willinstall = false, $state = false)
    {
        // trickiness: initialize here
        parent::PEAR_Downloader($this->ui, $options, $config);
        $ret = parent::download($packages);
        $errors = $this->getErrorMsgs();
        $installpackages = $this->getDownloadedPackages();
        trigger_error("PEAR Warning: PEAR_Installer::download() is deprecated " .
                      "in favor of PEAR_Downloader class", E_USER_WARNING);
        return $ret;
    }

    // }}}
    // {{{ _parsePackageXml()

    function _parsePackageXml(&$descfile, &$tmpdir)
    {
        if (substr($descfile, -4) == '.xml') {
            $tmpdir = false;
        } else {
            // {{{ Decompress pack in tmp dir -------------------------------------

            // To allow relative package file names
            $descfile = realpath($descfile);

            if (PEAR::isError($tmpdir = System::mktemp('-d'))) {
                return $tmpdir;
            }
            $this->log(3, '+ tmp dir created at ' . $tmpdir);
            // }}}
        }
        // Parse xml file -----------------------------------------------
        $pkg = new PEAR_PackageFile($this->config, $this->debug, $tmpdir);
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        $p = &$pkg->fromAnyFile($descfile, PEAR_VALIDATE_INSTALLING);
        PEAR::staticPopErrorHandling();
        if (PEAR::isError($p)) {
            if (is_array($p->getUserInfo())) {
                foreach ($p->getUserInfo() as $err) {
                    $loglevel = $err['level'] == 'error' ? 0 : 1;
                    if (!isset($this->_options['soft'])) {
                        $this->log($loglevel, ucfirst($err['level']) . ': ' . $err['message']);
                    }
                }
            }
            return $this->raiseError('Installation failed: invalid package file');
        } else {
            $descfile = $p->getPackageFile();
        }
        return $p;
    }

    // }}}
    /**
     * Set the list of PEAR_Downloader_Package objects to allow more sane
     * dependency validation
     * @param array
     */
    function setDownloadedPackages(&$pkgs)
    {
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        $err = $this->analyzeDependencies($pkgs);
        PEAR::popErrorHandling();
        if (PEAR::isError($err)) {
            return $err;
        }
        $this->_downloadedPackages = &$pkgs;
    }

    /**
     * Set the list of PEAR_Downloader_Package objects to allow more sane
     * dependency validation
     * @param array
     */
    function setUninstallPackages(&$pkgs)
    {
        $this->_downloadedPackages = &$pkgs;
    }

    function getInstallPackages()
    {
        return $this->_downloadedPackages;
    }

    // {{{ install()

    /**
     * Installs the files within the package file specified.
     *
     * @param string|PEAR_Downloader_Package $pkgfile path to the package file,
     *        or a pre-initialized packagefile object
     * @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())
    {
        $this->_options = $options;
        $this->_registry = &$this->config->getRegistry();
        if (is_object($pkgfile)) {
            $dlpkg = &$pkgfile;
            $pkg = $pkgfile->getPackageFile();
            $pkgfile = $pkg->getArchiveFile();
            $descfile = $pkg->getPackageFile();
            $tmpdir = dirname($descfile);
        } else {
            $descfile = $pkgfile;
            $tmpdir = '';
            if (PEAR::isError($pkg = &$this->_parsePackageXml($descfile, $tmpdir))) {
                return $pkg;
            }
        }

        if (realpath($descfile) != realpath($pkgfile)) {
            $tar = new Archive_Tar($pkgfile);
            if (!$tar->extract($tmpdir)) {
                return $this->raiseError("unable to unpack $pkgfile");
            }
        }

        $pkgname = $pkg->getName();
        $channel = $pkg->getChannel();
        if (isset($this->_options['packagingroot'])) {
            $regdir = $this->_prependPath(
                $this->config->get('php_dir', null, 'pear.php.net'),
                $this->_options['packagingroot']);
            $packrootphp_dir = $this->_prependPath(
                $this->config->get('php_dir', null, $channel),
                $this->_options['packagingroot']);
        }

        if (isset($options['installroot'])) {
            $this->config->setInstallRoot($options['installroot']);
            $this->_registry = &$this->config->getRegistry();
            $installregistry = &$this->_registry;
            $this->installroot = ''; // all done automagically now
            $php_dir = $this->config->get('php_dir', null, $channel);
        } else {
            $this->config->setInstallRoot(false);
            $this->_registry = &$this->config->getRegistry();
            if (isset($this->_options['packagingroot'])) {
                $installregistry = &new PEAR_Registry($regdir);
                if (!$installregistry->channelExists($channel, true)) {
                    // we need to fake a channel-discover of this channel
                    $chanobj = $this->_registry->getChannel($channel, true);
                    $installregistry->addChannel($chanobj);
                }
                $php_dir = $packrootphp_dir;
            } else {
                $installregistry = &$this->_registry;
                $php_dir = $this->config->get('php_dir', null, $channel);
            }
            $this->installroot = '';
        }

        // {{{ checks to do when not in "force" mode
        if (empty($options['force']) &&
              (file_exists($this->config->get('php_dir')) &&
               is_dir($this->config->get('php_dir')))) {
            $testp = $channel == 'pear.php.net' ? $pkgname : array($channel, $pkgname);
            $instfilelist = $pkg->getInstallationFileList(true);
            if (PEAR::isError($instfilelist)) {
                return $instfilelist;
            }
            // ensure we have the most accurate registry
            $installregistry->flushFileMap();
            $test = $installregistry->checkFileMap($instfilelist, $testp, '1.1');
            if (PEAR::isError($test)) {
                return $test;
            }
            if (sizeof($test)) {
                $pkgs = $this->getInstallPackages();
                $found = false;
                foreach ($pkgs as $param) {
                    if ($pkg->isSubpackageOf($param)) {
                        $found = true;
                        break;
                    }
                }
                if ($found) {
                    // subpackages can conflict with earlier versions of parent packages
                    $parentreg = $installregistry->packageInfo($param->getPackage(), null, $param->getChannel());
                    $tmp = $test;
                    foreach ($tmp as $file => $info) {
                        if (is_array($info)) {
                            if (strtolower($info[1]) == strtolower($param->getPackage()) &&
                                  strtolower($info[0]) == strtolower($param->getChannel())) {
                                unset($test[$file]);
                                unset($parentreg['filelist'][$file]);
                            }
                        } else {
                            if (strtolower($param->getChannel()) != 'pear.php.net') {
                                continue;
                            }
                            if (strtolower($info) == strtolower($param->getPackage())) {
                                unset($test[$file]);
                                unset($parentreg['filelist'][$file]);
                            }
                        }
                    }
                    $pfk = &new PEAR_PackageFile($this->config);
                    $parentpkg = &$pfk->fromArray($parentreg);
                    $installregistry->updatePackage2($parentpkg);
                }
                if ($param->getChannel() == 'pecl.php.net' && isset($options['upgrade'])) {
                    $tmp = $test;
                    foreach ($tmp as $file => $info) {
                        if (is_string($info)) {
                            // pear.php.net packages are always stored as strings
                            if (strtolower($info) == strtolower($param->getPackage())) {
                                // upgrading existing package
                                unset($test[$file]);
                            }
                        }
                    }
                }
                if (sizeof($test)) {
                    $msg = "$channel/$pkgname: conflicting files found:\n";
                    $longest = max(array_map("strlen", array_keys($test)));
                    $fmt = "%${longest}s (%s)\n";
                    foreach ($test as $file => $info) {
                        if (!is_array($info)) {
                            $info = array('pear.php.net', $info);
                        }
                        $info = $info[0] . '/' . $info[1];
                        $msg .= sprintf($fmt, $file, $info);
                    }
                    if (!isset($options['ignore-errors'])) {
                        return $this->raiseError($msg);
                    } else {
                        if (!isset($options['soft'])) {
                            $this->log(0, "WARNING: $msg");
                        }
                    }
                }
            }
        }
        // }}}

        $this->startFileTransaction();

        if (empty($options['upgrade']) && empty($options['soft'])) {
            // checks to do only when installing new packages
            if ($channel == 'pecl.php.net') {
                $test = $installregistry->packageExists($pkgname, $channel);
                if (!$test) {
                    $test = $installregistry->packageExists($pkgname, 'pear.php.net');
                }
            } else {
                $test = $installregistry->packageExists($pkgname, $channel);
            }
            if (empty($options['force']) && $test) {
                return $this->raiseError("$channel/$pkgname is already installed");
            }
        } else {
            $usechannel = $channel;
            if ($channel == 'pecl.php.net') {
                $test = $installregistry->packageExists($pkgname, $channel);
                if (!$test) {
                    $test = $installregistry->packageExists($pkgname, 'pear.php.net');
                    $usechannel = 'pear.php.net';
                }
            } else {
                $test = $installregistry->packageExists($pkgname, $channel);
            }
            if ($test) {
                $v1 = $installregistry->packageInfo($pkgname, 'version', $usechannel);
                $v2 = $pkg->getVersion();
                $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, $usechannel,
                          true))) {
                        if (!isset($options['ignore-errors'])) {
                            return $this->raiseError($err);
                        } else {
                            if (!isset($options['soft'])) {
                                $this->log(0, 'WARNING: ' . $err->getMessage());
                            }
                        }
                    } else {
                        $backedup = $err;
                    }
                }
            }
        }

        // {{{ Copy files to dest dir ---------------------------------------

        // info from the package it self we want to access from _installFile
        $this->pkginfo = &$pkg;
        // used to determine whether we should build any C code
        $this->source_files = 0;

        $savechannel = $this->config->get('default_channel');
        if (empty($options['register-only']) && !is_dir($php_dir)) {
            if (PEAR::isError(System::mkdir(array('-p'), $php_dir))) {
                return $this->raiseError("no installation destination directory '$php_dir'\n");
            }
        }

        $tmp_path = dirname($descfile);
        if (substr($pkgfile, -4) != '.xml') {
            $tmp_path .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkg->getVersion();
        }

        $this->configSet('default_channel', $channel);
        // {{{ install files

        $ver = $pkg->getPackagexmlVersion();
        if (version_compare($ver, '2.0', '>=')) {
            $filelist = $pkg->getInstallationFilelist();
        } else {
            $filelist = $pkg->getFileList();
        }
        if (PEAR::isError($filelist)) {
            return $filelist;
        }
        $p = &$installregistry->getPackage($pkgname, $channel);
        if (empty($options['register-only']) && $p) {
            $dirtree = $p->getDirTree();
        } else {
            $dirtree = false;
        }
        $pkg->resetFilelist();
        $pkg->setLastInstalledVersion($installregistry->packageInfo($pkg->getPackage(),
            'version', $pkg->getChannel()));
        foreach ($filelist as $file => $atts) {
            if ($pkg->getPackagexmlVersion() == '1.0') {
                $this->expectError(PEAR_INSTALLER_FAILED);
                $res = $this->_installFile($file, $atts, $tmp_path, $options);
                $this->popExpect();
            } else {
                $this->expectError(PEAR_INSTALLER_FAILED);
                $res = $this->_installFile2($pkg, $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 {
                    if (!isset($options['soft'])) {
                        $this->log(0, "Warning: " . $res->getMessage());
                    }
                }
            }
            $real = isset($atts['attribs']) ? $atts['attribs'] : $atts;
            if ($res == PEAR_INSTALLER_OK && $real['role'] != 'src') {
                // Register files that were installed
                $pkg->installedFile($file, $atts);
            }
        }
        // }}}

        // {{{ compile and install source files
        if ($this->source_files > 0 && empty($options['nobuild'])) {
            if (PEAR::isError($err =
                  $this->_compileSourceFiles($savechannel, $pkg))) {
                return $err;
            }
        }
        // }}}

        if (isset($backedup)) {
            $this->_removeBackups($backedup);
        }
        if (!$this->commitFileTransaction()) {
            $this->rollbackFileTransaction();
            $this->configSet('default_channel', $savechannel);
            return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED);
        }
        // }}}

        $ret = false;
        $installphase = 'install';
        $oldversion = false;
        // {{{ Register that the package is installed -----------------------
        if (empty($options['upgrade'])) {
            // if 'force' is used, replace the info in registry
            $usechannel = $channel;
            if ($channel == 'pecl.php.net') {
                $test = $installregistry->packageExists($pkgname, $channel);
                if (!$test) {
                    $test = $installregistry->packageExists($pkgname, 'pear.php.net');
                    $usechannel = 'pear.php.net';
                }
            } else {
                $test = $installregistry->packageExists($pkgname, $channel);
            }
            if (!empty($options['force']) && $test) {
                $oldversion = $installregistry->packageInfo($pkgname, 'version', $usechannel);
                $installregistry->deletePackage($pkgname, $usechannel);
            }
            $ret = $installregistry->addPackage2($pkg);
        } else {
            if ($dirtree) {
                $this->startFileTransaction();
                // attempt to delete empty directories
                uksort($dirtree, array($this, '_sortDirs'));
                foreach($dirtree as $dir => $notused) {
                    $this->addFileOperation('rmdir', array($dir));
                }
                $this->commitFileTransaction();
            }
            $usechannel = $channel;
            if ($channel == 'pecl.php.net') {
                $test = $installregistry->packageExists($pkgname, $channel);
                if (!$test) {
                    $test = $installregistry->packageExists($pkgname, 'pear.php.net');
                    $usechannel = 'pear.php.net';
                }
            } else {
                $test = $installregistry->packageExists($pkgname, $channel);
            }
            // new: upgrade installs a package if it isn't installed
            if (!$test) {
                $ret = $installregistry->addPackage2($pkg);
            } else {
                if ($usechannel != $channel) {
                    $installregistry->deletePackage($pkgname, $usechannel);
                    $ret = $installregistry->addPackage2($pkg);
                } else {
                    $ret = $installregistry->updatePackage2($pkg);
                }
                $installphase = 'upgrade';
            }
        }
        if (!$ret) {
            $this->configSet('default_channel', $savechannel);
            return $this->raiseError("Adding package $channel/$pkgname to registry failed");
        }
        // }}}
        $this->configSet('default_channel', $savechannel);
        if (class_exists('PEAR_Task_Common')) { // this is auto-included if any tasks exist
            if (PEAR_Task_Common::hasPostinstallTasks()) {
                PEAR_Task_Common::runPostinstallTasks($installphase);
            }
        }
        return $pkg->toArray(true);
    }

    // }}}

    // {{{ _compileSourceFiles()
    /**
     * @param string
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     */
    function _compileSourceFiles($savechannel, &$filelist)
    {
        require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Builder.php';
        $this->log(1, "$this->source_files source files, building");
        $bob = &new PEAR_Builder($this->ui);
        $bob->debug = $this->debug;
        $built = $bob->build($filelist, array(&$this, '_buildCallback'));
        if (PEAR::isError($built)) {
            $this->rollbackFileTransaction();
            $this->configSet('default_channel', $savechannel);
            return $built;
        }
        $this->log(1, "\nBuild process completed successfully");
        foreach ($built as $ext) {
            $bn = basename($ext['file']);
            list($_ext_name, $_ext_suff) = explode('.', $bn);
            if ($_ext_suff == '.so' || $_ext_suff == '.dll') {
                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');
                }
                $role = 'ext';
            } else {
                $role = 'src';
            }
            $dest = $ext['dest'];
            $packagingroot = '';
            if (isset($this->_options['packagingroot'])) {
                $packagingroot = $this->_options['packagingroot'];
            }
            $copyto = $this->_prependPath($dest, $packagingroot);
            if ($copyto != $dest) {
	            $this->log(1, "Installing '$dest' as '$copyto'");
            } else {
	            $this->log(1, "Installing '$dest'");
            }
            $copydir = dirname($copyto);
            // pretty much nothing happens if we are only registering the install
            if (empty($this->_options['register-only'])) {
                if (!file_exists($copydir) || !is_dir($copydir)) {
                    if (!$this->mkDirHier($copydir)) {
                        return $this->raiseError("failed to mkdir $copydir",
                            PEAR_INSTALLER_FAILED);
                    }
                    $this->log(3, "+ mkdir $copydir");
                }
                if (!@copy($ext['file'], $copyto)) {
                    return $this->raiseError("failed to write $copyto ($php_errormsg)", PEAR_INSTALLER_FAILED);
                }
                $this->log(3, "+ cp $ext[file] $copyto");
                $this->addFileOperation('rename', array($ext['file'], $copyto));
                if (!OS_WINDOWS) {
                    $mode = 0666 & ~(int)octdec($this->config->get('umask'));
                    $this->addFileOperation('chmod', array($mode, $copyto));
                    if (!@chmod($copyto, $mode)) {
                        $this->log(0, "failed to change mode of $copyto ($php_errormsg)");
                    }
                }
            }

            if ($filelist->getPackageXmlVersion() == '1.0') {
                $filelist->installedFile($bn, array(
                    'role' => $role,
                    'name' => $bn,
                    'installed_as' => $dest,
                    'php_api' => $ext['php_api'],
                    'zend_mod_api' => $ext['zend_mod_api'],
                    'zend_ext_api' => $ext['zend_ext_api'],
                    ));
            } else {
                $filelist->installedFile($bn, array('attribs' => array(
                    'role' => $role,
                    'name' => $bn,
                    'installed_as' => $dest,
                    'php_api' => $ext['php_api'],
                    'zend_mod_api' => $ext['zend_mod_api'],
                    'zend_ext_api' => $ext['zend_ext_api'],
                    )));
            }
        }
    }

    // }}}
    function &getUninstallPackages()
    {
        return $this->_downloadedPackages;
    }
    // {{{ 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
     *              - register-only : update registry but don't remove files
     *              - nodeps: do not process dependencies of other packages to ensure
     *                        uninstallation does not break things
     */
    function uninstall($package, $options = array())
    {
        if (isset($options['installroot'])) {
            $this->config->setInstallRoot($options['installroot']);
            $this->installroot = '';
        } else {
            $this->config->setInstallRoot('');
            $this->installroot = '';
        }
        $this->_registry = &$this->config->getRegistry();
        if (is_object($package)) {
            $channel = $package->getChannel();
            $pkg = $package;
            $package = $pkg->getPackage();
        } else {
            $pkg = false;
            $info = $this->_registry->parsePackageName($package,
                $this->config->get('default_channel'));
            $channel = $info['channel'];
            $package = $info['package'];
        }
        $savechannel = $this->config->get('default_channel');
        $this->configSet('default_channel', $channel);
        if (!is_object($pkg)) {
            $pkg = $this->_registry->getPackage($package, $channel);
        }
        if (!$pkg) {
            $this->configSet('default_channel', $savechannel);
            return $this->raiseError($this->_registry->parsedPackageNameToString(
                array(
                    'channel' => $channel,
                    'package' => $package
                ), true) . ' not installed');
        }
        if ($pkg->getInstalledBinary()) {
            // this is just an alias for a binary package
            return $this->_registry->deletePackage($package, $channel);
        }
        $filelist = $pkg->getFilelist();
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        if (!class_exists('PEAR_Dependency2')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Dependency2.php';
        }
        $depchecker = &new PEAR_Dependency2($this->config, $options, 
            array('channel' => $channel, 'package' => $package),
            PEAR_VALIDATE_UNINSTALLING);
        $e = $depchecker->validatePackageUninstall($this);
        PEAR::staticPopErrorHandling();
        if (PEAR::isError($e)) {
            if (!isset($options['ignore-errors'])) {
                return $this->raiseError($e);
            } else {
                if (!isset($options['soft'])) {
                    $this->log(0, 'WARNING: ' . $e->getMessage());
                }
            }
        } elseif (is_array($e)) {
            if (!isset($options['soft'])) {
                $this->log(0, $e[0]);
            }
        }
        $this->pkginfo = &$pkg;
        // pretty much nothing happens if we are only registering the uninstall
        if (empty($options['register-only'])) {
            // {{{ Delete the files
            $this->startFileTransaction();
            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
            if (PEAR::isError($err = $this->_deletePackageFiles($package, $channel))) {
                PEAR::popErrorHandling();
                $this->rollbackFileTransaction();
                $this->configSet('default_channel', $savechannel);
                if (!isset($options['ignore-errors'])) {
                    return $this->raiseError($err);
                } else {
                    if (!isset($options['soft'])) {
                        $this->log(0, 'WARNING: ' . $err->getMessage());
                    }
                }
            } else {
                PEAR::popErrorHandling();
            }
            if (!$this->commitFileTransaction()) {
                $this->rollbackFileTransaction();
                if (!isset($options['ignore-errors'])) {
                    return $this->raiseError("uninstall failed");
                } elseif (!isset($options['soft'])) {
                    $this->log(0, 'WARNING: uninstall failed');
                }
            } else {
                $this->startFileTransaction();
                if ($dirtree = $pkg->getDirTree()) {
                    // attempt to delete empty directories
                    uksort($dirtree, array($this, '_sortDirs'));
                    foreach($dirtree as $dir => $notused) {
                        $this->addFileOperation('rmdir', array($dir));
                    }
                } else {
                    $this->configSet('default_channel', $savechannel);
                    return $this->_registry->deletePackage($package, $channel);
                }
                if (!$this->commitFileTransaction()) {
                    $this->rollbackFileTransaction();
                    if (!isset($options['ignore-errors'])) {
                        return $this->raiseError("uninstall failed");
                    } elseif (!isset($options['soft'])) {
                        $this->log(0, 'WARNING: uninstall failed');
                    }
                }
            }
            // }}}
        }

        $this->configSet('default_channel', $savechannel);
        // Register that the package is no longer installed
        return $this->_registry->deletePackage($package, $channel);
    }

    /**
     * Sort a list of arrays of array(downloaded packagefilename) by dependency.
     *
     * It also removes duplicate dependencies
     * @param array an array of PEAR_PackageFile_v[1/2] objects
     * @return array|PEAR_Error array of array(packagefilename, package.xml contents)
     */
    function sortPackagesForUninstall(&$packages)
    {
        $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->config);
        if (PEAR::isError($this->_dependencyDB)) {
            return $this->_dependencyDB;
        }
        usort($packages, array(&$this, '_sortUninstall'));
    }

    function _sortUninstall($a, $b)
    {
        if (!$a->getDeps() && !$b->getDeps()) {
            return 0; // neither package has dependencies, order is insignificant
        }
        if ($a->getDeps() && !$b->getDeps()) {
            return -1; // $a must be installed after $b because $a has dependencies
        }
        if (!$a->getDeps() && $b->getDeps()) {
            return 1; // $b must be installed after $a because $b has dependencies
        }
        // both packages have dependencies
        if ($this->_dependencyDB->dependsOn($a, $b)) {
            return -1;
        }
        if ($this->_dependencyDB->dependsOn($b, $a)) {
            return 1;
        }
        return 0;
    }

    // }}}
    // {{{ _sortDirs()
    function _sortDirs($a, $b)
    {
        if (strnatcmp($a, $b) == -1) return 1;
        if (strnatcmp($a, $b) == 1) return -1;
        return 0;
    }

    // }}}

    // {{{ _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) {
        if (!$fd = @fopen($file, 'r')) {
            return false;
        }
        fclose($fd);
        return md5(file_get_contents($filename));
    }
}
// }}}

?><?php
/**
 * PEAR_Installer_Role
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Role.php,v 1.20 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * base class for installer roles
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Installer/Role/Common.php';
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/XMLParser.php';
/**
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Installer_Role
{
    /**
     * Set up any additional configuration variables that file roles require
     *
     * Never call this directly, it is called by the PEAR_Config constructor
     * @param PEAR_Config
     * @access private
     * @static
     */
    function initializeConfig(&$config)
    {
        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
            PEAR_Installer_Role::registerRoles();
        }
        foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $class => $info) {
            if (!$info['config_vars']) {
                continue;
            }
            $config->_addConfigVars($class, $info['config_vars']);
        }
    }

    /**
     * @param PEAR_PackageFile_v2
     * @param string role name
     * @param PEAR_Config
     * @return PEAR_Installer_Role_Common
     * @static
     */
    function &factory($pkg, $role, &$config)
    {
        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
            PEAR_Installer_Role::registerRoles();
        }
        if (!in_array($role, PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) {
            $a = false;
            return $a;
        }
        $a = 'PEAR_Installer_Role_' . ucfirst($role);
        if (!class_exists($a)) {
            require_once 'phar://install-pear-nozlib.phar/' . str_replace('_', '/', $a) . '.php';
        }
        $b = new $a($config);
        return $b;
    }

    /**
     * Get a list of file roles that are valid for the particular release type.
     *
     * For instance, src files serve no purpose in regular php releases.
     * @param string
     * @param bool clear cache
     * @return array
     * @static
     */
    function getValidRoles($release, $clear = false)
    {
        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
            PEAR_Installer_Role::registerRoles();
        }
        static $ret = array();
        if ($clear) {
            $ret = array();
        }
        if (isset($ret[$release])) {
            return $ret[$release];
        }
        $ret[$release] = array();
        foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
            if (in_array($release, $okreleases['releasetypes'])) {
                $ret[$release][] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
            }
        }
        return $ret[$release];
    }

    /**
     * Get a list of roles that require their files to be installed
     *
     * Most roles must be installed, but src and package roles, for instance
     * are pseudo-roles.  src files are compiled into a new extension.  Package
     * roles are actually fully bundled releases of a package
     * @param bool clear cache
     * @return array
     * @static
     */
    function getInstallableRoles($clear = false)
    {
        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
            PEAR_Installer_Role::registerRoles();
        }
        static $ret;
        if ($clear) {
            unset($ret);
        }
        if (!isset($ret)) {
            $ret = array();
            foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
                if ($okreleases['installable']) {
                    $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
                }
            }
        }
        return $ret;
    }

    /**
     * Return an array of roles that are affected by the baseinstalldir attribute
     *
     * Most roles ignore this attribute, and instead install directly into:
     * PackageName/filepath
     * so a tests file tests/file.phpt is installed into PackageName/tests/filepath.php
     * @param bool clear cache
     * @return array
     * @static
     */
    function getBaseinstallRoles($clear = false)
    {
        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
            PEAR_Installer_Role::registerRoles();
        }
        static $ret;
        if ($clear) {
            unset($ret);
        }
        if (!isset($ret)) {
            $ret = array();
            foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
                if ($okreleases['honorsbaseinstall']) {
                    $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
                }
            }
        }
        return $ret;
    }

    /**
     * Return an array of file roles that should be analyzed for PHP content at package time,
     * like the "php" role.
     * @param bool clear cache
     * @return array
     * @static
     */
    function getPhpRoles($clear = false)
    {
        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
            PEAR_Installer_Role::registerRoles();
        }
        static $ret;
        if ($clear) {
            unset($ret);
        }
        if (!isset($ret)) {
            $ret = array();
            foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
                if ($okreleases['phpfile']) {
                    $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
                }
            }
        }
        return $ret;
    }

    /**
     * Scan through the Command directory looking for classes
     * and see what commands they implement.
     * @param string which directory to look for classes, defaults to
     *               the Installer/Roles subdirectory of
     *               the directory from where this file (__FILE__) is
     *               included.
     *
     * @return bool TRUE on success, a PEAR error on failure
     * @access public
     * @static
     */
    function registerRoles($dir = null)
    {
        $GLOBALS['_PEAR_INSTALLER_ROLES'] = array();
        $parser = new PEAR_XMLParser;
        if ($dir === null) {
            $dir = dirname(__FILE__) . '/Role';
        }
        if (!file_exists($dir) || !is_dir($dir)) {
            return PEAR::raiseError("registerRoles: opendir($dir) failed: does not exist/is not directory");
        }
        $dp = @opendir($dir);
        if (empty($dp)) {
            return PEAR::raiseError("registerRoles: opendir($dir) failed: $php_errmsg");
        }
        while ($entry = readdir($dp)) {
            if ($entry{0} == '.' || substr($entry, -4) != '.xml') {
                continue;
            }
            $class = "PEAR_Installer_Role_".substr($entry, 0, -4);
            // List of roles
            if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'][$class])) {
                $file = "$dir/$entry";
                $parser->parse(file_get_contents($file));
                $data = $parser->getData();
                if (!is_array($data['releasetypes'])) {
                    $data['releasetypes'] = array($data['releasetypes']);
                }
                $GLOBALS['_PEAR_INSTALLER_ROLES'][$class] = $data;
            }
        }
        closedir($dp);
        ksort($GLOBALS['_PEAR_INSTALLER_ROLES']);
        PEAR_Installer_Role::getBaseinstallRoles(true);
        PEAR_Installer_Role::getInstallableRoles(true);
        PEAR_Installer_Role::getPhpRoles(true);
        PEAR_Installer_Role::getValidRoles('****', true);
        return true;
    }
}
?>
<?php
/**
 * Base class for all installation roles.
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2006 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Common.php,v 1.12 2006/10/19 23:55:32 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * Base class for all installation roles.
 *
 * This class allows extensibility of file roles.  Packages with complex
 * customization can now provide custom file roles along with the possibility of
 * adding configuration values to match.
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2006 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Installer_Role_Common
{
    /**
     * @var PEAR_Config
     * @access protected
     */
    var $config;

    /**
     * @param PEAR_Config
     */
    function PEAR_Installer_Role_Common(&$config)
    {
        $this->config = $config;
    }

    /**
     * Retrieve configuration information about a file role from its XML info
     *
     * @param string $role Role Classname, as in "PEAR_Installer_Role_Data"
     * @return array
     */
    function getInfo($role)
    {
        if (empty($GLOBALS['_PEAR_INSTALLER_ROLES'][$role])) {
            return PEAR::raiseError('Unknown Role class: "' . $role . '"');
        }
        return $GLOBALS['_PEAR_INSTALLER_ROLES'][$role];
    }

    /**
     * This is called for each file to set up the directories and files
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @param array attributes from the <file> tag
     * @param string file name
     * @return array an array consisting of:
     *
     *    1 the original, pre-baseinstalldir installation directory
     *    2 the final installation directory
     *    3 the full path to the final location of the file
     *    4 the location of the pre-installation file
     */
    function processInstallation($pkg, $atts, $file, $tmp_path, $layer = null)
    {
        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
        if (PEAR::isError($roleInfo)) {
            return $roleInfo;
        }
        if (!$roleInfo['locationconfig']) {
            return false;
        }
        if ($roleInfo['honorsbaseinstall']) {
            $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'], $layer,
                $pkg->getChannel());
            if (!empty($atts['baseinstalldir'])) {
                $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
            }
        } elseif ($roleInfo['unusualbaseinstall']) {
            $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'],
                    $layer, $pkg->getChannel()) . DIRECTORY_SEPARATOR . $pkg->getPackage();
            if (!empty($atts['baseinstalldir'])) {
                $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
            }
        } else {
            $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'],
                    $layer, $pkg->getChannel()) . DIRECTORY_SEPARATOR . $pkg->getPackage();
        }
        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_dir, $dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
                                                    array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR,
                                                          DIRECTORY_SEPARATOR),
                                                    array($dest_dir, $dest_file, $orig_file));
        return array($save_destdir, $dest_dir, $dest_file, $orig_file);
    }

    /**
     * Get the name of the configuration variable that specifies the location of this file
     * @return string|false
     */
    function getLocationConfig()
    {
        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
        if (PEAR::isError($roleInfo)) {
            return $roleInfo;
        }
        return $roleInfo['locationconfig'];
    }

    /**
     * Do any unusual setup here
     * @param PEAR_Installer
     * @param PEAR_PackageFile_v2
     * @param array file attributes
     * @param string file name
     */
    function setup(&$installer, $pkg, $atts, $file)
    {
    }

    function isExecutable()
    {
        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
        if (PEAR::isError($roleInfo)) {
            return $roleInfo;
        }
        return $roleInfo['executable'];
    }

    function isInstallable()
    {
        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
        if (PEAR::isError($roleInfo)) {
            return $roleInfo;
        }
        return $roleInfo['installable'];
    }

    function isExtension()
    {
        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
        if (PEAR::isError($roleInfo)) {
            return $roleInfo;
        }
        return $roleInfo['phpextension'];
    }
}
?><?php
/**
 * PEAR_Installer_Role_Data
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Data.php,v 1.7 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Installer_Role_Data extends PEAR_Installer_Role_Common {}
?><role version="1.0">
 <releasetypes>php</releasetypes>
 <releasetypes>extsrc</releasetypes>
 <releasetypes>extbin</releasetypes>
 <releasetypes>zendextsrc</releasetypes>
 <releasetypes>zendextbin</releasetypes>
 <installable>1</installable>
 <locationconfig>data_dir</locationconfig>
 <honorsbaseinstall />
 <unusualbaseinstall />
 <phpfile />
 <executable />
 <phpextension />
 <config_vars />
</role><?php
/**
 * PEAR_Installer_Role_Doc
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Doc.php,v 1.7 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Installer_Role_Doc extends PEAR_Installer_Role_Common {}
?><role version="1.0">
 <releasetypes>php</releasetypes>
 <releasetypes>extsrc</releasetypes>
 <releasetypes>extbin</releasetypes>
 <releasetypes>zendextsrc</releasetypes>
 <releasetypes>zendextbin</releasetypes>
 <installable>1</installable>
 <locationconfig>doc_dir</locationconfig>
 <honorsbaseinstall />
 <unusualbaseinstall />
 <phpfile />
 <executable />
 <phpextension />
 <config_vars />
</role><?php
/**
 * PEAR_Installer_Role_Php
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Php.php,v 1.8 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Installer_Role_Php extends PEAR_Installer_Role_Common {}
?><role version="1.0">
 <releasetypes>php</releasetypes>
 <releasetypes>extsrc</releasetypes>
 <releasetypes>extbin</releasetypes>
 <releasetypes>zendextsrc</releasetypes>
 <releasetypes>zendextbin</releasetypes>
 <installable>1</installable>
 <locationconfig>php_dir</locationconfig>
 <honorsbaseinstall>1</honorsbaseinstall>
 <unusualbaseinstall />
 <phpfile>1</phpfile>
 <executable />
 <phpextension />
 <config_vars />
</role><?php
/**
 * PEAR_Installer_Role_Script
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Script.php,v 1.7 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Installer_Role_Script extends PEAR_Installer_Role_Common {}
?><role version="1.0">
 <releasetypes>php</releasetypes>
 <releasetypes>extsrc</releasetypes>
 <releasetypes>extbin</releasetypes>
 <releasetypes>zendextsrc</releasetypes>
 <releasetypes>zendextbin</releasetypes>
 <installable>1</installable>
 <locationconfig>bin_dir</locationconfig>
 <honorsbaseinstall>1</honorsbaseinstall>
 <unusualbaseinstall />
 <phpfile />
 <executable>1</executable>
 <phpextension />
 <config_vars />
</role><?php
/**
 * PEAR_Installer_Role_Test
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Test.php,v 1.7 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Installer_Role_Test extends PEAR_Installer_Role_Common {}
?><role version="1.0">
 <releasetypes>php</releasetypes>
 <releasetypes>extsrc</releasetypes>
 <releasetypes>extbin</releasetypes>
 <releasetypes>zendextsrc</releasetypes>
 <releasetypes>zendextbin</releasetypes>
 <installable>1</installable>
 <locationconfig>test_dir</locationconfig>
 <honorsbaseinstall />
 <unusualbaseinstall />
 <phpfile />
 <executable />
 <phpextension />
 <config_vars />
</role><?php
/**
 * PEAR_PackageFile, package.xml parsing utility class
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: PackageFile.php,v 1.41 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * needed for PEAR_VALIDATE_* constants
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Validate.php';
/**
 * Error code if the package.xml <package> tag does not contain a valid version
 */
define('PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION', 1);
/**
 * Error code if the package.xml <package> tag version is not supported (version 1.0 and 1.1 are the only supported versions,
 * currently
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_PACKAGEVERSION', 2);
/**
 * Abstraction for the package.xml package description file
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_PackageFile
{
    /**
     * @var PEAR_Config
     */
    var $_config;
    var $_debug;
    /**
     * Temp directory for uncompressing tgz files.
     * @var string|false
     */
    var $_tmpdir;
    var $_logger = false;
    /**
     * @var boolean
     */
    var $_rawReturn = false;

    /**
     *
     * @param   PEAR_Config $config
     * @param   ?   $debug
     * @param   string @tmpdir Optional temporary directory for uncompressing
     *          files
     */
    function PEAR_PackageFile(&$config, $debug = false, $tmpdir = false)
    {
        $this->_config = $config;
        $this->_debug = $debug;
        $this->_tmpdir = $tmpdir;
    }

    /**
     * Turn off validation - return a parsed package.xml without checking it
     *
     * This is used by the package-validate command
     */
    function rawReturn()
    {
        $this->_rawReturn = true;
    }

    function setLogger(&$l)
    {
        $this->_logger = &$l;
    }

    /**
     * Create a PEAR_PackageFile_Parser_v* of a given version.
     * @param   int $version
     * @return  PEAR_PackageFile_Parser_v1|PEAR_PackageFile_Parser_v1
     */
    function &parserFactory($version)
    {
        if (!in_array($version{0}, array('1', '2'))) {
            $a = false;
            return $a;
        }
        include_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/Parser/v' . $version{0} . '.php';
        $version = $version{0};
        $class = "PEAR_PackageFile_Parser_v$version";
        $a = new $class;
        return $a;
    }

    /**
     * For simpler unit-testing
     * @return string
     */
    function getClassPrefix()
    {
        return 'PEAR_PackageFile_v';
    }

    /**
     * Create a PEAR_PackageFile_v* of a given version.
     * @param   int $version
     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v1
     */
    function &factory($version)
    {
        if (!in_array($version{0}, array('1', '2'))) {
            $a = false;
            return $a;
        }
        include_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v' . $version{0} . '.php';
        $version = $version{0};
        $class = $this->getClassPrefix() . $version;
        $a = new $class;
        return $a;
    }

    /**
     * Create a PEAR_PackageFile_v* from its toArray() method
     *
     * WARNING: no validation is performed, the array is assumed to be valid,
     * always parse from xml if you want validation.
     * @param   array $arr
     * @return PEAR_PackageFileManager_v1|PEAR_PackageFileManager_v2
     * @uses    factory() to construct the returned object.
     */
    function &fromArray($arr)
    {
        if (isset($arr['xsdversion'])) {
            $obj = &$this->factory($arr['xsdversion']);
            if ($this->_logger) {
                $obj->setLogger($this->_logger);
            }
            $obj->setConfig($this->_config);
            $obj->fromArray($arr);
            return $obj;
        } else {
            if (isset($arr['package']['attribs']['version'])) {
                $obj = &$this->factory($arr['package']['attribs']['version']);
            } else {
                $obj = &$this->factory('1.0');
            }
            if ($this->_logger) {
                $obj->setLogger($this->_logger);
            }
            $obj->setConfig($this->_config);
            $obj->fromArray($arr);
            return $obj;
        }
    }

    /**
     * Create a PEAR_PackageFile_v* from an XML string.
     * @access  public
     * @param   string $data contents of package.xml file
     * @param   int $state package state (one of PEAR_VALIDATE_* constants)
     * @param   string $file full path to the package.xml file (and the files
     *          it references)
     * @param   string $archive optional name of the archive that the XML was
     *          extracted from, if any
     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @uses    parserFactory() to construct a parser to load the package.
     */
    function &fromXmlString($data, $state, $file, $archive = false)
    {
        if (preg_match('/<package[^>]+version="([0-9]+\.[0-9]+)"/', $data, $packageversion)) {
            if (!in_array($packageversion[1], array('1.0', '2.0', '2.1'))) {
                return PEAR::raiseError('package.xml version "' . $packageversion[1] .
                    '" is not supported, only 1.0, 2.0, and 2.1 are supported.');
            }
            $object = &$this->parserFactory($packageversion[1]);
            if ($this->_logger) {
                $object->setLogger($this->_logger);
            }
            $object->setConfig($this->_config);
            $pf = $object->parse($data, $file, $archive);
            if (PEAR::isError($pf)) {
                return $pf;
            }
            if ($this->_rawReturn) {
                return $pf;
            }
            if ($pf->validate($state)) {
                if ($this->_logger) {
                    if ($pf->getValidationWarnings(false)) {
                        foreach ($pf->getValidationWarnings() as $warning) {
                            $this->_logger->log(0, 'WARNING: ' . $warning['message']);
                        }
                    }
                }
                if (method_exists($pf, 'flattenFilelist')) {
                    $pf->flattenFilelist(); // for v2
                }
                return $pf;
            } else {
                if ($this->_config->get('verbose') > 0) {
                    if ($this->_logger) {
                        if ($pf->getValidationWarnings(false)) {
                            foreach ($pf->getValidationWarnings(false) as $warning) {
                                $this->_logger->log(0, 'ERROR: ' . $warning['message']);
                            }
                        }
                    }
                }
                $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed',
                    2, null, null, $pf->getValidationWarnings());
                return $a;
            }
        } elseif (preg_match('/<package[^>]+version="([^"]+)"/', $data, $packageversion)) {
            $a = PEAR::raiseError('package.xml file "' . $file .
                '" has unsupported package.xml <package> version "' . $packageversion[1] . '"');
            return $a;
        } else {
            if (!class_exists('PEAR_ErrorStack')) {
                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ErrorStack.php';
            }
            PEAR_ErrorStack::staticPush('PEAR_PackageFile',
                PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION,
                'warning', array('xml' => $data), 'package.xml "' . $file .
                    '" has no package.xml <package> version');
            $object = &$this->parserFactory('1.0');
            $object->setConfig($this->_config);
            $pf = $object->parse($data, $file, $archive);
            if (PEAR::isError($pf)) {
                return $pf;
            }
            if ($this->_rawReturn) {
                return $pf;
            }
            if ($pf->validate($state)) {
                if ($this->_logger) {
                    if ($pf->getValidationWarnings(false)) {
                        foreach ($pf->getValidationWarnings() as $warning) {
                            $this->_logger->log(0, 'WARNING: ' . $warning['message']);
                        }
                    }
                }
                if (method_exists($pf, 'flattenFilelist')) {
                    $pf->flattenFilelist(); // for v2
                }
                return $pf;
            } else {
                $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed',
                    2, null, null, $pf->getValidationWarnings());
                return $a;
            }
        }
    }

    /**
     * Register a temporary file or directory.  When the destructor is
     * executed, all registered temporary files and directories are
     * removed.
     *
     * @param string  $file  name of file or directory
     * @return  void
     */
    function addTempFile($file)
    {
        $GLOBALS['_PEAR_Common_tempfiles'][] = $file;
    }

    /**
     * Create a PEAR_PackageFile_v* from a compresed Tar or Tgz file.
     * @access  public
     * @param string contents of package.xml file
     * @param int package state (one of PEAR_VALIDATE_* constants)
     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @using   Archive_Tar to extract the files
     * @using   fromPackageFile() to load the package after the package.xml
     *          file is extracted.
     */
    function &fromTgzFile($file, $state)
    {
        if (!class_exists('Archive_Tar')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'Archive/Tar.php';
        }
        $tar = new Archive_Tar($file);
        if ($this->_debug <= 1) {
            $tar->pushErrorHandling(PEAR_ERROR_RETURN);
        }
        $content = $tar->listContent();
        if ($this->_debug <= 1) {
            $tar->popErrorHandling();
        }
        if (!is_array($content)) {
            if (is_string($file) && strlen($file < 255) &&
                  (!file_exists($file) || !@is_file($file))) {
                $ret = PEAR::raiseError("could not open file \"$file\"");
                return $ret;
            }
            $file = realpath($file);
            $ret = PEAR::raiseError("Could not get contents of package \"$file\"".
                                     '. Invalid tgz file.');
            return $ret;
        } else {
            if (!count($content) && !@is_file($file)) {
                $ret = PEAR::raiseError("could not open file \"$file\"");
                return $ret;
            }
        }
        $xml = null;
        $origfile = $file;
        foreach ($content as $file) {
            $name = $file['filename'];
            if ($name == 'package2.xml') { // allow a .tgz to distribute both versions
                $xml = $name;
                break;
            }
            if ($name == 'package.xml') {
                $xml = $name;
                break;
            } elseif (ereg('package.xml$', $name, $match)) {
                $xml = $name;
                break;
            }
        }
        if ($this->_tmpdir) {
            $tmpdir = $this->_tmpdir;
        } else {
            $tmpdir = System::mkTemp(array('-d', 'pear'));
            PEAR_PackageFile::addTempFile($tmpdir);
        }
        $this->_extractErrors();
        PEAR::staticPushErrorHandling(PEAR_ERROR_CALLBACK, array($this, '_extractErrors'));
        if (!$xml || !$tar->extractList(array($xml), $tmpdir)) {
            $extra = implode("\n", $this->_extractErrors());
            if ($extra) {
                $extra = ' ' . $extra;
            }
            PEAR::staticPopErrorHandling();
            $ret = PEAR::raiseError('could not extract the package.xml file from "' .
                $origfile . '"' . $extra);
            return $ret;
        }
        PEAR::staticPopErrorHandling();
        $ret = &PEAR_PackageFile::fromPackageFile("$tmpdir/$xml", $state, $origfile);
        return $ret;
    }

    /**
     * helper for extracting Archive_Tar errors
     * @var array
     * @access private
     */
    var $_extractErrors = array();

    /**
     * helper callback for extracting Archive_Tar errors
     *
     * @param PEAR_Error|null $err
     * @return array
     * @access private
     */
    function _extractErrors($err = null)
    {
        static $errors = array();
        if ($err === null) {
            $e = $errors;
            $errors = array();
            return $e;
        }
        $errors[] = $err->getMessage();
    }

    /**
     * Create a PEAR_PackageFile_v* from a package.xml file.
     *
     * @access public
     * @param   string  $descfile  name of package xml file
     * @param   int     $state package state (one of PEAR_VALIDATE_* constants)
     * @param   string|false $archive name of the archive this package.xml came
     *          from, if any
     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @uses    PEAR_PackageFile::fromXmlString to create the oject after the
     *          XML is loaded from the package.xml file.
     */
    function &fromPackageFile($descfile, $state, $archive = false)
    {
        if (is_string($descfile) && strlen($descfile) < 255 &&
             (!file_exists($descfile) || !is_file($descfile) || !is_readable($descfile) ||
             (!$fp = @fopen($descfile, 'r')))) {
            $a = PEAR::raiseError("Unable to open $descfile");
            return $a;
        }

        // read the whole thing so we only get one cdata callback
        // for each block of cdata
        fclose($fp);
        $data = file_get_contents($descfile);
        $ret = &PEAR_PackageFile::fromXmlString($data, $state, $descfile, $archive);
        return $ret;
    }


    /**
     * Create a PEAR_PackageFile_v* from a .tgz archive or package.xml file.
     *
     * This method is able to extract information about a package from a .tgz
     * archive or from a XML package definition file.
     *
     * @access public
     * @param   string  $info file name
     * @param   int     $state package state (one of PEAR_VALIDATE_* constants)
     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @uses    fromPackageFile() if the file appears to be XML
     * @uses    fromTgzFile() to load all non-XML files
     */
    function &fromAnyFile($info, $state)
    {
        if (is_dir($info)) {
            $dir_name = realpath($info);
            if (file_exists($dir_name . '/package.xml')) {
                $info = PEAR_PackageFile::fromPackageFile($dir_name .  '/package.xml', $state);
            } elseif (file_exists($dir_name .  '/package2.xml')) {
                $info = PEAR_PackageFile::fromPackageFile($dir_name .  '/package2.xml', $state);
            } else {
                $info = PEAR::raiseError("No package definition found in '$info' directory");
            }
            return $info;
        }

        $fp = false;
        if (is_string($info) && strlen($info) < 255 &&
             (file_exists($info) || ($fp = @fopen($info, 'r')))) {
            if ($fp) {
                fclose($fp);
            }
            $tmp = substr($info, -4);
            if ($tmp == '.xml') {
                $info = &PEAR_PackageFile::fromPackageFile($info, $state);
            } elseif ($tmp == '.tar' || $tmp == '.tgz') {
                $info = &PEAR_PackageFile::fromTgzFile($info, $state);
            } else {
                $fp = fopen($info, "r");
                $test = fread($fp, 5);
                fclose($fp);
                if ($test == "<?xml") {
                    $info = &PEAR_PackageFile::fromPackageFile($info, $state);
                } else {
                    $info = &PEAR_PackageFile::fromTgzFile($info, $state);
                }
            }
        } else {
            $info = PEAR::raiseError("Cannot open '$info' for parsing");
            return $info;
        }
        return $info;
    }
}

?>
<?php
/**
 * package.xml generation class, package.xml version 1.0
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: v1.php,v 1.74 2008/01/03 20:26:37 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * needed for PEAR_VALIDATE_* constants
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Validate.php';
require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2.php';
/**
 * This class converts a PEAR_PackageFile_v1 object into any output format.
 *
 * Supported output formats include array, XML string, and a PEAR_PackageFile_v2
 * object, for converting package.xml 1.0 into package.xml 2.0 with no sweat.
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_PackageFile_Generator_v1
{
    /**
     * @var PEAR_PackageFile_v1
     */
    var $_packagefile;
    function PEAR_PackageFile_Generator_v1(&$packagefile)
    {
        $this->_packagefile = &$packagefile;
    }

    function getPackagerVersion()
    {
        return '1.7.2';
    }

    /**
     * @param PEAR_Packager
     * @param bool if true, a .tgz is written, otherwise a .tar is written
     * @param string|null directory in which to save the .tgz
     * @return string|PEAR_Error location of package or error object
     */
    function toTgz(&$packager, $compress = true, $where = null)
    {
        require_once 'phar://install-pear-nozlib.phar/' . 'Archive/Tar.php';
        if ($where === null) {
            if (!($where = System::mktemp(array('-d')))) {
                return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: mktemp failed');
            }
        } elseif (!@System::mkDir(array('-p', $where))) {
            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: "' . $where . '" could' .
                ' not be created');
        }
        if (file_exists($where . DIRECTORY_SEPARATOR . 'package.xml') &&
              !is_file($where . DIRECTORY_SEPARATOR . 'package.xml')) {
            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: unable to save package.xml as' .
                ' "' . $where . DIRECTORY_SEPARATOR . 'package.xml"');
        }
        if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) {
            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: invalid package file');
        }
        $pkginfo = $this->_packagefile->getArray();
        $ext = $compress ? '.tgz' : '.tar';
        $pkgver = $pkginfo['package'] . '-' . $pkginfo['version'];
        $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext;
        if (file_exists(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext) &&
              !is_file(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext)) {
            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: cannot create tgz file "' .
                getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext . '"');
        }
        if ($pkgfile = $this->_packagefile->getPackageFile()) {
            $pkgdir = dirname(realpath($pkgfile));
            $pkgfile = basename($pkgfile);
        } else {
            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: package file object must ' .
                'be created from a real file');
        }
        // {{{ Create the package file list
        $filelist = array();
        $i = 0;

        foreach ($this->_packagefile->getFilelist() as $fname => $atts) {
            $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
            if (!file_exists($file)) {
                return PEAR::raiseError("File does not exist: $fname");
            } else {
                $filelist[$i++] = $file;
                if (!isset($atts['md5sum'])) {
                    $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($file));
                }
                $packager->log(2, "Adding file $fname");
            }
        }
        // }}}
        $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, 'package.xml', true);
        if ($packagexml) {
            $tar =& new Archive_Tar($dest_package, $compress);
            $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors
            // ----- Creates with the package.xml file
            $ok = $tar->createModify(array($packagexml), '', $where);
            if (PEAR::isError($ok)) {
                return $ok;
            } elseif (!$ok) {
                return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: tarball creation failed');
            }
            // ----- Add the content of the package
            if (!$tar->addModify($filelist, $pkgver, $pkgdir)) {
                return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: tarball creation failed');
            }
            return $dest_package;
        }
    }

    /**
     * @param string|null directory to place the package.xml in, or null for a temporary dir
     * @param int one of the PEAR_VALIDATE_* constants
     * @param string name of the generated file
     * @param bool if true, then no analysis will be performed on role="php" files
     * @return string|PEAR_Error path to the created file on success
     */
    function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'package.xml',
                           $nofilechecking = false)
    {
        if (!$this->_packagefile->validate($state, $nofilechecking)) {
            return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: invalid package.xml',
                null, null, null, $this->_packagefile->getValidationWarnings());
        }
        if ($where === null) {
            if (!($where = System::mktemp(array('-d')))) {
                return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: mktemp failed');
            }
        } elseif (!@System::mkDir(array('-p', $where))) {
            return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: "' . $where . '" could' .
                ' not be created');
        }
        $newpkgfile = $where . DIRECTORY_SEPARATOR . $name;
        $np = @fopen($newpkgfile, 'wb');
        if (!$np) {
            return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: unable to save ' .
               "$name as $newpkgfile");
        }
        fwrite($np, $this->toXml($state, true));
        fclose($np);
        return $newpkgfile;
    }

    /**
     * fix both XML encoding to be UTF8, and replace standard XML entities < > " & '
     *
     * @param string $string
     * @return string
     * @access private
     */
    function _fixXmlEncoding($string)
    {
        if (version_compare(phpversion(), '5.0.0', 'lt')) {
            $string = utf8_encode($string);
        }
        return strtr($string, array(
                                          '&'  => '&amp;',
                                          '>'  => '&gt;',
                                          '<'  => '&lt;',
                                          '"'  => '&quot;',
                                          '\'' => '&apos;' ));
    }

    /**
     * Return an XML document based on the package info (as returned
     * by the PEAR_Common::infoFrom* methods).
     *
     * @return string XML data
     */
    function toXml($state = PEAR_VALIDATE_NORMAL, $nofilevalidation = false)
    {
        $this->_packagefile->setDate(date('Y-m-d'));
        if (!$this->_packagefile->validate($state, $nofilevalidation)) {
            return false;
        }
        $pkginfo = $this->_packagefile->getArray();
        static $maint_map = array(
            "handle" => "user",
            "name" => "name",
            "email" => "email",
            "role" => "role",
            );
        $ret = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
        $ret .= "<!DOCTYPE package SYSTEM \"http://pear.php.net/dtd/package-1.0\">\n";
        $ret .= "<package version=\"1.0\" packagerversion=\"1.7.2\">\n" .
" <name>$pkginfo[package]</name>";
        if (isset($pkginfo['extends'])) {
            $ret .= "\n<extends>$pkginfo[extends]</extends>";
        }
        $ret .=
 "\n <summary>".$this->_fixXmlEncoding($pkginfo['summary'])."</summary>\n" .
" <description>".trim($this->_fixXmlEncoding($pkginfo['description']))."\n </description>\n" .
" <maintainers>\n";
        foreach ($pkginfo['maintainers'] as $maint) {
            $ret .= "  <maintainer>\n";
            foreach ($maint_map as $idx => $elm) {
                $ret .= "   <$elm>";
                $ret .= $this->_fixXmlEncoding($maint[$idx]);
                $ret .= "</$elm>\n";
            }
            $ret .= "  </maintainer>\n";
        }
        $ret .= "  </maintainers>\n";
        $ret .= $this->_makeReleaseXml($pkginfo, false, $state);
        if (isset($pkginfo['changelog']) && count($pkginfo['changelog']) > 0) {
            $ret .= " <changelog>\n";
            foreach ($pkginfo['changelog'] as $oldrelease) {
                $ret .= $this->_makeReleaseXml($oldrelease, true);
            }
            $ret .= " </changelog>\n";
        }
        $ret .= "</package>\n";
        return $ret;
    }

    // }}}
    // {{{ _makeReleaseXml()

    /**
     * Generate part of an XML description with release information.
     *
     * @param array  $pkginfo    array with release information
     * @param bool   $changelog  whether the result will be in a changelog element
     *
     * @return string XML data
     *
     * @access private
     */
    function _makeReleaseXml($pkginfo, $changelog = false, $state = PEAR_VALIDATE_NORMAL)
    {
        // XXX QUOTE ENTITIES IN PCDATA, OR EMBED IN CDATA BLOCKS!!
        $indent = $changelog ? "  " : "";
        $ret = "$indent <release>\n";
        if (!empty($pkginfo['version'])) {
            $ret .= "$indent  <version>$pkginfo[version]</version>\n";
        }
        if (!empty($pkginfo['release_date'])) {
            $ret .= "$indent  <date>$pkginfo[release_date]</date>\n";
        }
        if (!empty($pkginfo['release_license'])) {
            $ret .= "$indent  <license>$pkginfo[release_license]</license>\n";
        }
        if (!empty($pkginfo['release_state'])) {
            $ret .= "$indent  <state>$pkginfo[release_state]</state>\n";
        }
        if (!empty($pkginfo['release_notes'])) {
            $ret .= "$indent  <notes>".trim($this->_fixXmlEncoding($pkginfo['release_notes']))
            ."\n$indent  </notes>\n";
        }
        if (!empty($pkginfo['release_warnings'])) {
            $ret .= "$indent  <warnings>".$this->_fixXmlEncoding($pkginfo['release_warnings'])."</warnings>\n";
        }
        if (isset($pkginfo['release_deps']) && sizeof($pkginfo['release_deps']) > 0) {
            $ret .= "$indent  <deps>\n";
            foreach ($pkginfo['release_deps'] as $dep) {
                $ret .= "$indent   <dep type=\"$dep[type]\" rel=\"$dep[rel]\"";
                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 {
                    $ret .= "/>\n";
                }
            }
            $ret .= "$indent  </deps>\n";
        }
        if (isset($pkginfo['configure_options'])) {
            $ret .= "$indent  <configureoptions>\n";
            foreach ($pkginfo['configure_options'] as $c) {
                $ret .= "$indent   <configureoption name=\"".
                    $this->_fixXmlEncoding($c['name']) . "\"";
                if (isset($c['default'])) {
                    $ret .= " default=\"" . $this->_fixXmlEncoding($c['default']) . "\"";
                }
                $ret .= " prompt=\"" . $this->_fixXmlEncoding($c['prompt']) . "\"";
                $ret .= "/>\n";
            }
            $ret .= "$indent  </configureoptions>\n";
        }
        if (isset($pkginfo['provides'])) {
            foreach ($pkginfo['provides'] as $key => $what) {
                $ret .= "$indent  <provides type=\"$what[type]\" ";
                $ret .= "name=\"$what[name]\" ";
                if (isset($what['extends'])) {
                    $ret .= "extends=\"$what[extends]\" ";
                }
                $ret .= "/>\n";
            }
        }
        if (isset($pkginfo['filelist'])) {
            $ret .= "$indent  <filelist>\n";
            if ($state ^ PEAR_VALIDATE_PACKAGING) {
                $ret .= $this->recursiveXmlFilelist($pkginfo['filelist']);
            } else {
                foreach ($pkginfo['filelist'] as $file => $fa) {
                    if (!isset($fa['role'])) {
                        $fa['role'] = '';
                    }
                    $ret .= "$indent   <file role=\"$fa[role]\"";
                    if (isset($fa['baseinstalldir'])) {
                        $ret .= ' baseinstalldir="' .
                            $this->_fixXmlEncoding($fa['baseinstalldir']) . '"';
                    }
                    if (isset($fa['md5sum'])) {
                        $ret .= " md5sum=\"$fa[md5sum]\"";
                    }
                    if (isset($fa['platform'])) {
                        $ret .= " platform=\"$fa[platform]\"";
                    }
                    if (!empty($fa['install-as'])) {
                        $ret .= ' install-as="' .
                            $this->_fixXmlEncoding($fa['install-as']) . '"';
                    }
                    $ret .= ' name="' . $this->_fixXmlEncoding($file) . '"';
                    if (empty($fa['replacements'])) {
                        $ret .= "/>\n";
                    } else {
                        $ret .= ">\n";
                        foreach ($fa['replacements'] as $r) {
                            $ret .= "$indent    <replace";
                            foreach ($r as $k => $v) {
                                $ret .= " $k=\"" . $this->_fixXmlEncoding($v) .'"';
                            }
                            $ret .= "/>\n";
                        }
                        $ret .= "$indent   </file>\n";
                    }
                }
            }
            $ret .= "$indent  </filelist>\n";
        }
        $ret .= "$indent </release>\n";
        return $ret;
    }

    /**
     * @param array
     * @access protected
     */
    function recursiveXmlFilelist($list)
    {
        $this->_dirs = array();
        foreach ($list as $file => $attributes) {
            $this->_addDir($this->_dirs, explode('/', dirname($file)), $file, $attributes);
        }
        return $this->_formatDir($this->_dirs);
    }

    /**
     * @param array
     * @param array
     * @param string|null
     * @param array|null
     * @access private
     */
    function _addDir(&$dirs, $dir, $file = null, $attributes = null)
    {
        if ($dir == array() || $dir == array('.')) {
            $dirs['files'][basename($file)] = $attributes;
            return;
        }
        $curdir = array_shift($dir);
        if (!isset($dirs['dirs'][$curdir])) {
            $dirs['dirs'][$curdir] = array();
        }
        $this->_addDir($dirs['dirs'][$curdir], $dir, $file, $attributes);
    }

    /**
     * @param array
     * @param string
     * @param string
     * @access private
     */
    function _formatDir($dirs, $indent = '', $curdir = '')
    {
        $ret = '';
        if (!count($dirs)) {
            return '';
        }
        if (isset($dirs['dirs'])) {
            uksort($dirs['dirs'], 'strnatcasecmp');
            foreach ($dirs['dirs'] as $dir => $contents) {
                $usedir = "$curdir/$dir";
                $ret .= "$indent   <dir name=\"$dir\">\n";
                $ret .= $this->_formatDir($contents, "$indent ", $usedir);
                $ret .= "$indent   </dir> <!-- $usedir -->\n";
            }
        }
        if (isset($dirs['files'])) {
            uksort($dirs['files'], 'strnatcasecmp');
            foreach ($dirs['files'] as $file => $attribs) {
                $ret .= $this->_formatFile($file, $attribs, $indent);
            }
        }
        return $ret;
    }

    /**
     * @param string
     * @param array
     * @param string
     * @access private
     */
    function _formatFile($file, $attributes, $indent)
    {
        $ret = "$indent   <file role=\"$attributes[role]\"";
        if (isset($attributes['baseinstalldir'])) {
            $ret .= ' baseinstalldir="' .
                $this->_fixXmlEncoding($attributes['baseinstalldir']) . '"';
        }
        if (isset($attributes['md5sum'])) {
            $ret .= " md5sum=\"$attributes[md5sum]\"";
        }
        if (isset($attributes['platform'])) {
            $ret .= " platform=\"$attributes[platform]\"";
        }
        if (!empty($attributes['install-as'])) {
            $ret .= ' install-as="' .
                $this->_fixXmlEncoding($attributes['install-as']) . '"';
        }
        $ret .= ' name="' . $this->_fixXmlEncoding($file) . '"';
        if (empty($attributes['replacements'])) {
            $ret .= "/>\n";
        } else {
            $ret .= ">\n";
            foreach ($attributes['replacements'] as $r) {
                $ret .= "$indent    <replace";
                foreach ($r as $k => $v) {
                    $ret .= " $k=\"" . $this->_fixXmlEncoding($v) .'"';
                }
                $ret .= "/>\n";
            }
            $ret .= "$indent   </file>\n";
        }
        return $ret;
    }

    // {{{ _unIndent()

    /**
     * Unindent given string (?)
     *
     * @param string $str The string that has to be unindented.
     * @return string
     * @access private
     */
    function _unIndent($str)
    {
        // remove leading newlines
        $str = preg_replace('/^[\r\n]+/', '', $str);
        // find whitespace at the beginning of the first line
        $indent_len = strspn($str, " \t");
        $indent = substr($str, 0, $indent_len);
        $data = '';
        // remove the same amount of whitespace from following lines
        foreach (explode("\n", $str) as $line) {
            if (substr($line, 0, $indent_len) == $indent) {
                $data .= substr($line, $indent_len) . "\n";
            }
        }
        return $data;
    }

    /**
     * @return array
     */
    function dependenciesToV2()
    {
        $arr = array();
        $this->_convertDependencies2_0($arr);
        return $arr['dependencies'];
    }

    /**
     * Convert a package.xml version 1.0 into version 2.0
     *
     * Note that this does a basic conversion, to allow more advanced
     * features like bundles and multiple releases
     * @param string the classname to instantiate and return.  This must be
     *               PEAR_PackageFile_v2 or a descendant
     * @param boolean if true, only valid, deterministic package.xml 1.0 as defined by the
     *                strictest parameters will be converted
     * @return PEAR_PackageFile_v2|PEAR_Error
     */
    function &toV2($class = 'PEAR_PackageFile_v2', $strict = false)
    {
        if ($strict) {
            if (!$this->_packagefile->validate()) {
                $a = PEAR::raiseError('invalid package.xml version 1.0 cannot be converted' .
                    ' to version 2.0', null, null, null,
                    $this->_packagefile->getValidationWarnings(true));
                return $a;
            }
        }
        $arr = array(
            'attribs' => array(
                             'version' => '2.0',
                             'xmlns' => 'http://pear.php.net/dtd/package-2.0',
                             'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
                             'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
                             'xsi:schemaLocation' => "http://pear.php.net/dtd/tasks-1.0\n" .
"http://pear.php.net/dtd/tasks-1.0.xsd\n" .
"http://pear.php.net/dtd/package-2.0\n" .
'http://pear.php.net/dtd/package-2.0.xsd',
                         ),
            'name' => $this->_packagefile->getPackage(),
            'channel' => 'pear.php.net',
        );
        $arr['summary'] = $this->_packagefile->getSummary();
        $arr['description'] = $this->_packagefile->getDescription();
        $maintainers = $this->_packagefile->getMaintainers();
        foreach ($maintainers as $maintainer) {
            if ($maintainer['role'] != 'lead') {
                continue;
            }
            $new = array(
                'name' => $maintainer['name'],
                'user' => $maintainer['handle'],
                'email' => $maintainer['email'],
                'active' => 'yes',
            );
            $arr['lead'][] = $new;
        }
        if (!isset($arr['lead'])) { // some people... you know?
            $arr['lead'] = array(
                'name' => 'unknown',
                'user' => 'unknown',
                'email' => 'noleadmaintainer@example.com',
                'active' => 'no',
            );
        }
        if (count($arr['lead']) == 1) {
            $arr['lead'] = $arr['lead'][0];
        }
        foreach ($maintainers as $maintainer) {
            if ($maintainer['role'] == 'lead') {
                continue;
            }
            $new = array(
                'name' => $maintainer['name'],
                'user' => $maintainer['handle'],
                'email' => $maintainer['email'],
                'active' => 'yes',
            );
            $arr[$maintainer['role']][] = $new;
        }
        if (isset($arr['developer']) && count($arr['developer']) == 1) {
            $arr['developer'] = $arr['developer'][0];
        }
        if (isset($arr['contributor']) && count($arr['contributor']) == 1) {
            $arr['contributor'] = $arr['contributor'][0];
        }
        if (isset($arr['helper']) && count($arr['helper']) == 1) {
            $arr['helper'] = $arr['helper'][0];
        }
        $arr['date'] = $this->_packagefile->getDate();
        $arr['version'] =
            array(
                'release' => $this->_packagefile->getVersion(),
                'api' => $this->_packagefile->getVersion(),
            );
        $arr['stability'] =
            array(
                'release' => $this->_packagefile->getState(),
                'api' => $this->_packagefile->getState(),
            );
        $licensemap =
            array(
                'php' => 'http://www.php.net/license',
                'php license' => 'http://www.php.net/license',
                'lgpl' => 'http://www.gnu.org/copyleft/lesser.html',
                'bsd' => 'http://www.opensource.org/licenses/bsd-license.php',
                'bsd style' => 'http://www.opensource.org/licenses/bsd-license.php',
                'bsd-style' => 'http://www.opensource.org/licenses/bsd-license.php',
                'mit' => 'http://www.opensource.org/licenses/mit-license.php',
                'gpl' => 'http://www.gnu.org/copyleft/gpl.html',
                'apache' => 'http://www.opensource.org/licenses/apache2.0.php'
            );
        if (isset($licensemap[strtolower($this->_packagefile->getLicense())])) {
            $arr['license'] = array(
                'attribs' => array('uri' =>
                    $licensemap[strtolower($this->_packagefile->getLicense())]),
                '_content' => $this->_packagefile->getLicense()
                );
        } else {
            // don't use bogus uri
            $arr['license'] = $this->_packagefile->getLicense();
        }
        $arr['notes'] = $this->_packagefile->getNotes();
        $temp = array();
        $arr['contents'] = $this->_convertFilelist2_0($temp);
        $this->_convertDependencies2_0($arr);
        $release = ($this->_packagefile->getConfigureOptions() || $this->_isExtension) ?
            'extsrcrelease' : 'phprelease';
        if ($release == 'extsrcrelease') {
            $arr['channel'] = 'pecl.php.net';
            $arr['providesextension'] = $arr['name']; // assumption
        }
        $arr[$release] = array();
        if ($this->_packagefile->getConfigureOptions()) {
            $arr[$release]['configureoption'] = $this->_packagefile->getConfigureOptions();
            foreach ($arr[$release]['configureoption'] as $i => $opt) {
                $arr[$release]['configureoption'][$i] = array('attribs' => $opt);
            }
            if (count($arr[$release]['configureoption']) == 1) {
                $arr[$release]['configureoption'] = $arr[$release]['configureoption'][0];
            }
        }
        $this->_convertRelease2_0($arr[$release], $temp);
        if ($release == 'extsrcrelease' && count($arr[$release]) > 1) {
            // multiple extsrcrelease tags added in PEAR 1.4.1
            $arr['dependencies']['required']['pearinstaller']['min'] = '1.4.1';
        }
        if ($cl = $this->_packagefile->getChangelog()) {
            foreach ($cl as $release) {
                $rel = array();
                $rel['version'] =
                    array(
                        'release' => $release['version'],
                        'api' => $release['version'],
                    );
                if (!isset($release['release_state'])) {
                    $release['release_state'] = 'stable';
                }
                $rel['stability'] =
                    array(
                        'release' => $release['release_state'],
                        'api' => $release['release_state'],
                    );
                if (isset($release['release_date'])) {
                    $rel['date'] = $release['release_date'];
                } else {
                    $rel['date'] = date('Y-m-d');
                }
                if (isset($release['release_license'])) {
                    if (isset($licensemap[strtolower($release['release_license'])])) {
                        $uri = $licensemap[strtolower($release['release_license'])];
                    } else {
                        $uri = 'http://www.example.com';
                    }
                    $rel['license'] = array(
                            'attribs' => array('uri' => $uri),
                            '_content' => $release['release_license']
                        );
                } else {
                    $rel['license'] = $arr['license'];
                }
                if (!isset($release['release_notes'])) {
                    $release['release_notes'] = 'no release notes';
                }
                $rel['notes'] = $release['release_notes'];
                $arr['changelog']['release'][] = $rel;
            }
        }
        $ret = new $class;
        $ret->setConfig($this->_packagefile->_config);
        if (isset($this->_packagefile->_logger) && is_object($this->_packagefile->_logger)) {
            $ret->setLogger($this->_packagefile->_logger);
        }
        $ret->fromArray($arr);
        return $ret;
    }

    /**
     * @param array
     * @param bool
     * @access private
     */
    function _convertDependencies2_0(&$release, $internal = false)
    {
        $peardep = array('pearinstaller' =>
            array('min' => '1.4.0b1')); // this is a lot safer
        $required = $optional = array();
        $release['dependencies'] = array('required' => array());
        if ($this->_packagefile->hasDeps()) {
            foreach ($this->_packagefile->getDeps() as $dep) {
                if (!isset($dep['optional']) || $dep['optional'] == 'no') {
                    $required[] = $dep;
                } else {
                    $optional[] = $dep;
                }
            }
            foreach (array('required', 'optional') as $arr) {
                $deps = array();
                foreach ($$arr as $dep) {
                    // organize deps by dependency type and name
                    if (!isset($deps[$dep['type']])) {
                        $deps[$dep['type']] = array();
                    }
                    if (isset($dep['name'])) {
                        $deps[$dep['type']][$dep['name']][] = $dep;
                    } else {
                        $deps[$dep['type']][] = $dep;
                    }
                }
                do {
                    if (isset($deps['php'])) {
                        $php = array();
                        if (count($deps['php']) > 1) {
                            $php = $this->_processPhpDeps($deps['php']);
                        } else {
                            if (!isset($deps['php'][0])) {
                                list($key, $blah) = each ($deps['php']); // stupid buggy versions
                                $deps['php'] = array($blah[0]);
                            }
                            $php = $this->_processDep($deps['php'][0]);
                            if (!$php) {
                                break; // poor mans throw
                            }
                        }
                        $release['dependencies'][$arr]['php'] = $php;
                    }
                } while (false);
                do {
                    if (isset($deps['pkg'])) {
                        $pkg = array();
                        $pkg = $this->_processMultipleDepsName($deps['pkg']);
                        if (!$pkg) {
                            break; // poor mans throw
                        }
                        $release['dependencies'][$arr]['package'] = $pkg;
                    }
                } while (false);
                do {
                    if (isset($deps['ext'])) {
                        $pkg = array();
                        $pkg = $this->_processMultipleDepsName($deps['ext']);
                        $release['dependencies'][$arr]['extension'] = $pkg;
                    }
                } while (false);
                // skip sapi - it's not supported so nobody will have used it
                // skip os - it's not supported in 1.0
            }
        }
        if (isset($release['dependencies']['required'])) {
            $release['dependencies']['required'] =
                array_merge($peardep, $release['dependencies']['required']);
        } else {
            $release['dependencies']['required'] = $peardep;
        }
        if (!isset($release['dependencies']['required']['php'])) {
            $release['dependencies']['required']['php'] =
                array('min' => '4.0.0');
        }
        $order = array();
        $bewm = $release['dependencies']['required'];
        $order['php'] = $bewm['php'];
        $order['pearinstaller'] = $bewm['pearinstaller'];
        isset($bewm['package']) ? $order['package'] = $bewm['package'] :0;
        isset($bewm['extension']) ? $order['extension'] = $bewm['extension'] :0;
        $release['dependencies']['required'] = $order;
    }

    /**
     * @param array
     * @access private
     */
    function _convertFilelist2_0(&$package)
    {
        $ret = array('dir' =>
                    array(
                        'attribs' => array('name' => '/'),
                        'file' => array()
                        )
                    );
        $package['platform'] =
        $package['install-as'] = array();
        $this->_isExtension = false;
        foreach ($this->_packagefile->getFilelist() as $name => $file) {
            $file['name'] = $name;
            if (isset($file['role']) && $file['role'] == 'src') {
                $this->_isExtension = true;
            }
            if (isset($file['replacements'])) {
                $repl = $file['replacements'];
                unset($file['replacements']);
            } else {
                unset($repl);
            }
            if (isset($file['install-as'])) {
                $package['install-as'][$name] = $file['install-as'];
                unset($file['install-as']);
            }
            if (isset($file['platform'])) {
                $package['platform'][$name] = $file['platform'];
                unset($file['platform']);
            }
            $file = array('attribs' => $file);
            if (isset($repl)) {
                foreach ($repl as $replace ) {
                    $file['tasks:replace'][] = array('attribs' => $replace);
                }
                if (count($repl) == 1) {
                    $file['tasks:replace'] = $file['tasks:replace'][0];
                }
            }
            $ret['dir']['file'][] = $file;
        }
        return $ret;
    }

    /**
     * Post-process special files with install-as/platform attributes and
     * make the release tag.
     * 
     * This complex method follows this work-flow to create the release tags:
     * 
     * <pre>
     * - if any install-as/platform exist, create a generic release and fill it with
     *   o <install as=..> tags for <file name=... install-as=...>
     *   o <install as=..> tags for <file name=... platform=!... install-as=..>
     *   o <ignore> tags for <file name=... platform=...>
     *   o <ignore> tags for <file name=... platform=... install-as=..>
     * - create a release for each platform encountered and fill with
     *   o <install as..> tags for <file name=... install-as=...>
     *   o <install as..> tags for <file name=... platform=this platform install-as=..>
     *   o <install as..> tags for <file name=... platform=!other platform install-as=..>
     *   o <ignore> tags for <file name=... platform=!this platform>
     *   o <ignore> tags for <file name=... platform=other platform>
     *   o <ignore> tags for <file name=... platform=other platform install-as=..>
     *   o <ignore> tags for <file name=... platform=!this platform install-as=..>
     * </pre>
     * 
     * It does this by accessing the $package parameter, which contains an array with
     * indices:
     * 
     *  - platform: mapping of file => OS the file should be installed on
     *  - install-as: mapping of file => installed name
     *  - osmap: mapping of OS => list of files that should be installed
     *    on that OS
     *  - notosmap: mapping of OS => list of files that should not be
     *    installed on that OS
     *
     * @param array
     * @param array
     * @access private
     */
    function _convertRelease2_0(&$release, $package)
    {
        //- if any install-as/platform exist, create a generic release and fill it with 
        if (count($package['platform']) || count($package['install-as'])) {
            $generic = array();
            $genericIgnore = array();
            foreach ($package['install-as'] as $file => $as) {
                //o <install as=..> tags for <file name=... install-as=...>
                if (!isset($package['platform'][$file])) {
                    $generic[] = $file;
                    continue;
                }
                //o <install as=..> tags for <file name=... platform=!... install-as=..>
                if (isset($package['platform'][$file]) &&
                      $package['platform'][$file]{0} == '!') {
                    $generic[] = $file;
                    continue;
                }
                //o <ignore> tags for <file name=... platform=... install-as=..>
                if (isset($package['platform'][$file]) &&
                      $package['platform'][$file]{0} != '!') {
                    $genericIgnore[] = $file;
                    continue;
                }
            }
            foreach ($package['platform'] as $file => $platform) {
                if (isset($package['install-as'][$file])) {
                    continue;
                }
                if ($platform{0} != '!') {
                    //o <ignore> tags for <file name=... platform=...>
                    $genericIgnore[] = $file;
                }
            }
            if (count($package['platform'])) {
                $oses = $notplatform = $platform = array();
                foreach ($package['platform'] as $file => $os) {
                    // get a list of oses
                    if ($os{0} == '!') {
                        if (isset($oses[substr($os, 1)])) {
                            continue;
                        }
                        $oses[substr($os, 1)] = count($oses);
                    } else {
                        if (isset($oses[$os])) {
                            continue;
                        }
                        $oses[$os] = count($oses);
                    }
                }
                //- create a release for each platform encountered and fill with
                foreach ($oses as $os => $releaseNum) {
                    $release[$releaseNum]['installconditions']['os']['name'] = $os;
                    $release[$releaseNum]['filelist'] = array('install' => array(),
                        'ignore' => array());
                    foreach ($package['install-as'] as $file => $as) {
                        //o <install as=..> tags for <file name=... install-as=...>
                        if (!isset($package['platform'][$file])) {
                            $release[$releaseNum]['filelist']['install'][] =
                                array(
                                    'attribs' => array(
                                        'name' => $file,
                                        'as' => $as,
                                    ),
                                );
                            continue;
                        }
                        //o <install as..> tags for
                        //  <file name=... platform=this platform install-as=..>
                        if (isset($package['platform'][$file]) &&
                              $package['platform'][$file] == $os) {
                            $release[$releaseNum]['filelist']['install'][] =
                                array(
                                    'attribs' => array(
                                        'name' => $file,
                                        'as' => $as,
                                    ),
                                );
                            continue;
                        }
                        //o <install as..> tags for
                        //  <file name=... platform=!other platform install-as=..>
                        if (isset($package['platform'][$file]) &&
                              $package['platform'][$file] != "!$os" &&
                              $package['platform'][$file]{0} == '!') {
                            $release[$releaseNum]['filelist']['install'][] =
                                array(
                                    'attribs' => array(
                                        'name' => $file,
                                        'as' => $as,
                                    ),
                                );
                            continue;
                        }
                        //o <ignore> tags for
                        //  <file name=... platform=!this platform install-as=..>
                        if (isset($package['platform'][$file]) &&
                              $package['platform'][$file] == "!$os") {
                            $release[$releaseNum]['filelist']['ignore'][] =
                                array(
                                    'attribs' => array(
                                        'name' => $file,
                                    ),
                                );
                            continue;
                        }
                        //o <ignore> tags for
                        //  <file name=... platform=other platform install-as=..>
                        if (isset($package['platform'][$file]) &&
                              $package['platform'][$file]{0} != '!' &&
                              $package['platform'][$file] != $os) {
                            $release[$releaseNum]['filelist']['ignore'][] =
                                array(
                                    'attribs' => array(
                                        'name' => $file,
                                    ),
                                );
                            continue;
                        }
                    }
                    foreach ($package['platform'] as $file => $platform) {
                        if (isset($package['install-as'][$file])) {
                            continue;
                        }
                        //o <ignore> tags for <file name=... platform=!this platform>
                        if ($platform == "!$os") {
                            $release[$releaseNum]['filelist']['ignore'][] =
                                array(
                                    'attribs' => array(
                                        'name' => $file,
                                    ),
                                );
                            continue;
                        }
                        //o <ignore> tags for <file name=... platform=other platform>
                        if ($platform{0} != '!' && $platform != $os) {
                            $release[$releaseNum]['filelist']['ignore'][] =
                                array(
                                    'attribs' => array(
                                        'name' => $file,
                                    ),
                                );
                        }
                    }
                    if (!count($release[$releaseNum]['filelist']['install'])) {
                        unset($release[$releaseNum]['filelist']['install']);
                    }
                    if (!count($release[$releaseNum]['filelist']['ignore'])) {
                        unset($release[$releaseNum]['filelist']['ignore']);
                    }
                }
                if (count($generic) || count($genericIgnore)) {
                    $release[count($oses)] = array();
                    if (count($generic)) {
                        foreach ($generic as $file) {
                            if (isset($package['install-as'][$file])) {
                                $installas = $package['install-as'][$file];
                            } else {
                                $installas = $file;
                            }
                            $release[count($oses)]['filelist']['install'][] =
                                array(
                                    'attribs' => array(
                                        'name' => $file,
                                        'as' => $installas,
                                    )
                                );
                        }
                    }
                    if (count($genericIgnore)) {
                        foreach ($genericIgnore as $file) {
                            $release[count($oses)]['filelist']['ignore'][] =
                                array(
                                    'attribs' => array(
                                        'name' => $file,
                                    )
                                );
                        }
                    }
                }
                // cleanup
                foreach ($release as $i => $rel) {
                    if (isset($rel['filelist']['install']) &&
                          count($rel['filelist']['install']) == 1) {
                        $release[$i]['filelist']['install'] =
                            $release[$i]['filelist']['install'][0];
                    }
                    if (isset($rel['filelist']['ignore']) &&
                          count($rel['filelist']['ignore']) == 1) {
                        $release[$i]['filelist']['ignore'] =
                            $release[$i]['filelist']['ignore'][0];
                    }
                }
                if (count($release) == 1) {
                    $release = $release[0];
                }
            } else {
                // no platform atts, but some install-as atts
                foreach ($package['install-as'] as $file => $value) {
                    $release['filelist']['install'][] =
                        array(
                            'attribs' => array(
                                'name' => $file,
                                'as' => $value
                            )
                        );
                }
                if (count($release['filelist']['install']) == 1) {
                    $release['filelist']['install'] = $release['filelist']['install'][0];
                }
            }
        }
    }

    /**
     * @param array
     * @return array
     * @access private
     */
    function _processDep($dep)
    {
        if ($dep['type'] == 'php') {
            if ($dep['rel'] == 'has') {
                // come on - everyone has php!
                return false;
            }
        }
        $php = array();
        if ($dep['type'] != 'php') {
            $php['name'] = $dep['name'];
            if ($dep['type'] == 'pkg') {
                $php['channel'] = 'pear.php.net';
            }
        }
        switch ($dep['rel']) {
            case 'gt' :
                $php['min'] = $dep['version'];
                $php['exclude'] = $dep['version'];
            break;
            case 'ge' :
                if (!isset($dep['version'])) {
                    if ($dep['type'] == 'php') {
                        if (isset($dep['name'])) {
                            $dep['version'] = $dep['name'];
                        }
                    }
                }
                $php['min'] = $dep['version'];
            break;
            case 'lt' :
                $php['max'] = $dep['version'];
                $php['exclude'] = $dep['version'];
            break;
            case 'le' :
                $php['max'] = $dep['version'];
            break;
            case 'eq' :
                $php['min'] = $dep['version'];
                $php['max'] = $dep['version'];
            break;
            case 'ne' :
                $php['exclude'] = $dep['version'];
            break;
            case 'not' :
                $php['conflicts'] = 'yes';
            break;
        }
        return $php;
    }

    /**
     * @param array
     * @return array
     */
    function _processPhpDeps($deps)
    {
        $test = array();
        foreach ($deps as $dep) {
            $test[] = $this->_processDep($dep);
        }
        $min = array();
        $max = array();
        foreach ($test as $dep) {
            if (!$dep) {
                continue;
            }
            if (isset($dep['min'])) {
                $min[$dep['min']] = count($min);
            }
            if (isset($dep['max'])) {
                $max[$dep['max']] = count($max);
            }
        }
        if (count($min) > 0) {
            uksort($min, 'version_compare');
        }
        if (count($max) > 0) {
            uksort($max, 'version_compare');
        }
        if (count($min)) {
            // get the highest minimum
            $min = array_pop($a = array_flip($min));
        } else {
            $min = false;
        }
        if (count($max)) {
            // get the lowest maximum
            $max = array_shift($a = array_flip($max));
        } else {
            $max = false;
        }
        if ($min) {
            $php['min'] = $min;
        }
        if ($max) {
            $php['max'] = $max;
        }
        $exclude = array();
        foreach ($test as $dep) {
            if (!isset($dep['exclude'])) {
                continue;
            }
            $exclude[] = $dep['exclude'];
        }
        if (count($exclude)) {
            $php['exclude'] = $exclude;
        }
        return $php;
    }

    /**
     * process multiple dependencies that have a name, like package deps
     * @param array
     * @return array
     * @access private
     */
    function _processMultipleDepsName($deps)
    {
        $tests = array();
        foreach ($deps as $name => $dep) {
            foreach ($dep as $d) {
                $tests[$name][] = $this->_processDep($d);
            }
        }
        foreach ($tests as $name => $test) {
            $php = array();
            $min = array();
            $max = array();
            $php['name'] = $name;
            foreach ($test as $dep) {
                if (!$dep) {
                    continue;
                }
                if (isset($dep['channel'])) {
                    $php['channel'] = 'pear.php.net';
                }
                if (isset($dep['conflicts']) && $dep['conflicts'] == 'yes') {
                    $php['conflicts'] = 'yes';
                }
                if (isset($dep['min'])) {
                    $min[$dep['min']] = count($min);
                }
                if (isset($dep['max'])) {
                    $max[$dep['max']] = count($max);
                }
            }
            if (count($min) > 0) {
                uksort($min, 'version_compare');
            }
            if (count($max) > 0) {
                uksort($max, 'version_compare');
            }
            if (count($min)) {
                // get the highest minimum
                $min = array_pop($a = array_flip($min));
            } else {
                $min = false;
            }
            if (count($max)) {
                // get the lowest maximum
                $max = array_shift($a = array_flip($max));
            } else {
                $max = false;
            }
            if ($min) {
                $php['min'] = $min;
            }
            if ($max) {
                $php['max'] = $max;
            }
            $exclude = array();
            foreach ($test as $dep) {
                if (!isset($dep['exclude'])) {
                    continue;
                }
                $exclude[] = $dep['exclude'];
            }
            if (count($exclude)) {
                $php['exclude'] = $exclude;
            }
            $ret[] = $php;
        }
        return $ret;
    }
}
?><?php
/**
 * package.xml generation class, package.xml version 2.0
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @author     Stephan Schmidt (original XML_Serializer code)
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: v2.php,v 1.39 2008/05/13 05:29:24 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * file/dir manipulation routines
 */
require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
/**
 * This class converts a PEAR_PackageFile_v2 object into any output format.
 *
 * Supported output formats include array, XML string (using S. Schmidt's
 * XML_Serializer, slightly customized)
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @author     Stephan Schmidt (original XML_Serializer code)
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_PackageFile_Generator_v2
{
   /**
    * default options for the serialization
    * @access private
    * @var array $_defaultOptions
    */
    var $_defaultOptions = array(
                         'indent'             => ' ',                    // string used for indentation
                         'linebreak'          => "\n",                  // string used for newlines
                         'typeHints'          => false,                 // automatically add type hin attributes
                         'addDecl'            => true,                 // add an XML declaration
                         'defaultTagName'     => 'XML_Serializer_Tag',  // tag used for indexed arrays or invalid names
                         'classAsTagName'     => false,                 // use classname for objects in indexed arrays
                         'keyAttribute'       => '_originalKey',        // attribute where original key is stored
                         'typeAttribute'      => '_type',               // attribute for type (only if typeHints => true)
                         'classAttribute'     => '_class',              // attribute for class of objects (only if typeHints => true)
                         'scalarAsAttributes' => false,                 // scalar values (strings, ints,..) will be serialized as attribute
                         'prependAttributes'  => '',                    // prepend string for attributes
                         'indentAttributes'   => false,                 // indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column
                         'mode'               => 'simplexml',             // use 'simplexml' to use parent name as tagname if transforming an indexed array
                         'addDoctype'         => false,                 // add a doctype declaration
                         'doctype'            => null,                  // supply a string or an array with id and uri ({@see PEAR_PackageFile_Generator_v2_PEAR_PackageFile_Generator_v2_XML_Util::getDoctypeDeclaration()}
                         'rootName'           => 'package',                  // name of the root tag
                         'rootAttributes'     => array(
                             'version' => '2.0',
                             'xmlns' => 'http://pear.php.net/dtd/package-2.0',
                             'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
                             'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
                             'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0
http://pear.php.net/dtd/tasks-1.0.xsd
http://pear.php.net/dtd/package-2.0
http://pear.php.net/dtd/package-2.0.xsd',
                         ),               // attributes of the root tag
                         'attributesArray'    => 'attribs',                  // all values in this key will be treated as attributes
                         'contentName'        => '_content',                   // this value will be used directly as content, instead of creating a new tag, may only be used in conjuction with attributesArray
                         'beautifyFilelist'   => false,
                         'encoding' => 'UTF-8',
                        );

   /**
    * options for the serialization
    * @access private
    * @var array $options
    */
    var $options = array();

   /**
    * current tag depth
    * @var integer $_tagDepth
    */
    var $_tagDepth = 0;

   /**
    * serilialized representation of the data
    * @var string $_serializedData
    */
    var $_serializedData = null;
    /**
     * @var PEAR_PackageFile_v2
     */
    var $_packagefile;
    /**
     * @param PEAR_PackageFile_v2
     */
    function PEAR_PackageFile_Generator_v2(&$packagefile)
    {
        $this->_packagefile = &$packagefile;
    }

    /**
     * @return string
     */
    function getPackagerVersion()
    {
        return '1.7.2';
    }

    /**
     * @param PEAR_Packager
     * @param bool generate a .tgz or a .tar
     * @param string|null temporary directory to package in
     */
    function toTgz(&$packager, $compress = true, $where = null)
    {
        $a = null;
        return $this->toTgz2($packager, $a, $compress, $where);
    }

    /**
     * Package up both a package.xml and package2.xml for the same release
     * @param PEAR_Packager
     * @param PEAR_PackageFile_v1
     * @param bool generate a .tgz or a .tar
     * @param string|null temporary directory to package in
     */
    function toTgz2(&$packager, &$pf1, $compress = true, $where = null)
    {
        require_once 'phar://install-pear-nozlib.phar/' . 'Archive/Tar.php';
        if (!$this->_packagefile->isEquivalent($pf1)) {
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' .
                basename($pf1->getPackageFile()) .
                '" is not equivalent to "' . basename($this->_packagefile->getPackageFile())
                . '"');
        }
        if ($where === null) {
            if (!($where = System::mktemp(array('-d')))) {
                return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: mktemp failed');
            }
        } elseif (!@System::mkDir(array('-p', $where))) {
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' . $where . '" could' .
                ' not be created');
        }
        if (file_exists($where . DIRECTORY_SEPARATOR . 'package.xml') &&
              !is_file($where . DIRECTORY_SEPARATOR . 'package.xml')) {
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: unable to save package.xml as' .
                ' "' . $where . DIRECTORY_SEPARATOR . 'package.xml"');
        }
        if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) {
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: invalid package.xml');
        }
        $ext = $compress ? '.tgz' : '.tar';
        $pkgver = $this->_packagefile->getPackage() . '-' . $this->_packagefile->getVersion();
        $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext;
        if (file_exists(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext) &&
              !is_file(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext)) {
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: cannot create tgz file "' .
                getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext . '"');
        }
        if ($pkgfile = $this->_packagefile->getPackageFile()) {
            $pkgdir = dirname(realpath($pkgfile));
            $pkgfile = basename($pkgfile);
        } else {
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: package file object must ' .
                'be created from a real file');
        }
        // {{{ Create the package file list
        $filelist = array();
        $i = 0;
        $this->_packagefile->flattenFilelist();
        $contents = $this->_packagefile->getContents();
        if (isset($contents['bundledpackage'])) { // bundles of packages
            $contents = $contents['bundledpackage'];
            if (!isset($contents[0])) {
                $contents = array($contents);
            }
            $packageDir = $where;
            foreach ($contents as $i => $package) {
                $fname = $package;
                $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
                if (!file_exists($file)) {
                    return $packager->raiseError("File does not exist: $fname");
                }
                $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname;
                System::mkdir(array('-p', dirname($tfile)));
                copy($file, $tfile);
                $filelist[$i++] = $tfile;
                $packager->log(2, "Adding package $fname");
            }
        } else { // normal packages
            $contents = $contents['dir']['file'];
            if (!isset($contents[0])) {
                $contents = array($contents);
            }
    
            $packageDir = $where;
            foreach ($contents as $i => $file) {
                $fname = $file['attribs']['name'];
                $atts = $file['attribs'];
                $orig = $file;
                $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
                if (!file_exists($file)) {
                    return $packager->raiseError("File does not exist: $fname");
                } else {
                    $origperms = fileperms($file);
                    $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname;
                    unset($orig['attribs']);
                    if (count($orig)) { // file with tasks
                        // run any package-time tasks
                        $contents = file_get_contents($file);
                        foreach ($orig as $tag => $raw) {
                            $tag = str_replace(
                                array($this->_packagefile->getTasksNs() . ':', '-'),
                                array('', '_'), $tag);
                            $task = "PEAR_Task_$tag";
                            $task = &new $task($this->_packagefile->_config,
                                $this->_packagefile->_logger,
                                PEAR_TASK_PACKAGE);
                            $task->init($raw, $atts, null);
                            $res = $task->startSession($this->_packagefile, $contents, $tfile);
                            if (!$res) {
                                continue; // skip this task
                            }
                            if (PEAR::isError($res)) {
                                return $res;
                            }
                            $contents = $res; // save changes
                            System::mkdir(array('-p', dirname($tfile)));
                            $wp = fopen($tfile, "wb");
                            fwrite($wp, $contents);
                            fclose($wp);
                        }
                    }
                    if (!file_exists($tfile)) {
                        System::mkdir(array('-p', dirname($tfile)));
                        copy($file, $tfile);
                    }
                    chmod($tfile, $origperms);
                    $filelist[$i++] = $tfile;
                    $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($tfile), $i - 1);
                    $packager->log(2, "Adding file $fname");
                }
            }
        }
            // }}}
        if ($pf1 !== null) {
            $name = 'package2.xml';
        } else {
            $name = 'package.xml';
        }
        $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, $name);
        if ($packagexml) {
            $tar =& new Archive_Tar($dest_package, $compress);
            $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors
            // ----- Creates with the package.xml file
            $ok = $tar->createModify(array($packagexml), '', $where);
            if (PEAR::isError($ok)) {
                return $packager->raiseError($ok);
            } elseif (!$ok) {
                return $packager->raiseError('PEAR_Packagefile_v2::toTgz(): adding ' . $name .
                    ' failed');
            }
            // ----- Add the content of the package
            if (!$tar->addModify($filelist, $pkgver, $where)) {
                return $packager->raiseError(
                    'PEAR_Packagefile_v2::toTgz(): tarball creation failed');
            }
            // add the package.xml version 1.0
            if ($pf1 !== null) {
                $pfgen = &$pf1->getDefaultGenerator();
                $packagexml1 = $pfgen->toPackageFile($where, PEAR_VALIDATE_PACKAGING,
                    'package.xml', true);
                if (!$tar->addModify(array($packagexml1), '', $where)) {
                    return $packager->raiseError(
                        'PEAR_Packagefile_v2::toTgz(): adding package.xml failed');
                }
            }
            return $dest_package;
        }
    }

    function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'package.xml')
    {
        if (!$this->_packagefile->validate($state)) {
            return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: invalid package.xml',
                null, null, null, $this->_packagefile->getValidationWarnings());
        }
        if ($where === null) {
            if (!($where = System::mktemp(array('-d')))) {
                return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: mktemp failed');
            }
        } elseif (!@System::mkDir(array('-p', $where))) {
            return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: "' . $where . '" could' .
                ' not be created');
        }
        $newpkgfile = $where . DIRECTORY_SEPARATOR . $name;
        $np = @fopen($newpkgfile, 'wb');
        if (!$np) {
            return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: unable to save ' .
               "$name as $newpkgfile");
        }
        fwrite($np, $this->toXml($state));
        fclose($np);
        return $newpkgfile;
    }

    function &toV2()
    {
        return $this->_packagefile;
    }

    /**
     * Return an XML document based on the package info (as returned
     * by the PEAR_Common::infoFrom* methods).
     *
     * @return string XML data
     */
    function toXml($state = PEAR_VALIDATE_NORMAL, $options = array())
    {
        $this->_packagefile->setDate(date('Y-m-d'));
        $this->_packagefile->setTime(date('H:i:s'));
        if (!$this->_packagefile->validate($state)) {
            return false;
        }
        if (is_array($options)) {
            $this->options = array_merge($this->_defaultOptions, $options);
        } else {
            $this->options = $this->_defaultOptions;
        }
        $arr = $this->_packagefile->getArray();
        if (isset($arr['filelist'])) {
            unset($arr['filelist']);
        }
        if (isset($arr['_lastversion'])) {
            unset($arr['_lastversion']);
        }
        if ($state ^ PEAR_VALIDATE_PACKAGING && !isset($arr['bundle'])) {
            $use = $this->_recursiveXmlFilelist($arr['contents']['dir']['file']);
            unset($arr['contents']['dir']['file']);
            if (isset($use['dir'])) {
                $arr['contents']['dir']['dir'] = $use['dir'];
            }
            if (isset($use['file'])) {
                $arr['contents']['dir']['file'] = $use['file'];
            }
            $this->options['beautifyFilelist'] = true;
        }
        $arr['attribs']['packagerversion'] = '1.7.2';
        if ($this->serialize($arr, $options)) {
            return $this->_serializedData . "\n";
        }
        return false;
    }


    function _recursiveXmlFilelist($list)
    {
        $dirs = array();
        if (isset($list['attribs'])) {
            $file = $list['attribs']['name'];
            unset($list['attribs']['name']);
            $attributes = $list['attribs'];
            $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes);
        } else {
            foreach ($list as $a) {
                $file = $a['attribs']['name'];
                $attributes = $a['attribs'];
                unset($a['attribs']);
                $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes, $a);
            }
        }
        $this->_formatDir($dirs);
        $this->_deFormat($dirs);
        return $dirs;
    }

    function _addDir(&$dirs, $dir, $file = null, $attributes = null, $tasks = null)
    {
        if (!$tasks) {
            $tasks = array();
        }
        if ($dir == array() || $dir == array('.')) {
            $dirs['file'][basename($file)] = $tasks;
            $attributes['name'] = basename($file);
            $dirs['file'][basename($file)]['attribs'] = $attributes;
            return;
        }
        $curdir = array_shift($dir);
        if (!isset($dirs['dir'][$curdir])) {
            $dirs['dir'][$curdir] = array();
        }
        $this->_addDir($dirs['dir'][$curdir], $dir, $file, $attributes, $tasks);
    }

    function _formatDir(&$dirs)
    {
        if (!count($dirs)) {
            return array();
        }
        $newdirs = array();
        if (isset($dirs['dir'])) {
            $newdirs['dir'] = $dirs['dir'];
        }
        if (isset($dirs['file'])) {
            $newdirs['file'] = $dirs['file'];
        }
        $dirs = $newdirs;
        if (isset($dirs['dir'])) {
            uksort($dirs['dir'], 'strnatcasecmp');
            foreach ($dirs['dir'] as $dir => $contents) {
                $this->_formatDir($dirs['dir'][$dir]);
            }
        }
        if (isset($dirs['file'])) {
            uksort($dirs['file'], 'strnatcasecmp');
        };
    }

    function _deFormat(&$dirs)
    {
        if (!count($dirs)) {
            return array();
        }
        $newdirs = array();
        if (isset($dirs['dir'])) {
            foreach ($dirs['dir'] as $dir => $contents) {
                $newdir = array();
                $newdir['attribs']['name'] = $dir;
                $this->_deFormat($contents);
                foreach ($contents as $tag => $val) {
                    $newdir[$tag] = $val;
                }
                $newdirs['dir'][] = $newdir;
            }
            if (count($newdirs['dir']) == 1) {
                $newdirs['dir'] = $newdirs['dir'][0];
            }
        }
        if (isset($dirs['file'])) {
            foreach ($dirs['file'] as $name => $file) {
                $newdirs['file'][] = $file;
            }
            if (count($newdirs['file']) == 1) {
                $newdirs['file'] = $newdirs['file'][0];
            }
        }
        $dirs = $newdirs;
    }

    /**
    * reset all options to default options
    *
    * @access   public
    * @see      setOption(), XML_Unserializer()
    */
    function resetOptions()
    {
        $this->options = $this->_defaultOptions;
    }

   /**
    * set an option
    *
    * You can use this method if you do not want to set all options in the constructor
    *
    * @access   public
    * @see      resetOption(), XML_Serializer()
    */
    function setOption($name, $value)
    {
        $this->options[$name] = $value;
    }
    
   /**
    * sets several options at once
    *
    * You can use this method if you do not want to set all options in the constructor
    *
    * @access   public
    * @see      resetOption(), XML_Unserializer(), setOption()
    */
    function setOptions($options)
    {
        $this->options = array_merge($this->options, $options);
    }

   /**
    * serialize data
    *
    * @access   public
    * @param    mixed    $data data to serialize
    * @return   boolean  true on success, pear error on failure
    */
    function serialize($data, $options = null)
    {
        // if options have been specified, use them instead
        // of the previously defined ones
        if (is_array($options)) {
            $optionsBak = $this->options;
            if (isset($options['overrideOptions']) && $options['overrideOptions'] == true) {
                $this->options = array_merge($this->_defaultOptions, $options);
            } else {
                $this->options = array_merge($this->options, $options);
            }
        }
        else {
            $optionsBak = null;
        }
        
        //  start depth is zero
        $this->_tagDepth = 0;

        $this->_serializedData = '';
        // serialize an array
        if (is_array($data)) {
            if (isset($this->options['rootName'])) {
                $tagName = $this->options['rootName'];
            } else {
                $tagName = 'array';
            }

            $this->_serializedData .= $this->_serializeArray($data, $tagName, $this->options['rootAttributes']);
        }
        
        // add doctype declaration
        if ($this->options['addDoctype'] === true) {
            $this->_serializedData = PEAR_PackageFile_Generator_v2_XML_Util::getDoctypeDeclaration($tagName, $this->options['doctype'])
                                   . $this->options['linebreak']
                                   . $this->_serializedData;
        }

        //  build xml declaration
        if ($this->options['addDecl']) {
            $atts = array();
            if (isset($this->options['encoding']) ) {
                $encoding = $this->options['encoding'];
            } else {
                $encoding = null;
            }
            $this->_serializedData = PEAR_PackageFile_Generator_v2_XML_Util::getXMLDeclaration('1.0', $encoding)
                                   . $this->options['linebreak']
                                   . $this->_serializedData;
        }
        
        
		if ($optionsBak !== null) {
			$this->options = $optionsBak;
		}
		
        return  true;
    }

   /**
    * get the result of the serialization
    *
    * @access public
    * @return string serialized XML
    */
    function getSerializedData()
    {
        if ($this->_serializedData == null ) {
            return  $this->raiseError('No serialized data available. Use XML_Serializer::serialize() first.', XML_SERIALIZER_ERROR_NO_SERIALIZATION);
        }
        return $this->_serializedData;
    }
    
   /**
    * serialize any value
    *
    * This method checks for the type of the value and calls the appropriate method
    *
    * @access private
    * @param  mixed     $value
    * @param  string    $tagName
    * @param  array     $attributes
    * @return string
    */
    function _serializeValue($value, $tagName = null, $attributes = array())
    {
        if (is_array($value)) {
            $xml = $this->_serializeArray($value, $tagName, $attributes);
        } elseif (is_object($value)) {
            $xml = $this->_serializeObject($value, $tagName);
        } else {
            $tag = array(
                          'qname'      => $tagName,
                          'attributes' => $attributes,
                          'content'    => $value
                        );
            $xml = $this->_createXMLTag($tag);
        }
        return $xml;
    }
    
   /**
    * serialize an array
    *
    * @access   private
    * @param    array   $array       array to serialize
    * @param    string  $tagName     name of the root tag
    * @param    array   $attributes  attributes for the root tag
    * @return   string  $string      serialized data
    * @uses     PEAR_PackageFile_Generator_v2_XML_Util::isValidName() to check, whether key has to be substituted
    */
    function _serializeArray(&$array, $tagName = null, $attributes = array())
    {
        $_content = null;
        
        /**
         * check for special attributes
         */
        if ($this->options['attributesArray'] !== null) {
            if (isset($array[$this->options['attributesArray']])) {
                $attributes = $array[$this->options['attributesArray']];
                unset($array[$this->options['attributesArray']]);
            }
            /**
             * check for special content
             */
            if ($this->options['contentName'] !== null) {
                if (isset($array[$this->options['contentName']])) {
                    $_content = $array[$this->options['contentName']];
                    unset($array[$this->options['contentName']]);
                }
            }
        }

        /*
        * if mode is set to simpleXML, check whether
        * the array is associative or indexed
        */
        if (is_array($array) && $this->options['mode'] == 'simplexml') {
            $indexed = true;
            if (!count($array)) {
                $indexed = false;
            }
            foreach ($array as $key => $val) {
                if (!is_int($key)) {
                    $indexed = false;
                    break;
                }
            }

            if ($indexed && $this->options['mode'] == 'simplexml') {
                $string = '';
                foreach ($array as $key => $val) {
                    if ($this->options['beautifyFilelist'] && $tagName == 'dir') {
                        if (!isset($this->_curdir)) {
                            $this->_curdir = '';
                        }
                        $savedir = $this->_curdir;
                        if (isset($val['attribs'])) {
                            if ($val['attribs']['name'] == '/') {
                                $this->_curdir = '/';
                            } else {
                                if ($this->_curdir == '/') {
                                    $this->_curdir = '';
                                }
                                $this->_curdir .= '/' . $val['attribs']['name'];
                            }
                        }
                    }
                    $string .= $this->_serializeValue( $val, $tagName, $attributes);
                    if ($this->options['beautifyFilelist'] && $tagName == 'dir') {
                        $string .= ' <!-- ' . $this->_curdir . ' -->';
                        if (empty($savedir)) {
                            unset($this->_curdir);
                        } else {
                            $this->_curdir = $savedir;
                        }
                    }
                    
                    $string .= $this->options['linebreak'];
        			//	do indentation
                    if ($this->options['indent']!==null && $this->_tagDepth>0) {
                        $string .= str_repeat($this->options['indent'], $this->_tagDepth);
                    }
                }
                return rtrim($string);
            }
        }
        
		if ($this->options['scalarAsAttributes'] === true) {
	        foreach ($array as $key => $value) {
				if (is_scalar($value) && (PEAR_PackageFile_Generator_v2_XML_Util::isValidName($key) === true)) {
					unset($array[$key]);
					$attributes[$this->options['prependAttributes'].$key] = $value;
				}
			}
		}

        // check for empty array => create empty tag
        if (empty($array)) {
            $tag = array(
                            'qname'      => $tagName,
                            'content'    => $_content,
                            'attributes' => $attributes
                        );

        } else {
            $this->_tagDepth++;
            $tmp = $this->options['linebreak'];
            foreach ($array as $key => $value) {
    			//	do indentation
                if ($this->options['indent']!==null && $this->_tagDepth>0) {
                    $tmp .= str_repeat($this->options['indent'], $this->_tagDepth);
                }
    
    			//	copy key
    			$origKey	=	$key;
    			//	key cannot be used as tagname => use default tag
                $valid = PEAR_PackageFile_Generator_v2_XML_Util::isValidName($key);
    	        if (PEAR::isError($valid)) {
    	            if ($this->options['classAsTagName'] && is_object($value)) {
    	                $key = get_class($value);
    	            } else {
            	        $key = $this->options['defaultTagName'];
    	            }
           	 	}
                $atts = array();
                if ($this->options['typeHints'] === true) {
                    $atts[$this->options['typeAttribute']] = gettype($value);
    				if ($key !== $origKey) {
    					$atts[$this->options['keyAttribute']] = (string)$origKey;
    				}
    
                }
                if ($this->options['beautifyFilelist'] && $key == 'dir') {
                    if (!isset($this->_curdir)) {
                        $this->_curdir = '';
                    }
                    $savedir = $this->_curdir;
                    if (isset($value['attribs'])) {
                        if ($value['attribs']['name'] == '/') {
                            $this->_curdir = '/';
                        } else {
                            $this->_curdir .= '/' . $value['attribs']['name'];
                        }
                    }
                }

                if (is_string($value) && $value && ($value{strlen($value) - 1} == "\n")) {
                    $value .= str_repeat($this->options['indent'], $this->_tagDepth);
                }
                $tmp .= $this->_createXMLTag(array(
                                                    'qname'      => $key,
                                                    'attributes' => $atts,
                                                    'content'    => $value )
                                            );
                if ($this->options['beautifyFilelist'] && $key == 'dir') {
                    if (isset($value['attribs'])) {
                        $tmp .= ' <!-- ' . $this->_curdir . ' -->';
                        if (empty($savedir)) {
                            unset($this->_curdir);
                        } else {
                            $this->_curdir = $savedir;
                        }
                    }
                }
                $tmp .= $this->options['linebreak'];
            }
            
            $this->_tagDepth--;
            if ($this->options['indent']!==null && $this->_tagDepth>0) {
                $tmp .= str_repeat($this->options['indent'], $this->_tagDepth);
            }
    
    		if (trim($tmp) === '') {
    			$tmp = null;
    		}
    		
            $tag = array(
                            'qname'      => $tagName,
                            'content'    => $tmp,
                            'attributes' => $attributes
                        );
        }
        if ($this->options['typeHints'] === true) {
            if (!isset($tag['attributes'][$this->options['typeAttribute']])) {
                $tag['attributes'][$this->options['typeAttribute']] = 'array';
            }
        }

        $string = $this->_createXMLTag($tag, false);
        return $string;
    }
  
   /**
    * create a tag from an array
    * this method awaits an array in the following format
    * array(
    *       'qname'        => $tagName,
    *       'attributes'   => array(),
    *       'content'      => $content,      // optional
    *       'namespace'    => $namespace     // optional
    *       'namespaceUri' => $namespaceUri  // optional
    *   )
    *
    * @access   private
    * @param    array   $tag tag definition
    * @param    boolean $replaceEntities whether to replace XML entities in content or not
    * @return   string  $string XML tag
    */
    function _createXMLTag( $tag, $replaceEntities = true )
    {
        if ($this->options['indentAttributes'] !== false) {
            $multiline = true;
            $indent    = str_repeat($this->options['indent'], $this->_tagDepth);

            if ($this->options['indentAttributes'] == '_auto') {
                $indent .= str_repeat(' ', (strlen($tag['qname'])+2));

            } else {
                $indent .= $this->options['indentAttributes'];
            }
        } else {
            $multiline = false;
            $indent    = false;
        }
    
        if (is_array($tag['content'])) {
            if (empty($tag['content'])) {
                $tag['content'] =   '';
            }
        } elseif(is_scalar($tag['content']) && (string)$tag['content'] == '') {
            $tag['content'] =   '';
        }
    
        if (is_scalar($tag['content']) || is_null($tag['content'])) {
            if ($this->options['encoding'] == 'UTF-8' &&
                  version_compare(phpversion(), '5.0.0', 'lt')) {
                $encoding = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_UTF8_XML;
            } else {
                $encoding = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML;
            }
            $tag = PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $this->options['linebreak'], $encoding);
        } elseif (is_array($tag['content'])) {
            $tag    =   $this->_serializeArray($tag['content'], $tag['qname'], $tag['attributes']);
        } elseif (is_object($tag['content'])) {
            $tag    =   $this->_serializeObject($tag['content'], $tag['qname'], $tag['attributes']);
        } elseif (is_resource($tag['content'])) {
            settype($tag['content'], 'string');
            $tag    =   PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray($tag, $replaceEntities);
        }
        return  $tag;
    }
}

// well, it's one way to do things without extra deps ...
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// | PHP Version 4                                                        |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2002 The PHP Group                                |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license,       |
// | that is bundled with this package in the file LICENSE, and is        |
// | available at through the world-wide-web at                           |
// | http://www.php.net/license/2_02.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: Stephan Schmidt <schst@php-tools.net>                       |
// +----------------------------------------------------------------------+
//
//    $Id: v2.php,v 1.39 2008/05/13 05:29:24 cellog Exp $

/**
 * error code for invalid chars in XML name
 */
define("PEAR_PackageFile_Generator_v2_XML_Util_ERROR_INVALID_CHARS", 51);

/**
 * error code for invalid chars in XML name
 */
define("PEAR_PackageFile_Generator_v2_XML_Util_ERROR_INVALID_START", 52);

/**
 * error code for non-scalar tag content
 */
define("PEAR_PackageFile_Generator_v2_XML_Util_ERROR_NON_SCALAR_CONTENT", 60);
    
/**
 * error code for missing tag name
 */
define("PEAR_PackageFile_Generator_v2_XML_Util_ERROR_NO_TAG_NAME", 61);
    
/**
 * replace XML entities
 */
define("PEAR_PackageFile_Generator_v2_XML_Util_REPLACE_ENTITIES", 1);

/**
 * embedd content in a CData Section
 */
define("PEAR_PackageFile_Generator_v2_XML_Util_CDATA_SECTION", 2);

/**
 * do not replace entitites
 */
define("PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_NONE", 0);

/**
 * replace all XML entitites
 * This setting will replace <, >, ", ' and &
 */
define("PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML", 1);

/**
 * replace only required XML entitites
 * This setting will replace <, " and &
 */
define("PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML_REQUIRED", 2);

/**
 * replace HTML entitites
 * @link    http://www.php.net/htmlentities
 */
define("PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_HTML", 3);

/**
 * replace all XML entitites, and encode from ISO-8859-1 to UTF-8
 * This setting will replace <, >, ", ' and &
 */
define("PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_UTF8_XML", 4);

/**
 * utility class for working with XML documents
 * 
 * customized version of XML_Util 0.6.0
 *
 * @category XML
 * @package  PEAR
 * @version  0.6.0
 * @author   Stephan Schmidt <schst@php.net>
 * @author   Gregory Beaver <cellog@php.net>
 */
class PEAR_PackageFile_Generator_v2_XML_Util {

   /**
    * return API version
    *
    * @access   public
    * @static
    * @return   string  $version API version
    */
    function apiVersion()
    {
        return "0.6";
    }

   /**
    * replace XML entities
    *
    * With the optional second parameter, you may select, which
    * entities should be replaced.
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * // replace XML entites:
    * $string = PEAR_PackageFile_Generator_v2_XML_Util::replaceEntities("This string contains < & >.");
    * </code>
    *
    * @access   public
    * @static
    * @param    string  string where XML special chars should be replaced
    * @param    integer setting for entities in attribute values (one of PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML, PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML_REQUIRED, PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_HTML)
    * @return   string  string with replaced chars
    */
    function replaceEntities($string, $replaceEntities = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML)
    {
        switch ($replaceEntities) {
            case PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_UTF8_XML:
                return strtr(utf8_encode($string),array(
                                          '&'  => '&amp;',
                                          '>'  => '&gt;',
                                          '<'  => '&lt;',
                                          '"'  => '&quot;',
                                          '\'' => '&apos;' ));
                break;
            case PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML:
                return strtr($string,array(
                                          '&'  => '&amp;',
                                          '>'  => '&gt;',
                                          '<'  => '&lt;',
                                          '"'  => '&quot;',
                                          '\'' => '&apos;' ));
                break;
            case PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML_REQUIRED:
                return strtr($string,array(
                                          '&'  => '&amp;',
                                          '<'  => '&lt;',
                                          '"'  => '&quot;' ));
                break;
            case PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_HTML:
                return htmlspecialchars($string);
                break;
        }
        return $string;
    }

   /**
    * build an xml declaration
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * // get an XML declaration:
    * $xmlDecl = PEAR_PackageFile_Generator_v2_XML_Util::getXMLDeclaration("1.0", "UTF-8", true);
    * </code>
    *
    * @access   public
    * @static
    * @param    string  $version     xml version
    * @param    string  $encoding    character encoding
    * @param    boolean $standAlone  document is standalone (or not)
    * @return   string  $decl xml declaration
    * @uses     PEAR_PackageFile_Generator_v2_XML_Util::attributesToString() to serialize the attributes of the XML declaration
    */
    function getXMLDeclaration($version = "1.0", $encoding = null, $standalone = null)
    {
        $attributes = array(
                            "version" => $version,
                           );
        // add encoding
        if ($encoding !== null) {
            $attributes["encoding"] = $encoding;
        }
        // add standalone, if specified
        if ($standalone !== null) {
            $attributes["standalone"] = $standalone ? "yes" : "no";
        }
        
        return sprintf("<?xml%s?>", PEAR_PackageFile_Generator_v2_XML_Util::attributesToString($attributes, false));
    }

   /**
    * build a document type declaration
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * // get a doctype declaration:
    * $xmlDecl = PEAR_PackageFile_Generator_v2_XML_Util::getDocTypeDeclaration("rootTag","myDocType.dtd");
    * </code>
    *
    * @access   public
    * @static
    * @param    string  $root         name of the root tag
    * @param    string  $uri          uri of the doctype definition (or array with uri and public id)
    * @param    string  $internalDtd  internal dtd entries   
    * @return   string  $decl         doctype declaration
    * @since    0.2
    */
    function getDocTypeDeclaration($root, $uri = null, $internalDtd = null)
    {
        if (is_array($uri)) {
            $ref = sprintf( ' PUBLIC "%s" "%s"', $uri["id"], $uri["uri"] );
        } elseif (!empty($uri)) {
            $ref = sprintf( ' SYSTEM "%s"', $uri );
        } else {
            $ref = "";
        }

        if (empty($internalDtd)) {
            return sprintf("<!DOCTYPE %s%s>", $root, $ref);
        } else {
            return sprintf("<!DOCTYPE %s%s [\n%s\n]>", $root, $ref, $internalDtd);
        }
    }

   /**
    * create string representation of an attribute list
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * // build an attribute string
    * $att = array(
    *              "foo"   =>  "bar",
    *              "argh"  =>  "tomato"
    *            );
    *
    * $attList = PEAR_PackageFile_Generator_v2_XML_Util::attributesToString($att);    
    * </code>
    *
    * @access   public
    * @static
    * @param    array         $attributes        attribute array
    * @param    boolean|array $sort              sort attribute list alphabetically, may also be an assoc array containing the keys 'sort', 'multiline', 'indent', 'linebreak' and 'entities'
    * @param    boolean       $multiline         use linebreaks, if more than one attribute is given
    * @param    string        $indent            string used for indentation of multiline attributes
    * @param    string        $linebreak         string used for linebreaks of multiline attributes
    * @param    integer       $entities          setting for entities in attribute values (one of PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_NONE, PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML, PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML_REQUIRED, PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_HTML)
    * @return   string                           string representation of the attributes
    * @uses     PEAR_PackageFile_Generator_v2_XML_Util::replaceEntities() to replace XML entities in attribute values
    * @todo     allow sort also to be an options array
    */
    function attributesToString($attributes, $sort = true, $multiline = false, $indent = '    ', $linebreak = "\n", $entities = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML)
    {
        /**
         * second parameter may be an array
         */
        if (is_array($sort)) {
            if (isset($sort['multiline'])) {
                $multiline = $sort['multiline'];
            }
            if (isset($sort['indent'])) {
                $indent = $sort['indent'];
            }
            if (isset($sort['linebreak'])) {
                $multiline = $sort['linebreak'];
            }
            if (isset($sort['entities'])) {
                $entities = $sort['entities'];
            }
            if (isset($sort['sort'])) {
                $sort = $sort['sort'];
            } else {
                $sort = true;
            }
        }
        $string = '';
        if (is_array($attributes) && !empty($attributes)) {
            if ($sort) {
                ksort($attributes);
            }
            if( !$multiline || count($attributes) == 1) {
                foreach ($attributes as $key => $value) {
                    if ($entities != PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_NONE) {
                        $value = PEAR_PackageFile_Generator_v2_XML_Util::replaceEntities($value, $entities);
                    }
                    $string .= ' '.$key.'="'.$value.'"';
                }
            } else {
                $first = true;
                foreach ($attributes as $key => $value) {
                    if ($entities != PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_NONE) {
                        $value = PEAR_PackageFile_Generator_v2_XML_Util::replaceEntities($value, $entities);
                    }
                    if ($first) {
                        $string .= " ".$key.'="'.$value.'"';
                        $first = false;
                    } else {
                        $string .= $linebreak.$indent.$key.'="'.$value.'"';
                    }
                }
            }
        }
        return $string;
    }

   /**
    * create a tag
    *
    * This method will call PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray(), which
    * is more flexible.
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * // create an XML tag:
    * $tag = PEAR_PackageFile_Generator_v2_XML_Util::createTag("myNs:myTag", array("foo" => "bar"), "This is inside the tag", "http://www.w3c.org/myNs#");
    * </code>
    *
    * @access   public
    * @static
    * @param    string  $qname             qualified tagname (including namespace)
    * @param    array   $attributes        array containg attributes
    * @param    mixed   $content
    * @param    string  $namespaceUri      URI of the namespace
    * @param    integer $replaceEntities   whether to replace XML special chars in content, embedd it in a CData section or none of both
    * @param    boolean $multiline         whether to create a multiline tag where each attribute gets written to a single line
    * @param    string  $indent            string used to indent attributes (_auto indents attributes so they start at the same column)
    * @param    string  $linebreak         string used for linebreaks
    * @param    string  $encoding          encoding that should be used to translate content
    * @return   string  $string            XML tag
    * @see      PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray()
    * @uses     PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray() to create the tag
    */
    function createTag($qname, $attributes = array(), $content = null, $namespaceUri = null, $replaceEntities = PEAR_PackageFile_Generator_v2_XML_Util_REPLACE_ENTITIES, $multiline = false, $indent = "_auto", $linebreak = "\n", $encoding = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML)
    {
        $tag = array(
                     "qname"      => $qname,
                     "attributes" => $attributes
                    );

        // add tag content
        if ($content !== null) {
            $tag["content"] = $content;
        }
        
        // add namespace Uri
        if ($namespaceUri !== null) {
            $tag["namespaceUri"] = $namespaceUri;
        }

        return PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $linebreak, $encoding);
    }

   /**
    * create a tag from an array
    * this method awaits an array in the following format
    * <pre>
    * array(
    *  "qname"        => $qname         // qualified name of the tag
    *  "namespace"    => $namespace     // namespace prefix (optional, if qname is specified or no namespace)
    *  "localpart"    => $localpart,    // local part of the tagname (optional, if qname is specified)
    *  "attributes"   => array(),       // array containing all attributes (optional)
    *  "content"      => $content,      // tag content (optional)
    *  "namespaceUri" => $namespaceUri  // namespaceUri for the given namespace (optional)
    *   )
    * </pre>
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * $tag = array(
    *           "qname"        => "foo:bar",
    *           "namespaceUri" => "http://foo.com",
    *           "attributes"   => array( "key" => "value", "argh" => "fruit&vegetable" ),
    *           "content"      => "I'm inside the tag",
    *            );
    * // creating a tag with qualified name and namespaceUri
    * $string = PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray($tag);
    * </code>
    *
    * @access   public
    * @static
    * @param    array   $tag               tag definition
    * @param    integer $replaceEntities   whether to replace XML special chars in content, embedd it in a CData section or none of both
    * @param    boolean $multiline         whether to create a multiline tag where each attribute gets written to a single line
    * @param    string  $indent            string used to indent attributes (_auto indents attributes so they start at the same column)
    * @param    string  $linebreak         string used for linebreaks
    * @return   string  $string            XML tag
    * @see      PEAR_PackageFile_Generator_v2_XML_Util::createTag()
    * @uses     PEAR_PackageFile_Generator_v2_XML_Util::attributesToString() to serialize the attributes of the tag
    * @uses     PEAR_PackageFile_Generator_v2_XML_Util::splitQualifiedName() to get local part and namespace of a qualified name
    */
    function createTagFromArray($tag, $replaceEntities = PEAR_PackageFile_Generator_v2_XML_Util_REPLACE_ENTITIES, $multiline = false, $indent = "_auto", $linebreak = "\n", $encoding = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML)
    {
        if (isset($tag["content"]) && !is_scalar($tag["content"])) {
            return PEAR_PackageFile_Generator_v2_XML_Util::raiseError( "Supplied non-scalar value as tag content", PEAR_PackageFile_Generator_v2_XML_Util_ERROR_NON_SCALAR_CONTENT );
        }

        if (!isset($tag['qname']) && !isset($tag['localPart'])) {
            return PEAR_PackageFile_Generator_v2_XML_Util::raiseError( 'You must either supply a qualified name (qname) or local tag name (localPart).', PEAR_PackageFile_Generator_v2_XML_Util_ERROR_NO_TAG_NAME );
        }

        // if no attributes hav been set, use empty attributes
        if (!isset($tag["attributes"]) || !is_array($tag["attributes"])) {
            $tag["attributes"] = array();
        }
        
        // qualified name is not given
        if (!isset($tag["qname"])) {
            // check for namespace
            if (isset($tag["namespace"]) && !empty($tag["namespace"])) {
                $tag["qname"] = $tag["namespace"].":".$tag["localPart"];
            } else {
                $tag["qname"] = $tag["localPart"];
            }
        // namespace URI is set, but no namespace
        } elseif (isset($tag["namespaceUri"]) && !isset($tag["namespace"])) {
            $parts = PEAR_PackageFile_Generator_v2_XML_Util::splitQualifiedName($tag["qname"]);
            $tag["localPart"] = $parts["localPart"];
            if (isset($parts["namespace"])) {
                $tag["namespace"] = $parts["namespace"];
            }
        }

        if (isset($tag["namespaceUri"]) && !empty($tag["namespaceUri"])) {
            // is a namespace given
            if (isset($tag["namespace"]) && !empty($tag["namespace"])) {
                $tag["attributes"]["xmlns:".$tag["namespace"]] = $tag["namespaceUri"];
            } else {
                // define this Uri as the default namespace
                $tag["attributes"]["xmlns"] = $tag["namespaceUri"];
            }
        }

        // check for multiline attributes
        if ($multiline === true) {
            if ($indent === "_auto") {
                $indent = str_repeat(" ", (strlen($tag["qname"])+2));
            }
        }
        
        // create attribute list
        $attList    =   PEAR_PackageFile_Generator_v2_XML_Util::attributesToString($tag["attributes"], true, $multiline, $indent, $linebreak );
        if (!isset($tag["content"]) || (string)$tag["content"] == '') {
            $tag    =   sprintf("<%s%s />", $tag["qname"], $attList);
        } else {
            if ($replaceEntities == PEAR_PackageFile_Generator_v2_XML_Util_REPLACE_ENTITIES) {
                $tag["content"] = PEAR_PackageFile_Generator_v2_XML_Util::replaceEntities($tag["content"], $encoding);
            } elseif ($replaceEntities == PEAR_PackageFile_Generator_v2_XML_Util_CDATA_SECTION) {
                $tag["content"] = PEAR_PackageFile_Generator_v2_XML_Util::createCDataSection($tag["content"]);
            }
            $tag    =   sprintf("<%s%s>%s</%s>", $tag["qname"], $attList, $tag["content"], $tag["qname"] );
        }        
        return  $tag;
    }

   /**
    * create a start element
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * // create an XML start element:
    * $tag = PEAR_PackageFile_Generator_v2_XML_Util::createStartElement("myNs:myTag", array("foo" => "bar") ,"http://www.w3c.org/myNs#");
    * </code>
    *
    * @access   public
    * @static
    * @param    string  $qname             qualified tagname (including namespace)
    * @param    array   $attributes        array containg attributes
    * @param    string  $namespaceUri      URI of the namespace
    * @param    boolean $multiline         whether to create a multiline tag where each attribute gets written to a single line
    * @param    string  $indent            string used to indent attributes (_auto indents attributes so they start at the same column)
    * @param    string  $linebreak         string used for linebreaks
    * @return   string  $string            XML start element
    * @see      PEAR_PackageFile_Generator_v2_XML_Util::createEndElement(), PEAR_PackageFile_Generator_v2_XML_Util::createTag()
    */
    function createStartElement($qname, $attributes = array(), $namespaceUri = null, $multiline = false, $indent = '_auto', $linebreak = "\n")
    {
        // if no attributes hav been set, use empty attributes
        if (!isset($attributes) || !is_array($attributes)) {
            $attributes = array();
        }
        
        if ($namespaceUri != null) {
            $parts = PEAR_PackageFile_Generator_v2_XML_Util::splitQualifiedName($qname);
        }

        // check for multiline attributes
        if ($multiline === true) {
            if ($indent === "_auto") {
                $indent = str_repeat(" ", (strlen($qname)+2));
            }
        }

        if ($namespaceUri != null) {
            // is a namespace given
            if (isset($parts["namespace"]) && !empty($parts["namespace"])) {
                $attributes["xmlns:".$parts["namespace"]] = $namespaceUri;
            } else {
                // define this Uri as the default namespace
                $attributes["xmlns"] = $namespaceUri;
            }
        }

        // create attribute list
        $attList    =   PEAR_PackageFile_Generator_v2_XML_Util::attributesToString($attributes, true, $multiline, $indent, $linebreak);
        $element    =   sprintf("<%s%s>", $qname, $attList);
        return  $element;
    }

   /**
    * create an end element
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * // create an XML start element:
    * $tag = PEAR_PackageFile_Generator_v2_XML_Util::createEndElement("myNs:myTag");
    * </code>
    *
    * @access   public
    * @static
    * @param    string  $qname             qualified tagname (including namespace)
    * @return   string  $string            XML end element
    * @see      PEAR_PackageFile_Generator_v2_XML_Util::createStartElement(), PEAR_PackageFile_Generator_v2_XML_Util::createTag()
    */
    function createEndElement($qname)
    {
        $element    =   sprintf("</%s>", $qname);
        return  $element;
    }
    
   /**
    * create an XML comment
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * // create an XML start element:
    * $tag = PEAR_PackageFile_Generator_v2_XML_Util::createComment("I am a comment");
    * </code>
    *
    * @access   public
    * @static
    * @param    string  $content           content of the comment
    * @return   string  $comment           XML comment
    */
    function createComment($content)
    {
        $comment    =   sprintf("<!-- %s -->", $content);
        return  $comment;
    }
    
   /**
    * create a CData section
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * // create a CData section
    * $tag = PEAR_PackageFile_Generator_v2_XML_Util::createCDataSection("I am content.");
    * </code>
    *
    * @access   public
    * @static
    * @param    string  $data              data of the CData section
    * @return   string  $string            CData section with content
    */
    function createCDataSection($data)
    {
        return  sprintf("<![CDATA[%s]]>", $data);
    }

   /**
    * split qualified name and return namespace and local part
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * // split qualified tag
    * $parts = PEAR_PackageFile_Generator_v2_XML_Util::splitQualifiedName("xslt:stylesheet");
    * </code>
    * the returned array will contain two elements:
    * <pre>
    * array(
    *       "namespace" => "xslt",
    *       "localPart" => "stylesheet"
    *      );
    * </pre>
    *
    * @access public
    * @static
    * @param  string    $qname      qualified tag name
    * @param  string    $defaultNs  default namespace (optional)
    * @return array     $parts      array containing namespace and local part
    */
    function splitQualifiedName($qname, $defaultNs = null)
    {
        if (strstr($qname, ':')) {
            $tmp = explode(":", $qname);
            return array(
                          "namespace" => $tmp[0],
                          "localPart" => $tmp[1]
                        );
        }
        return array(
                      "namespace" => $defaultNs,
                      "localPart" => $qname
                    );
    }

   /**
    * check, whether string is valid XML name
    *
    * <p>XML names are used for tagname, attribute names and various
    * other, lesser known entities.</p>
    * <p>An XML name may only consist of alphanumeric characters,
    * dashes, undescores and periods, and has to start with a letter
    * or an underscore.
    * </p>
    *
    * <code>
    * require_once 'XML/Util.php';
    * 
    * // verify tag name
    * $result = PEAR_PackageFile_Generator_v2_XML_Util::isValidName("invalidTag?");
    * if (PEAR_PackageFile_Generator_v2_XML_Util::isError($result)) {
    *    print "Invalid XML name: " . $result->getMessage();
    * }
    * </code>
    *
    * @access  public
    * @static
    * @param   string  $string string that should be checked
    * @return  mixed   $valid  true, if string is a valid XML name, PEAR error otherwise
    * @todo    support for other charsets
    */
    function isValidName($string)
    {
        // check for invalid chars
        if (!preg_match("/^[[:alnum:]_\-.]\\z/", $string{0})) {
            return PEAR_PackageFile_Generator_v2_XML_Util::raiseError( "XML names may only start with letter or underscore", PEAR_PackageFile_Generator_v2_XML_Util_ERROR_INVALID_START );
        }
        
        // check for invalid chars
        if (!preg_match("/^([a-zA-Z_]([a-zA-Z0-9_\-\.]*)?:)?[a-zA-Z_]([a-zA-Z0-9_\-\.]+)?\\z/", $string)) {
            return PEAR_PackageFile_Generator_v2_XML_Util::raiseError( "XML names may only contain alphanumeric chars, period, hyphen, colon and underscores", PEAR_PackageFile_Generator_v2_XML_Util_ERROR_INVALID_CHARS );
         }
        // XML name is valid
        return true;
    }

   /**
    * replacement for PEAR_PackageFile_Generator_v2_XML_Util::raiseError
    *
    * Avoids the necessity to always require
    * PEAR.php
    *
    * @access   public
    * @param    string      error message
    * @param    integer     error code
    * @return   object PEAR_Error
    */
    function raiseError($msg, $code)
    {
        require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
        return PEAR::raiseError($msg, $code);
    }
}
?><?php
/**
 * package.xml parsing class, package.xml version 1.0
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: v1.php,v 1.27 2008/01/03 20:55:16 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * package.xml abstraction class
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v1.php';
/**
 * Parser for package.xml version 1.0
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_PackageFile_Parser_v1
{
    var $_registry;
    var $_config;
    var $_logger;
    /**
     * BC hack to allow PEAR_Common::infoFromString() to sort of
     * work with the version 2.0 format - there's no filelist though
     * @param PEAR_PackageFile_v2
     */
    function fromV2($packagefile)
    {
        $info = $packagefile->getArray(true);
        $ret = new PEAR_PackageFile_v1;
        $ret->fromArray($info['old']);
    }

    function setConfig(&$c)
    {
        $this->_config = &$c;
        $this->_registry = &$c->getRegistry();
    }

    function setLogger(&$l)
    {
        $this->_logger = &$l;
    }

    /**
     * @param string contents of package.xml file, version 1.0
     * @return bool success of parsing
     */
    function &parse($data, $file, $archive = false)
    {
        if (!extension_loaded('xml')) {
            return PEAR::raiseError('Cannot create xml parser for parsing package.xml, no xml extension');
        }
        $xp = xml_parser_create();
        if (!$xp) {
            $a = &PEAR::raiseError('Cannot create xml parser for parsing package.xml');
            return $a;
        }
        xml_set_object($xp, $this);
        xml_set_element_handler($xp, '_element_start_1_0', '_element_end_1_0');
        xml_set_character_data_handler($xp, '_pkginfo_cdata_1_0');
        xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, false);

        $this->element_stack = array();
        $this->_packageInfo = array('provides' => array());
        $this->current_element = false;
        unset($this->dir_install);
        $this->_packageInfo['filelist'] = array();
        $this->filelist =& $this->_packageInfo['filelist'];
        $this->dir_names = array();
        $this->in_changelog = false;
        $this->d_i = 0;
        $this->cdata = '';
        $this->_isValid = true;

        if (!xml_parse($xp, $data, 1)) {
            $code = xml_get_error_code($xp);
            $line = xml_get_current_line_number($xp);
            xml_parser_free($xp);
            $a = &PEAR::raiseError(sprintf("XML error: %s at line %d",
                           $str = xml_error_string($code), $line), 2);
            return $a;
        }

        xml_parser_free($xp);

        $pf = new PEAR_PackageFile_v1;
        $pf->setConfig($this->_config);
        if (isset($this->_logger)) {
            $pf->setLogger($this->_logger);
        }
        $pf->setPackagefile($file, $archive);
        $pf->fromArray($this->_packageInfo);
        return $pf;
    }
    // {{{ _unIndent()

    /**
     * Unindent given string
     *
     * @param string $str The string that has to be unindented.
     * @return string
     * @access private
     */
    function _unIndent($str)
    {
        // remove leading newlines
        $str = preg_replace('/^[\r\n]+/', '', $str);
        // find whitespace at the beginning of the first line
        $indent_len = strspn($str, " \t");
        $indent = substr($str, 0, $indent_len);
        $data = '';
        // remove the same amount of whitespace from following lines
        foreach (explode("\n", $str) as $line) {
            if (substr($line, 0, $indent_len) == $indent) {
                $data .= substr($line, $indent_len) . "\n";
            } elseif (trim(substr($line, 0, $indent_len))) {
                $data .= ltrim($line);
            }
        }
        return $data;
    }

    // Support for package DTD v1.0:
    // {{{ _element_start_1_0()

    /**
     * XML parser callback for ending elements.  Used for version 1.0
     * packages.
     *
     * @param resource  $xp    XML parser resource
     * @param string    $name  name of ending element
     *
     * @return void
     *
     * @access private
     */
    function _element_start_1_0($xp, $name, $attribs)
    {
        array_push($this->element_stack, $name);
        $this->current_element = $name;
        $spos = sizeof($this->element_stack) - 2;
        $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : '';
        $this->current_attributes = $attribs;
        $this->cdata = '';
        switch ($name) {
            case 'dir':
                if ($this->in_changelog) {
                    break;
                }
                if (array_key_exists('name', $attribs) && $attribs['name'] != '/') {
                    $attribs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
                        $attribs['name']);
                    if (strrpos($attribs['name'], '/') === strlen($attribs['name']) - 1) {
                        $attribs['name'] = substr($attribs['name'], 0,
                            strlen($attribs['name']) - 1);
                    }
                    if (strpos($attribs['name'], '/') === 0) {
                        $attribs['name'] = substr($attribs['name'], 1);
                    }
                    $this->dir_names[] = $attribs['name'];
                }
                if (isset($attribs['baseinstalldir'])) {
                    $this->dir_install = $attribs['baseinstalldir'];
                }
                if (isset($attribs['role'])) {
                    $this->dir_role = $attribs['role'];
                }
                break;
            case 'file':
                if ($this->in_changelog) {
                    break;
                }
                if (isset($attribs['name'])) {
                    $path = '';
                    if (count($this->dir_names)) {
                        foreach ($this->dir_names as $dir) {
                            $path .= $dir . '/';
                        }
                    }
                    $path .= preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
                        $attribs['name']);
                    unset($attribs['name']);
                    $this->current_path = $path;
                    $this->filelist[$path] = $attribs;
                    // Set the baseinstalldir only if the file don't have this attrib
                    if (!isset($this->filelist[$path]['baseinstalldir']) &&
                        isset($this->dir_install))
                    {
                        $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
                    }
                    // Set the Role
                    if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
                        $this->filelist[$path]['role'] = $this->dir_role;
                    }
                }
                break;
            case 'replace':
                if (!$this->in_changelog) {
                    $this->filelist[$this->current_path]['replacements'][] = $attribs;
                }
                break;
            case 'maintainers':
                $this->_packageInfo['maintainers'] = array();
                $this->m_i = 0; // maintainers array index
                break;
            case 'maintainer':
                // compatibility check
                if (!isset($this->_packageInfo['maintainers'])) {
                    $this->_packageInfo['maintainers'] = array();
                    $this->m_i = 0;
                }
                $this->_packageInfo['maintainers'][$this->m_i] = array();
                $this->current_maintainer =& $this->_packageInfo['maintainers'][$this->m_i];
                break;
            case 'changelog':
                $this->_packageInfo['changelog'] = array();
                $this->c_i = 0; // changelog array index
                $this->in_changelog = true;
                break;
            case 'release':
                if ($this->in_changelog) {
                    $this->_packageInfo['changelog'][$this->c_i] = array();
                    $this->current_release = &$this->_packageInfo['changelog'][$this->c_i];
                } else {
                    $this->current_release = &$this->_packageInfo;
                }
                break;
            case 'deps':
                if (!$this->in_changelog) {
                    $this->_packageInfo['release_deps'] = array();
                }
                break;
            case 'dep':
                // dependencies array index
                if (!$this->in_changelog) {
                    $this->d_i++;
                    isset($attribs['type']) ? ($attribs['type'] = strtolower($attribs['type'])) : false;
                    $this->_packageInfo['release_deps'][$this->d_i] = $attribs;
                }
                break;
            case 'configureoptions':
                if (!$this->in_changelog) {
                    $this->_packageInfo['configure_options'] = array();
                }
                break;
            case 'configureoption':
                if (!$this->in_changelog) {
                    $this->_packageInfo['configure_options'][] = $attribs;
                }
                break;
            case 'provides':
                if (empty($attribs['type']) || empty($attribs['name'])) {
                    break;
                }
                $attribs['explicit'] = true;
                $this->_packageInfo['provides']["$attribs[type];$attribs[name]"] = $attribs;
                break;
            case 'package' :
                if (isset($attribs['version'])) {
                    $this->_packageInfo['xsdversion'] = trim($attribs['version']);
                } else {
                    $this->_packageInfo['xsdversion'] = '1.0';
                }
                if (isset($attribs['packagerversion'])) {
                    $this->_packageInfo['packagerversion'] = $attribs['packagerversion'];
                }
                break;
        }
    }

    // }}}
    // {{{ _element_end_1_0()

    /**
     * XML parser callback for ending elements.  Used for version 1.0
     * packages.
     *
     * @param resource  $xp    XML parser resource
     * @param string    $name  name of ending element
     *
     * @return void
     *
     * @access private
     */
    function _element_end_1_0($xp, $name)
    {
        $data = trim($this->cdata);
        switch ($name) {
            case 'name':
                switch ($this->prev_element) {
                    case 'package':
                        $this->_packageInfo['package'] = $data;
                        break;
                    case 'maintainer':
                        $this->current_maintainer['name'] = $data;
                        break;
                }
                break;
            case 'extends' :
                $this->_packageInfo['extends'] = $data;
                break;
            case 'summary':
                $this->_packageInfo['summary'] = $data;
                break;
            case 'description':
                $data = $this->_unIndent($this->cdata);
                $this->_packageInfo['description'] = $data;
                break;
            case 'user':
                $this->current_maintainer['handle'] = $data;
                break;
            case 'email':
                $this->current_maintainer['email'] = $data;
                break;
            case 'role':
                $this->current_maintainer['role'] = $data;
                break;
            case 'version':
                //$data = ereg_replace ('[^a-zA-Z0-9._\-]', '_', $data);
                if ($this->in_changelog) {
                    $this->current_release['version'] = $data;
                } else {
                    $this->_packageInfo['version'] = $data;
                }
                break;
            case 'date':
                if ($this->in_changelog) {
                    $this->current_release['release_date'] = $data;
                } else {
                    $this->_packageInfo['release_date'] = $data;
                }
                break;
            case 'notes':
                // try to "de-indent" release notes in case someone
                // has been over-indenting their xml ;-)
                $data = $this->_unIndent($this->cdata);
                if ($this->in_changelog) {
                    $this->current_release['release_notes'] = $data;
                } else {
                    $this->_packageInfo['release_notes'] = $data;
                }
                break;
            case 'warnings':
                if ($this->in_changelog) {
                    $this->current_release['release_warnings'] = $data;
                } else {
                    $this->_packageInfo['release_warnings'] = $data;
                }
                break;
            case 'state':
                if ($this->in_changelog) {
                    $this->current_release['release_state'] = $data;
                } else {
                    $this->_packageInfo['release_state'] = $data;
                }
                break;
            case 'license':
                if ($this->in_changelog) {
                    $this->current_release['release_license'] = $data;
                } else {
                    $this->_packageInfo['release_license'] = $data;
                }
                break;
            case 'dep':
                if ($data && !$this->in_changelog) {
                    $this->_packageInfo['release_deps'][$this->d_i]['name'] = $data;
                }
                break;
            case 'dir':
                if ($this->in_changelog) {
                    break;
                }
                array_pop($this->dir_names);
                break;
            case 'file':
                if ($this->in_changelog) {
                    break;
                }
                if ($data) {
                    $path = '';
                    if (count($this->dir_names)) {
                        foreach ($this->dir_names as $dir) {
                            $path .= $dir . '/';
                        }
                    }
                    $path .= $data;
                    $this->filelist[$path] = $this->current_attributes;
                    // Set the baseinstalldir only if the file don't have this attrib
                    if (!isset($this->filelist[$path]['baseinstalldir']) &&
                        isset($this->dir_install))
                    {
                        $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
                    }
                    // Set the Role
                    if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
                        $this->filelist[$path]['role'] = $this->dir_role;
                    }
                }
                break;
            case 'maintainer':
                if (empty($this->_packageInfo['maintainers'][$this->m_i]['role'])) {
                    $this->_packageInfo['maintainers'][$this->m_i]['role'] = 'lead';
                }
                $this->m_i++;
                break;
            case 'release':
                if ($this->in_changelog) {
                    $this->c_i++;
                }
                break;
            case 'changelog':
                $this->in_changelog = false;
                break;
        }
        array_pop($this->element_stack);
        $spos = sizeof($this->element_stack) - 1;
        $this->current_element = ($spos > 0) ? $this->element_stack[$spos] : '';
        $this->cdata = '';
    }

    // }}}
    // {{{ _pkginfo_cdata_1_0()

    /**
     * XML parser callback for character data.  Used for version 1.0
     * packages.
     *
     * @param resource  $xp    XML parser resource
     * @param string    $name  character data
     *
     * @return void
     *
     * @access private
     */
    function _pkginfo_cdata_1_0($xp, $data)
    {
        if (isset($this->cdata)) {
            $this->cdata .= $data;
        }
    }

    // }}}
}
?><?php
/**
 * package.xml parsing class, package.xml version 2.0
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: v2.php,v 1.21 2008/01/03 20:26:37 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * base xml parser class
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/XMLParser.php';
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2.php';
/**
 * Parser for package.xml version 2.0
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_PackageFile_Parser_v2 extends PEAR_XMLParser
{
    var $_config;
    var $_logger;
    var $_registry;

    function setConfig(&$c)
    {
        $this->_config = &$c;
        $this->_registry = &$c->getRegistry();
    }

    function setLogger(&$l)
    {
        $this->_logger = &$l;
    }
    /**
     * Unindent given string
     *
     * @param string $str The string that has to be unindented.
     * @return string
     * @access private
     */
    function _unIndent($str)
    {
        // remove leading newlines
        $str = preg_replace('/^[\r\n]+/', '', $str);
        // find whitespace at the beginning of the first line
        $indent_len = strspn($str, " \t");
        $indent = substr($str, 0, $indent_len);
        $data = '';
        // remove the same amount of whitespace from following lines
        foreach (explode("\n", $str) as $line) {
            if (substr($line, 0, $indent_len) == $indent) {
                $data .= substr($line, $indent_len) . "\n";
            } else {
                $data .= $line . "\n";
            }
        }
        return $data;
    }

    /**
     * post-process data
     *
     * @param string $data
     * @param string $element element name
     */
    function postProcess($data, $element)
    {
        if ($element == 'notes') {
            return trim($this->_unIndent($data));
        }
        return trim($data);
    }

    /**
     * @param string
     * @param string file name of the package.xml
     * @param string|false name of the archive this package.xml came from, if any
     * @param string class name to instantiate and return.  This must be PEAR_PackageFile_v2 or
     *               a subclass
     * @return PEAR_PackageFile_v2
     */
    function &parse($data, $file, $archive = false, $class = 'PEAR_PackageFile_v2')
    {
        if (PEAR::isError($err = parent::parse($data, $file))) {
            return $err;
        }
        $ret = new $class;
        $ret->setConfig($this->_config);
        if (isset($this->_logger)) {
            $ret->setLogger($this->_logger);
        }
        $ret->fromArray($this->_unserializedData);
        $ret->setPackagefile($file, $archive);
        return $ret;
    }
}
?><?php
/**
 * PEAR_PackageFile_v1, package.xml version 1.0
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: v1.php,v 1.74 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * For error handling
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ErrorStack.php';

/**
 * Error code if parsing is attempted with no xml extension
 */
define('PEAR_PACKAGEFILE_ERROR_NO_XML_EXT', 3);

/**
 * Error code if creating the xml parser resource fails
 */
define('PEAR_PACKAGEFILE_ERROR_CANT_MAKE_PARSER', 4);

/**
 * Error code used for all sax xml parsing errors
 */
define('PEAR_PACKAGEFILE_ERROR_PARSER_ERROR', 5);

/**
 * Error code used when there is no name
 */
define('PEAR_PACKAGEFILE_ERROR_NO_NAME', 6);

/**
 * Error code when a package name is not valid
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_NAME', 7);

/**
 * Error code used when no summary is parsed
 */
define('PEAR_PACKAGEFILE_ERROR_NO_SUMMARY', 8);

/**
 * Error code for summaries that are more than 1 line
 */
define('PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY', 9);

/**
 * Error code used when no description is present
 */
define('PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION', 10);

/**
 * Error code used when no license is present
 */
define('PEAR_PACKAGEFILE_ERROR_NO_LICENSE', 11);

/**
 * Error code used when a <version> version number is not present
 */
define('PEAR_PACKAGEFILE_ERROR_NO_VERSION', 12);

/**
 * Error code used when a <version> version number is invalid
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_VERSION', 13);

/**
 * Error code when release state is missing
 */
define('PEAR_PACKAGEFILE_ERROR_NO_STATE', 14);

/**
 * Error code when release state is invalid
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_STATE', 15);

/**
 * Error code when release state is missing
 */
define('PEAR_PACKAGEFILE_ERROR_NO_DATE', 16);

/**
 * Error code when release state is invalid
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_DATE', 17);

/**
 * Error code when no release notes are found
 */
define('PEAR_PACKAGEFILE_ERROR_NO_NOTES', 18);

/**
 * Error code when no maintainers are found
 */
define('PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS', 19);

/**
 * Error code when a maintainer has no handle
 */
define('PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE', 20);

/**
 * Error code when a maintainer has no handle
 */
define('PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE', 21);

/**
 * Error code when a maintainer has no name
 */
define('PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME', 22);

/**
 * Error code when a maintainer has no email
 */
define('PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL', 23);

/**
 * Error code when a maintainer has no handle
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_MAINTROLE', 24);

/**
 * Error code when a dependency is not a PHP dependency, but has no name
 */
define('PEAR_PACKAGEFILE_ERROR_NO_DEPNAME', 25);

/**
 * Error code when a dependency has no type (pkg, php, etc.)
 */
define('PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE', 26);

/**
 * Error code when a dependency has no relation (lt, ge, has, etc.)
 */
define('PEAR_PACKAGEFILE_ERROR_NO_DEPREL', 27);

/**
 * Error code when a dependency is not a 'has' relation, but has no version
 */
define('PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION', 28);

/**
 * Error code when a dependency has an invalid relation
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPREL', 29);

/**
 * Error code when a dependency has an invalid type
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPTYPE', 30);

/**
 * Error code when a dependency has an invalid optional option
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL', 31);

/**
 * Error code when a dependency is a pkg dependency, and has an invalid package name
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPNAME', 32);

/**
 * Error code when a dependency has a channel="foo" attribute, and foo is not a registered channel
 */
define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_DEPCHANNEL', 33);

/**
 * Error code when rel="has" and version attribute is present.
 */
define('PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED', 34);

/**
 * Error code when type="php" and dependency name is present
 */
define('PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED', 35);

/**
 * Error code when a configure option has no name
 */
define('PEAR_PACKAGEFILE_ERROR_NO_CONFNAME', 36);

/**
 * Error code when a configure option has no name
 */
define('PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT', 37);

/**
 * Error code when a file in the filelist has an invalid role
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE', 38);

/**
 * Error code when a file in the filelist has no role
 */
define('PEAR_PACKAGEFILE_ERROR_NO_FILEROLE', 39);

/**
 * Error code when analyzing a php source file that has parse errors
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE', 40);

/**
 * Error code when analyzing a php source file reveals a source element
 * without a package name prefix
 */
define('PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX', 41);

/**
 * Error code when an unknown channel is specified
 */
define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_CHANNEL', 42);

/**
 * Error code when no files are found in the filelist
 */
define('PEAR_PACKAGEFILE_ERROR_NO_FILES', 43);

/**
 * Error code when a file is not valid php according to _analyzeSourceCode()
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_FILE', 44);

/**
 * Error code when the channel validator returns an error or warning
 */
define('PEAR_PACKAGEFILE_ERROR_CHANNELVAL', 45);

/**
 * Error code when a php5 package is packaged in php4 (analysis doesn't work)
 */
define('PEAR_PACKAGEFILE_ERROR_PHP5', 46);

/**
 * Error code when a file is listed in package.xml but does not exist
 */
define('PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND', 47);

/**
 * Error code when a <dep type="php" rel="not"... is encountered (use rel="ne")
 */
define('PEAR_PACKAGEFILE_PHP_NO_NOT', 48);

/**
 * Error code when a package.xml contains non-ISO-8859-1 characters
 */
define('PEAR_PACKAGEFILE_ERROR_NON_ISO_CHARS', 49);

/**
 * Error code when a dependency is not a 'has' relation, but has no version
 */
define('PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION', 50);

/**
 * Error code when a package has no lead developer
 */
define('PEAR_PACKAGEFILE_ERROR_NO_LEAD', 51);

/**
 * Error code when a filename begins with "."
 */
define('PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME', 52);
/**
 * package.xml encapsulator
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_PackageFile_v1
{
    /**
     * @access private
     * @var PEAR_ErrorStack
     * @access private
     */
    var $_stack;

    /**
     * A registry object, used to access the package name validation regex for non-standard channels
     * @var PEAR_Registry
     * @access private
     */
    var $_registry;

    /**
     * An object that contains a log method that matches PEAR_Common::log's signature
     * @var object
     * @access private
     */
    var $_logger;

    /**
     * Parsed package information
     * @var array
     * @access private
     */
    var $_packageInfo;

    /**
     * path to package.xml
     * @var string
     * @access private
     */
    var $_packageFile;

    /**
     * path to package .tgz or false if this is a local/extracted package.xml
     * @var string
     * @access private
     */
    var $_archiveFile;

    /**
     * @var int
     * @access private
     */
    var $_isValid = 0;

    /**
     * Determines whether this packagefile was initialized only with partial package info
     *
     * If this package file was constructed via parsing REST, it will only contain
     *
     * - package name
     * - channel name
     * - dependencies 
     * @var boolean
     * @access private
     */
    var $_incomplete = true;

    /**
     * @param bool determines whether to return a PEAR_Error object, or use the PEAR_ErrorStack
     * @param string Name of Error Stack class to use.
     */
    function PEAR_PackageFile_v1()
    {
        $this->_stack = &new PEAR_ErrorStack('PEAR_PackageFile_v1');
        $this->_stack->setErrorMessageTemplate($this->_getErrorMessage());
        $this->_isValid = 0;
    }

    function installBinary($installer)
    {
        return false;
    }

    function isExtension($name)
    {
        return false;
    }

    function setConfig(&$config)
    {
        $this->_config = &$config;
        $this->_registry = &$config->getRegistry();
    }

    function setRequestedGroup()
    {
        // placeholder
    }

    /**
     * For saving in the registry.
     *
     * Set the last version that was installed
     * @param string
     */
    function setLastInstalledVersion($version)
    {
        $this->_packageInfo['_lastversion'] = $version;
    }

    /**
     * @return string|false
     */
    function getLastInstalledVersion()
    {
        if (isset($this->_packageInfo['_lastversion'])) {
            return $this->_packageInfo['_lastversion'];
        }
        return false;
    }

    function getInstalledBinary()
    {
        return false;
    }

    function listPostinstallScripts()
    {
        return false;
    }

    function initPostinstallScripts()
    {
        return false;
    }

    function setLogger(&$logger)
    {
        if ($logger && (!is_object($logger) || !method_exists($logger, 'log'))) {
            return PEAR::raiseError('Logger must be compatible with PEAR_Common::log');
        }
        $this->_logger = &$logger;
    }

    function setPackagefile($file, $archive = false)
    {
        $this->_packageFile = $file;
        $this->_archiveFile = $archive ? $archive : $file;
    }

    function getPackageFile()
    {
        return isset($this->_packageFile) ? $this->_packageFile : false;
    }

    function getPackageType()
    {
        return 'php';
    }

    function getArchiveFile()
    {
        return $this->_archiveFile;
    }

    function packageInfo($field)
    {
        if (!is_string($field) || empty($field) ||
            !isset($this->_packageInfo[$field])) {
            return false;
        }
        return $this->_packageInfo[$field];
    }

    function setDirtree($path)
    {
        if (!isset($this->_packageInfo['dirtree'])) {
            $this->_packageInfo['dirtree'] = array();
        }
        $this->_packageInfo['dirtree'][$path] = true;
    }

    function getDirtree()
    {
        if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) {
            return $this->_packageInfo['dirtree'];
        }
        return false;
    }

    function resetDirtree()
    {
        unset($this->_packageInfo['dirtree']);
    }

    function fromArray($pinfo)
    {
        $this->_incomplete = false;
        $this->_packageInfo = $pinfo;
    }

    function isIncomplete()
    {
        return $this->_incomplete;
    }

    function getChannel()
    {
        return 'pear.php.net';
    }

    function getUri()
    {
        return false;
    }

    function getTime()
    {
        return false;
    }

    function getExtends()
    {
        if (isset($this->_packageInfo['extends'])) {
            return $this->_packageInfo['extends'];
        }
        return false;
    }

    /**
     * @return array
     */
    function toArray()
    {
        if (!$this->validate(PEAR_VALIDATE_NORMAL)) {
            return false;
        }
        return $this->getArray();
    }

    function getArray()
    {
        return $this->_packageInfo;
    }

    function getName()
    {
        return $this->getPackage();
    }

    function getPackage()
    {
        if (isset($this->_packageInfo['package'])) {
            return $this->_packageInfo['package'];
        }
        return false;
    }

    /**
     * WARNING - don't use this unless you know what you are doing
     */
    function setRawPackage($package)
    {
        $this->_packageInfo['package'] = $package;
    }

    function setPackage($package)
    {
        $this->_packageInfo['package'] = $package;
        $this->_isValid = false;
    }

    function getVersion()
    {
        if (isset($this->_packageInfo['version'])) {
            return $this->_packageInfo['version'];
        }
        return false;
    }

    function setVersion($version)
    {
        $this->_packageInfo['version'] = $version;
        $this->_isValid = false;
    }

    function clearMaintainers()
    {
        unset($this->_packageInfo['maintainers']);
    }

    function getMaintainers()
    {
        if (isset($this->_packageInfo['maintainers'])) {
            return $this->_packageInfo['maintainers'];
        }
        return false;
    }

    /**
     * Adds a new maintainer - no checking of duplicates is performed, use
     * updatemaintainer for that purpose.
     */
    function addMaintainer($role, $handle, $name, $email)
    {
        $this->_packageInfo['maintainers'][] =
            array('handle' => $handle, 'role' => $role, 'email' => $email, 'name' => $name);
        $this->_isValid = false;
    }

    function updateMaintainer($role, $handle, $name, $email)
    {
        $found = false;
        if (!isset($this->_packageInfo['maintainers']) ||
              !is_array($this->_packageInfo['maintainers'])) {
            return $this->addMaintainer($role, $handle, $name, $email);
        }
        foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) {
            if ($maintainer['handle'] == $handle) {
                $found = $i;
                break;
            }
        }
        if ($found !== false) {
            unset($this->_packageInfo['maintainers'][$found]);
            $this->_packageInfo['maintainers'] =
                array_values($this->_packageInfo['maintainers']);
        }
        $this->addMaintainer($role, $handle, $name, $email);
    }

    function deleteMaintainer($handle)
    {
        $found = false;
        foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) {
            if ($maintainer['handle'] == $handle) {
                $found = $i;
                break;
            }
        }
        if ($found !== false) {
            unset($this->_packageInfo['maintainers'][$found]);
            $this->_packageInfo['maintainers'] =
                array_values($this->_packageInfo['maintainers']);
            return true;
        }
        return false;
    }

    function getState()
    {
        if (isset($this->_packageInfo['release_state'])) {
            return $this->_packageInfo['release_state'];
        }
        return false;
    }

    function setRawState($state)
    {
        $this->_packageInfo['release_state'] = $state;
    }

    function setState($state)
    {
        $this->_packageInfo['release_state'] = $state;
        $this->_isValid = false;
    }

    function getDate()
    {
        if (isset($this->_packageInfo['release_date'])) {
            return $this->_packageInfo['release_date'];
        }
        return false;
    }

    function setDate($date)
    {
        $this->_packageInfo['release_date'] = $date;
        $this->_isValid = false;
    }

    function getLicense()
    {
        if (isset($this->_packageInfo['release_license'])) {
            return $this->_packageInfo['release_license'];
        }
        return false;
    }

    function setLicense($date)
    {
        $this->_packageInfo['release_license'] = $date;
        $this->_isValid = false;
    }

    function getSummary()
    {
        if (isset($this->_packageInfo['summary'])) {
            return $this->_packageInfo['summary'];
        }
        return false;
    }

    function setSummary($summary)
    {
        $this->_packageInfo['summary'] = $summary;
        $this->_isValid = false;
    }

    function getDescription()
    {
        if (isset($this->_packageInfo['description'])) {
            return $this->_packageInfo['description'];
        }
        return false;
    }

    function setDescription($desc)
    {
        $this->_packageInfo['description'] = $desc;
        $this->_isValid = false;
    }

    function getNotes()
    {
        if (isset($this->_packageInfo['release_notes'])) {
            return $this->_packageInfo['release_notes'];
        }
        return false;
    }

    function setNotes($notes)
    {
        $this->_packageInfo['release_notes'] = $notes;
        $this->_isValid = false;
    }

    function getDeps()
    {
        if (isset($this->_packageInfo['release_deps'])) {
            return $this->_packageInfo['release_deps'];
        }
        return false;
    }

    /**
     * Reset dependencies prior to adding new ones
     */
    function clearDeps()
    {
        unset($this->_packageInfo['release_deps']);
    }

    function addPhpDep($version, $rel)
    {
        $this->_isValid = false;
        $this->_packageInfo['release_deps'][] =
            array('type' => 'php',
                  'rel' => $rel,
                  'version' => $version);
    }

    function addPackageDep($name, $version, $rel, $optional = 'no')
    {
        $this->_isValid = false;
        $dep =
            array('type' => 'pkg',
                  'name' => $name,
                  'rel' => $rel,
                  'optional' => $optional);
        if ($rel != 'has' && $rel != 'not') {
            $dep['version'] = $version;
        }
        $this->_packageInfo['release_deps'][] = $dep;
    }

    function addExtensionDep($name, $version, $rel, $optional = 'no')
    {
        $this->_isValid = false;
        $this->_packageInfo['release_deps'][] =
            array('type' => 'ext',
                  'name' => $name,
                  'rel' => $rel,
                  'version' => $version,
                  'optional' => $optional);
    }

    /**
     * WARNING - do not use this function directly unless you know what you're doing
     */
    function setDeps($deps)
    {
        $this->_packageInfo['release_deps'] = $deps;
    }

    function hasDeps()
    {
        return isset($this->_packageInfo['release_deps']) &&
            count($this->_packageInfo['release_deps']);
    }

    function getDependencyGroup($group)
    {
        return false;
    }

    function isCompatible($pf)
    {
        return false;
    }

    function isSubpackageOf($p)
    {
        return $p->isSubpackage($this);
    }

    function isSubpackage($p)
    {
        return false;
    }

    function dependsOn($package, $channel)
    {
        if (strtolower($channel) != 'pear.php.net') {
            return false;
        }
        if (!($deps = $this->getDeps())) {
            return false;
        }
        foreach ($deps as $dep) {
            if ($dep['type'] != 'pkg') {
                continue;
            }
            if (strtolower($dep['name']) == strtolower($package)) {
                return true;
            }
        }
        return false;
    }

    function getConfigureOptions()
    {
        if (isset($this->_packageInfo['configure_options'])) {
            return $this->_packageInfo['configure_options'];
        }
        return false;
    }

    function hasConfigureOptions()
    {
        return isset($this->_packageInfo['configure_options']) &&
            count($this->_packageInfo['configure_options']);
    }

    function addConfigureOption($name, $prompt, $default = false)
    {
        $o = array('name' => $name, 'prompt' => $prompt);
        if ($default !== false) {
            $o['default'] = $default;
        }
        if (!isset($this->_packageInfo['configure_options'])) {
            $this->_packageInfo['configure_options'] = array();
        }
        $this->_packageInfo['configure_options'][] = $o;
    }

    function clearConfigureOptions()
    {
        unset($this->_packageInfo['configure_options']);
    }

    function getProvides()
    {
        if (isset($this->_packageInfo['provides'])) {
            return $this->_packageInfo['provides'];
        }
        return false;
    }

    function getProvidesExtension()
    {
        return false;
    }

    function addFile($dir, $file, $attrs)
    {
        $dir = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $dir);
        if ($dir == '/' || $dir == '') {
            $dir = '';
        } else {
            $dir .= '/';
        }
        $file = $dir . $file;
        $file = preg_replace('![\\/]+!', '/', $file);
        $this->_packageInfo['filelist'][$file] = $attrs;
    }

    function getInstallationFilelist()
    {
        return $this->getFilelist();
    }

    function getFilelist()
    {
        if (isset($this->_packageInfo['filelist'])) {
            return $this->_packageInfo['filelist'];
        }
        return false;
    }

    function setFileAttribute($file, $attr, $value)
    {
        $this->_packageInfo['filelist'][$file][$attr] = $value;
    }

    function resetFilelist()
    {
        $this->_packageInfo['filelist'] = array();
    }

    function setInstalledAs($file, $path)
    {
        if ($path) {
            return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
        }
        unset($this->_packageInfo['filelist'][$file]['installed_as']);
    }

    function installedFile($file, $atts)
    {
        if (isset($this->_packageInfo['filelist'][$file])) {
            $this->_packageInfo['filelist'][$file] =
                array_merge($this->_packageInfo['filelist'][$file], $atts);
        } else {
            $this->_packageInfo['filelist'][$file] = $atts;
        }
    }

    function getChangelog()
    {
        if (isset($this->_packageInfo['changelog'])) {
            return $this->_packageInfo['changelog'];
        }
        return false;
    }

    function getPackagexmlVersion()
    {
        return '1.0';
    }

    /**
     * Wrapper to {@link PEAR_ErrorStack::getErrors()}
     * @param boolean determines whether to purge the error stack after retrieving
     * @return array
     */
    function getValidationWarnings($purge = true)
    {
        return $this->_stack->getErrors($purge);
    }

    // }}}
    /**
     * Validation error.  Also marks the object contents as invalid
     * @param error code
     * @param array error information
     * @access private
     */
    function _validateError($code, $params = array())
    {
        $this->_stack->push($code, 'error', $params, false, false, debug_backtrace());
        $this->_isValid = false;
    }

    /**
     * Validation warning.  Does not mark the object contents invalid.
     * @param error code
     * @param array error information
     * @access private
     */
    function _validateWarning($code, $params = array())
    {
        $this->_stack->push($code, 'warning', $params, false, false, debug_backtrace());
    }

    /**
     * @param integer error code
     * @access protected
     */
    function _getErrorMessage()
    {
        return array(
                PEAR_PACKAGEFILE_ERROR_NO_NAME =>
                    'Missing Package Name',
                PEAR_PACKAGEFILE_ERROR_NO_SUMMARY =>
                    'No summary found',
                PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY =>
                    'Summary should be on one line',
                PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION =>
                    'Missing description',
                PEAR_PACKAGEFILE_ERROR_NO_LICENSE =>
                    'Missing license',
                PEAR_PACKAGEFILE_ERROR_NO_VERSION =>
                    'No release version found',
                PEAR_PACKAGEFILE_ERROR_NO_STATE =>
                    'No release state found',
                PEAR_PACKAGEFILE_ERROR_NO_DATE =>
                    'No release date found',
                PEAR_PACKAGEFILE_ERROR_NO_NOTES =>
                    'No release notes found',
                PEAR_PACKAGEFILE_ERROR_NO_LEAD =>
                    'Package must have at least one lead maintainer',
                PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS =>
                    'No maintainers found, at least one must be defined',
                PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE =>
                    'Maintainer %index% has no handle (user ID at channel server)',
                PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE =>
                    'Maintainer %index% has no role',
                PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME =>
                    'Maintainer %index% has no name',
                PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL =>
                    'Maintainer %index% has no email',
                PEAR_PACKAGEFILE_ERROR_NO_DEPNAME =>
                    'Dependency %index% is not a php dependency, and has no name',
                PEAR_PACKAGEFILE_ERROR_NO_DEPREL =>
                    'Dependency %index% has no relation (rel)',
                PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE =>
                    'Dependency %index% has no type',
                PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED =>
                    'PHP Dependency %index% has a name attribute of "%name%" which will be' .
                        ' ignored!',
                PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION =>
                    'Dependency %index% is not a rel="has" or rel="not" dependency, ' .
                        'and has no version',
                PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION =>
                    'Dependency %index% is a type="php" dependency, ' .
                        'and has no version',
                PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED =>
                    'Dependency %index% is a rel="%rel%" dependency, versioning is ignored',
                PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL =>
                    'Dependency %index% has invalid optional value "%opt%", should be yes or no',
                PEAR_PACKAGEFILE_PHP_NO_NOT =>
                    'Dependency %index%: php dependencies cannot use "not" rel, use "ne"' .
                        ' to exclude specific versions',
                PEAR_PACKAGEFILE_ERROR_NO_CONFNAME =>
                    'Configure Option %index% has no name',
                PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT =>
                    'Configure Option %index% has no prompt',
                PEAR_PACKAGEFILE_ERROR_NO_FILES =>
                    'No files in <filelist> section of package.xml',
                PEAR_PACKAGEFILE_ERROR_NO_FILEROLE =>
                    'File "%file%" has no role, expecting one of "%roles%"',
                PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE =>
                    'File "%file%" has invalid role "%role%", expecting one of "%roles%"',
                PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME =>
                    'File "%file%" cannot start with ".", cannot package or install',
                PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE =>
                    'Parser error: invalid PHP found in file "%file%"',
                PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX =>
                    'in %file%: %type% "%name%" not prefixed with package name "%package%"',
                PEAR_PACKAGEFILE_ERROR_INVALID_FILE =>
                    'Parser error: invalid PHP file "%file%"',
                PEAR_PACKAGEFILE_ERROR_CHANNELVAL =>
                    'Channel validator error: field "%field%" - %reason%',
                PEAR_PACKAGEFILE_ERROR_PHP5 =>
                    'Error, PHP5 token encountered in %file%, analysis should be in PHP5',
                PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND =>
                    'File "%file%" in package.xml does not exist',
                PEAR_PACKAGEFILE_ERROR_NON_ISO_CHARS =>
                    'Package.xml contains non-ISO-8859-1 characters, and may not validate',
            );
    }

    /**
     * Validate XML package definition file.
     *
     * @access public
     * @return boolean
     */
    function validate($state = PEAR_VALIDATE_NORMAL, $nofilechecking = false)
    {
        if (($this->_isValid & $state) == $state) {
            return true;
        }
        $this->_isValid = true;
        $info = $this->_packageInfo;
        if (empty($info['package'])) {
            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NAME);
            $this->_packageName = $pn = 'unknown';
        } else {
            $this->_packageName = $pn = $info['package'];
        }

        if (empty($info['summary'])) {
            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_SUMMARY);
        } elseif (strpos(trim($info['summary']), "\n") !== false) {
            $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY,
                array('summary' => $info['summary']));
        }
        if (empty($info['description'])) {
            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION);
        }
        if (empty($info['release_license'])) {
            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LICENSE);
        }
        if (empty($info['version'])) {
            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_VERSION);
        }
        if (empty($info['release_state'])) {
            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_STATE);
        }
        if (empty($info['release_date'])) {
            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DATE);
        }
        if (empty($info['release_notes'])) {
            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NOTES);
        }
        if (empty($info['maintainers'])) {
            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS);
        } else {
            $haslead = false;
            $i = 1;
            foreach ($info['maintainers'] as $m) {
                if (empty($m['handle'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE,
                        array('index' => $i));
                }
                if (empty($m['role'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE,
                        array('index' => $i, 'roles' => PEAR_Common::getUserRoles()));
                } elseif ($m['role'] == 'lead') {
                    $haslead = true;
                }
                if (empty($m['name'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME,
                        array('index' => $i));
                }
                if (empty($m['email'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL,
                        array('index' => $i));
                }
                $i++;
            }
            if (!$haslead) {
                $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LEAD);
            }
        }
        if (!empty($info['release_deps'])) {
            $i = 1;
            foreach ($info['release_deps'] as $d) {
                if (!isset($d['type']) || empty($d['type'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE,
                        array('index' => $i, 'types' => PEAR_Common::getDependencyTypes()));
                    continue;
                }
                if (!isset($d['rel']) || empty($d['rel'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPREL,
                        array('index' => $i, 'rels' => PEAR_Common::getDependencyRelations()));
                    continue;
                }
                if (!empty($d['optional'])) {
                    if (!in_array($d['optional'], array('yes', 'no'))) {
                        $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL,
                            array('index' => $i, 'opt' => $d['optional']));
                    }
                }
                if ($d['rel'] != 'has' && $d['rel'] != 'not' && empty($d['version'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION,
                        array('index' => $i));
                } elseif (($d['rel'] == 'has' || $d['rel'] == 'not') && !empty($d['version'])) {
                    $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED,
                        array('index' => $i, 'rel' => $d['rel']));
                }
                if ($d['type'] == 'php' && !empty($d['name'])) {
                    $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED,
                        array('index' => $i, 'name' => $d['name']));
                } elseif ($d['type'] != 'php' && empty($d['name'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPNAME,
                        array('index' => $i));
                }
                if ($d['type'] == 'php' && empty($d['version'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION,
                        array('index' => $i));
                }
                if (($d['rel'] == 'not') && ($d['type'] == 'php')) {
                    $this->_validateError(PEAR_PACKAGEFILE_PHP_NO_NOT,
                        array('index' => $i));
                }
                $i++;
            }
        }
        if (!empty($info['configure_options'])) {
            $i = 1;
            foreach ($info['configure_options'] as $c) {
                if (empty($c['name'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFNAME,
                        array('index' => $i));
                }
                if (empty($c['prompt'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT,
                        array('index' => $i));
                }
                $i++;
            }
        }
        if (empty($info['filelist'])) {
            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILES);
            $errors[] = 'no files';
        } else {
            foreach ($info['filelist'] as $file => $fa) {
                if (empty($fa['role'])) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILEROLE,
                        array('file' => $file, 'roles' => PEAR_Common::getFileRoles()));
                    continue;
                } elseif (!in_array($fa['role'], PEAR_Common::getFileRoles())) {
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE,
                        array('file' => $file, 'role' => $fa['role'], 'roles' => PEAR_Common::getFileRoles()));
                }
                if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~', str_replace('\\', '/', $file))) {
                    // file contains .. parent directory or . cur directory references
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
                        array('file' => $file));
                }
                if (isset($fa['install-as']) &&
                      preg_match('~/\.\.?(/|\\z)|^\.\.?/~', 
                                 str_replace('\\', '/', $fa['install-as']))) {
                    // install-as contains .. parent directory or . cur directory references
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
                        array('file' => $file . ' [installed as ' . $fa['install-as'] . ']'));
                }
                if (isset($fa['baseinstalldir']) &&
                      preg_match('~/\.\.?(/|\\z)|^\.\.?/~', 
                                 str_replace('\\', '/', $fa['baseinstalldir']))) {
                    // install-as contains .. parent directory or . cur directory references
                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
                        array('file' => $file . ' [baseinstalldir ' . $fa['baseinstalldir'] . ']'));
                }
            }
        }
        if (isset($this->_registry) && $this->_isValid) {
            $chan = $this->_registry->getChannel('pear.php.net');
            if (PEAR::isError($chan)) {
                $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $chan->getMessage());
                return $this->_isValid = 0;
            }
            $validator = $chan->getValidationObject();
            $validator->setPackageFile($this);
            $validator->validate($state);
            $failures = $validator->getFailures();
            foreach ($failures['errors'] as $error) {
                $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $error);
            }
            foreach ($failures['warnings'] as $warning) {
                $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $warning);
            }
        }
        if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$nofilechecking) {
            if ($this->_analyzePhpFiles()) {
                $this->_isValid = true;
            }
        }
        if ($this->_isValid) {
            return $this->_isValid = $state;
        }
        return $this->_isValid = 0;
    }

    function _analyzePhpFiles()
    {
        if (!$this->_isValid) {
            return false;
        }
        if (!isset($this->_packageFile)) {
            return false;
        }
        $dir_prefix = dirname($this->_packageFile);
        $common = new PEAR_Common;
        $log = isset($this->_logger) ? array(&$this->_logger, 'log') :
            array($common, 'log');
        $info = $this->getFilelist();
        foreach ($info as $file => $fa) {
            if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $file)) {
                $this->_validateError(PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND,
                    array('file' => realpath($dir_prefix) . DIRECTORY_SEPARATOR . $file));
                continue;
            }
            if ($fa['role'] == 'php' && $dir_prefix) {
                call_user_func_array($log, array(1, "Analyzing $file"));
                $srcinfo = $this->_analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file);
                if ($srcinfo) {
                    $this->_buildProvidesArray($srcinfo);
                }
            }
        }
        $this->_packageName = $pn = $this->getPackage();
        $pnl = strlen($pn);
        if (isset($this->_packageInfo['provides'])) {
            foreach ((array) $this->_packageInfo['provides'] as $key => $what) {
                if (isset($what['explicit'])) {
                    // skip conformance checks if the provides entry is
                    // specified in the package.xml file
                    continue;
                }
                extract($what);
                if ($type == 'class') {
                    if (!strncasecmp($name, $pn, $pnl)) {
                        continue;
                    }
                    $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX,
                        array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn));
                } elseif ($type == 'function') {
                    if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) {
                        continue;
                    }
                    $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX,
                        array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn));
                }
            }
        }
        return $this->_isValid;
    }

    /**
     * Get the default xml generator object
     *
     * @return PEAR_PackageFile_Generator_v1
     */
    function &getDefaultGenerator()
    {
        if (!class_exists('PEAR_PackageFile_Generator_v1')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/Generator/v1.php';
        }
        $a = &new PEAR_PackageFile_Generator_v1($this);
        return $a;
    }

    /**
     * Get the contents of a file listed within the package.xml
     * @param string
     * @return string
     */
    function getFileContents($file)
    {
        if ($this->_archiveFile == $this->_packageFile) { // unpacked
            $dir = dirname($this->_packageFile);
            $file = $dir . DIRECTORY_SEPARATOR . $file;
            $file = str_replace(array('/', '\\'),
                array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file);
            if (file_exists($file) && is_readable($file)) {
                return implode('', file($file));
            }
        } else { // tgz
            if (!class_exists('Archive_Tar')) {
                require_once 'phar://install-pear-nozlib.phar/' . 'Archive/Tar.php';
            }
            $tar = &new Archive_Tar($this->_archiveFile);
            $tar->pushErrorHandling(PEAR_ERROR_RETURN);
            if ($file != 'package.xml' && $file != 'package2.xml') {
                $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file;
            }
            $file = $tar->extractInString($file);
            $tar->popErrorHandling();
            if (PEAR::isError($file)) {
                return PEAR::raiseError("Cannot locate file '$file' in archive");
            }
            return $file;
        }
    }

    // {{{ analyzeSourceCode()
    /**
     * Analyze the source code of the given PHP file
     *
     * @param  string Filename of the PHP file
     * @return mixed
     * @access private
     */
    function _analyzeSourceCode($file)
    {
        if (!function_exists("token_get_all")) {
            return false;
        }
        if (!defined('T_DOC_COMMENT')) {
            define('T_DOC_COMMENT', T_COMMENT);
        }
        if (!defined('T_INTERFACE')) {
            define('T_INTERFACE', -1);
        }
        if (!defined('T_IMPLEMENTS')) {
            define('T_IMPLEMENTS', -1);
        }
        if (!$fp = @fopen($file, "r")) {
            return false;
        }
        fclose($fp);
        $contents = file_get_contents($file);
        $tokens = token_get_all($contents);
/*
        for ($i = 0; $i < sizeof($tokens); $i++) {
            @list($token, $data) = $tokens[$i];
            if (is_string($token)) {
                var_dump($token);
            } else {
                print token_name($token) . ' ';
                var_dump(rtrim($data));
            }
        }
*/
        $look_for = 0;
        $paren_level = 0;
        $bracket_level = 0;
        $brace_level = 0;
        $lastphpdoc = '';
        $current_class = '';
        $current_interface = '';
        $current_class_level = -1;
        $current_function = '';
        $current_function_level = -1;
        $declared_classes = array();
        $declared_interfaces = array();
        $declared_functions = array();
        $declared_methods = array();
        $used_classes = array();
        $used_functions = array();
        $extends = array();
        $implements = array();
        $nodeps = array();
        $inquote = false;
        $interface = false;
        for ($i = 0; $i < sizeof($tokens); $i++) {
            if (is_array($tokens[$i])) {
                list($token, $data) = $tokens[$i];
            } else {
                $token = $tokens[$i];
                $data = '';
            }
            if ($inquote) {
                if ($token != '"' && $token != T_END_HEREDOC) {
                    continue;
                } else {
                    $inquote = false;
                    continue;
                }
            }
            switch ($token) {
                case T_WHITESPACE :
                    continue;
                case ';':
                    if ($interface) {
                        $current_function = '';
                        $current_function_level = -1;
                    }
                    break;
                case '"':
                case T_START_HEREDOC:
                    $inquote = true;
                    break;
                case T_CURLY_OPEN:
                case T_DOLLAR_OPEN_CURLY_BRACES:
                case '{': $brace_level++; continue 2;
                case '}':
                    $brace_level--;
                    if ($current_class_level == $brace_level) {
                        $current_class = '';
                        $current_class_level = -1;
                    }
                    if ($current_function_level == $brace_level) {
                        $current_function = '';
                        $current_function_level = -1;
                    }
                    continue 2;
                case '[': $bracket_level++; continue 2;
                case ']': $bracket_level--; continue 2;
                case '(': $paren_level++;   continue 2;
                case ')': $paren_level--;   continue 2;
                case T_INTERFACE:
                    $interface = true;
                case T_CLASS:
                    if (($current_class_level != -1) || ($current_function_level != -1)) {
                        $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE,
                            array('file' => $file));
                        return false;
                    }
                case T_FUNCTION:
                case T_NEW:
                case T_EXTENDS:
                case T_IMPLEMENTS:
                    $look_for = $token;
                    continue 2;
                case T_STRING:
                    if (version_compare(zend_version(), '2.0', '<')) {
                        if (in_array(strtolower($data),
                            array('public', 'private', 'protected', 'abstract',
                                  'interface', 'implements', 'throw') 
                                 )) {
                            $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_PHP5,
                                array($file));
                        }
                    }
                    if ($look_for == T_CLASS) {
                        $current_class = $data;
                        $current_class_level = $brace_level;
                        $declared_classes[] = $current_class;
                    } elseif ($look_for == T_INTERFACE) {
                        $current_interface = $data;
                        $current_class_level = $brace_level;
                        $declared_interfaces[] = $current_interface;
                    } elseif ($look_for == T_IMPLEMENTS) {
                        $implements[$current_class] = $data;
                    } elseif ($look_for == T_EXTENDS) {
                        $extends[$current_class] = $data;
                    } elseif ($look_for == T_FUNCTION) {
                        if ($current_class) {
                            $current_function = "$current_class::$data";
                            $declared_methods[$current_class][] = $data;
                        } elseif ($current_interface) {
                            $current_function = "$current_interface::$data";
                            $declared_methods[$current_interface][] = $data;
                        } else {
                            $current_function = $data;
                            $declared_functions[] = $current_function;
                        }
                        $current_function_level = $brace_level;
                        $m = array();
                    } elseif ($look_for == T_NEW) {
                        $used_classes[$data] = true;
                    }
                    $look_for = 0;
                    continue 2;
                case T_VARIABLE:
                    $look_for = 0;
                    continue 2;
                case T_DOC_COMMENT:
                case T_COMMENT:
                    if (preg_match('!^/\*\*\s!', $data)) {
                        $lastphpdoc = $data;
                        if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
                            $nodeps = array_merge($nodeps, $m[1]);
                        }
                    }
                    continue 2;
                case T_DOUBLE_COLON:
                    if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) {
                        $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE,
                            array('file' => $file));
                        return false;
                    }
                    $class = $tokens[$i - 1][1];
                    if (strtolower($class) != 'parent') {
                        $used_classes[$class] = true;
                    }
                    continue 2;
            }
        }
        return array(
            "source_file" => $file,
            "declared_classes" => $declared_classes,
            "declared_interfaces" => $declared_interfaces,
            "declared_methods" => $declared_methods,
            "declared_functions" => $declared_functions,
            "used_classes" => array_diff(array_keys($used_classes), $nodeps),
            "inheritance" => $extends,
            "implements" => $implements,
            );
    }

    /**
     * Build a "provides" array from data returned by
     * analyzeSourceCode().  The format of the built array is like
     * this:
     *
     *  array(
     *    'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
     *    ...
     *  )
     *
     *
     * @param array $srcinfo array with information about a source file
     * as returned by the analyzeSourceCode() method.
     *
     * @return void
     *
     * @access private
     *
     */
    function _buildProvidesArray($srcinfo)
    {
        if (!$this->_isValid) {
            return false;
        }
        $file = basename($srcinfo['source_file']);
        $pn = $this->getPackage();
        $pnl = strlen($pn);
        foreach ($srcinfo['declared_classes'] as $class) {
            $key = "class;$class";
            if (isset($this->_packageInfo['provides'][$key])) {
                continue;
            }
            $this->_packageInfo['provides'][$key] =
                array('file'=> $file, 'type' => 'class', 'name' => $class);
            if (isset($srcinfo['inheritance'][$class])) {
                $this->_packageInfo['provides'][$key]['extends'] =
                    $srcinfo['inheritance'][$class];
            }
        }
        foreach ($srcinfo['declared_methods'] as $class => $methods) {
            foreach ($methods as $method) {
                $function = "$class::$method";
                $key = "function;$function";
                if ($method{0} == '_' || !strcasecmp($method, $class) ||
                    isset($this->_packageInfo['provides'][$key])) {
                    continue;
                }
                $this->_packageInfo['provides'][$key] =
                    array('file'=> $file, 'type' => 'function', 'name' => $function);
            }
        }

        foreach ($srcinfo['declared_functions'] as $function) {
            $key = "function;$function";
            if ($function{0} == '_' || isset($this->_packageInfo['provides'][$key])) {
                continue;
            }
            if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
                $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
            }
            $this->_packageInfo['provides'][$key] =
                array('file'=> $file, 'type' => 'function', 'name' => $function);
        }
    }

    // }}}
}
?>
<?php
/**
 * PEAR_PackageFile_v2, package.xml version 2.0
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: v2.php,v 1.143 2008/05/13 05:28:51 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * For error handling
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ErrorStack.php';
/**
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_PackageFile_v2
{

    /**
     * Parsed package information
     * @var array
     * @access private
     */
    var $_packageInfo = array();

    /**
     * path to package .tgz or false if this is a local/extracted package.xml
     * @var string|false
     * @access private
     */
    var $_archiveFile;

    /**
     * path to package .xml or false if this is an abstract parsed-from-string xml
     * @var string|false
     * @access private
     */
    var $_packageFile;

    /**
     * This is used by file analysis routines to log progress information
     * @var PEAR_Common
     * @access protected
     */
    var $_logger;

    /**
     * This is set to the highest validation level that has been validated
     *
     * If the package.xml is invalid or unknown, this is set to 0.  If
     * normal validation has occurred, this is set to PEAR_VALIDATE_NORMAL.  If
     * downloading/installation validation has occurred it is set to PEAR_VALIDATE_DOWNLOADING
     * or INSTALLING, and so on up to PEAR_VALIDATE_PACKAGING.  This allows validation
     * "caching" to occur, which is particularly important for package validation, so
     * that PHP files are not validated twice
     * @var int
     * @access private
     */
    var $_isValid = 0;

    /**
     * True if the filelist has been validated
     * @param bool
     */
    var $_filesValid = false;

    /**
     * @var PEAR_Registry
     * @access protected
     */
    var $_registry;

    /**
     * @var PEAR_Config
     * @access protected
     */
    var $_config;

    /**
     * Optional Dependency group requested for installation
     * @var string
     * @access private
     */
    var $_requestedGroup = false;

    /**
     * @var PEAR_ErrorStack
     * @access protected
     */
    var $_stack;

    /**
     * Namespace prefix used for tasks in this package.xml - use tasks: whenever possible
     */
    var $_tasksNs;

    /**
     * Determines whether this packagefile was initialized only with partial package info
     *
     * If this package file was constructed via parsing REST, it will only contain
     *
     * - package name
     * - channel name
     * - dependencies
     * @var boolean
     * @access private
     */
    var $_incomplete = true;

    /**
     * @var PEAR_PackageFile_v2_Validator
     */
    var $_v2Validator;

    /**
     * The constructor merely sets up the private error stack
     */
    function PEAR_PackageFile_v2()
    {
        $this->_stack = new PEAR_ErrorStack('PEAR_PackageFile_v2', false, null);
        $this->_isValid = false;
    }

    /**
     * To make unit-testing easier
     * @param PEAR_Frontend_*
     * @param array options
     * @param PEAR_Config
     * @return PEAR_Downloader
     * @access protected
     */
    function &getPEARDownloader(&$i, $o, &$c)
    {
        $z = &new PEAR_Downloader($i, $o, $c);
        return $z;
    }

    /**
     * To make unit-testing easier
     * @param PEAR_Config
     * @param array options
     * @param array package name as returned from {@link PEAR_Registry::parsePackageName()}
     * @param int PEAR_VALIDATE_* constant
     * @return PEAR_Dependency2
     * @access protected
     */
    function &getPEARDependency2(&$c, $o, $p, $s = PEAR_VALIDATE_INSTALLING)
    {
        if (!class_exists('PEAR_Dependency2')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Dependency2.php';
        }
        $z = &new PEAR_Dependency2($c, $o, $p, $s);
        return $z;
    }

    function getInstalledBinary()
    {
        return isset($this->_packageInfo['#binarypackage']) ? $this->_packageInfo['#binarypackage'] :
            false;
    }

    /**
     * Installation of source package has failed, attempt to download and install the
     * binary version of this package.
     * @param PEAR_Installer
     * @return array|false
     */
    function installBinary(&$installer)
    {
        if (!OS_WINDOWS) {
            $a = false;
            return $a;
        }
        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
            $releasetype = $this->getPackageType() . 'release';
            if (!is_array($installer->getInstallPackages())) {
                $a = false;
                return $a;
            }
            foreach ($installer->getInstallPackages() as $p) {
                if ($p->isExtension($this->_packageInfo['providesextension'])) {
                    if ($p->getPackageType() != 'extsrc' && $p->getPackageType() != 'zendextsrc') {
                        $a = false;
                        return $a; // the user probably downloaded it separately
                    }
                }
            }
            if (isset($this->_packageInfo[$releasetype]['binarypackage'])) {
                $installer->log(0, 'Attempting to download binary version of extension "' .
                    $this->_packageInfo['providesextension'] . '"');
                $params = $this->_packageInfo[$releasetype]['binarypackage'];
                if (!is_array($params) || !isset($params[0])) {
                    $params = array($params);
                }
                if (isset($this->_packageInfo['channel'])) {
                    foreach ($params as $i => $param) {
                        $params[$i] = array('channel' => $this->_packageInfo['channel'],
                            'package' => $param, 'version' => $this->getVersion());
                    }
                }
                $dl = &$this->getPEARDownloader($installer->ui, $installer->getOptions(),
                    $installer->config);
                $verbose = $dl->config->get('verbose');
                $dl->config->set('verbose', -1);
                foreach ($params as $param) {
                    PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                    $ret = $dl->download(array($param));
                    PEAR::popErrorHandling();
                    if (is_array($ret) && count($ret)) {
                        break;
                    }
                }
                $dl->config->set('verbose', $verbose);
                if (is_array($ret)) {
                    if (count($ret) == 1) {
                        $pf = $ret[0]->getPackageFile();
                        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                        $err = $installer->install($ret[0]);
                        PEAR::popErrorHandling();
                        if (is_array($err)) {
                            $this->_packageInfo['#binarypackage'] = $ret[0]->getPackage();
                            // "install" self, so all dependencies will work transparently
                            $this->_registry->addPackage2($this);
                            $installer->log(0, 'Download and install of binary extension "' .
                                $this->_registry->parsedPackageNameToString(
                                    array('channel' => $pf->getChannel(),
                                          'package' => $pf->getPackage()), true) . '" successful');
                            $a = array($ret[0], $err);
                            return $a;
                        }
                        $installer->log(0, 'Download and install of binary extension "' .
                            $this->_registry->parsedPackageNameToString(
                                    array('channel' => $pf->getChannel(),
                                          'package' => $pf->getPackage()), true) . '" failed');
                    }
                }
            }
        }
        $a = false;
        return $a;
    }

    /**
     * @return string|false Extension name
     */
    function getProvidesExtension()
    {
        if (in_array($this->getPackageType(),
              array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
            if (isset($this->_packageInfo['providesextension'])) {
                return $this->_packageInfo['providesextension'];
            }
        }
        return false;
    }

    /**
     * @param string Extension name
     * @return bool
     */
    function isExtension($extension)
    {
        if (in_array($this->getPackageType(),
              array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
            return $this->_packageInfo['providesextension'] == $extension;
        }
        return false;
    }

    /**
     * Tests whether every part of the package.xml 1.0 is represented in
     * this package.xml 2.0
     * @param PEAR_PackageFile_v1
     * @return bool
     */
    function isEquivalent($pf1)
    {
        if (!$pf1) {
            return true;
        }
        if ($this->getPackageType() == 'bundle') {
            return false;
        }
        $this->_stack->getErrors(true);
        if (!$pf1->validate(PEAR_VALIDATE_NORMAL)) {
            return false;
        }
        $pass = true;
        if ($pf1->getPackage() != $this->getPackage()) {
            $this->_differentPackage($pf1->getPackage());
            $pass = false;
        }
        if ($pf1->getVersion() != $this->getVersion()) {
            $this->_differentVersion($pf1->getVersion());
            $pass = false;
        }
        if (trim($pf1->getSummary()) != $this->getSummary()) {
            $this->_differentSummary($pf1->getSummary());
            $pass = false;
        }
        if (preg_replace('/\s+/', '', $pf1->getDescription()) !=
              preg_replace('/\s+/', '', $this->getDescription())) {
            $this->_differentDescription($pf1->getDescription());
            $pass = false;
        }
        if ($pf1->getState() != $this->getState()) {
            $this->_differentState($pf1->getState());
            $pass = false;
        }
        if (!strstr(preg_replace('/\s+/', '', $this->getNotes()),
              preg_replace('/\s+/', '', $pf1->getNotes()))) {
            $this->_differentNotes($pf1->getNotes());
            $pass = false;
        }
        $mymaintainers = $this->getMaintainers();
        $yourmaintainers = $pf1->getMaintainers();
        for ($i1 = 0; $i1 < count($yourmaintainers); $i1++) {
            $reset = false;
            for ($i2 = 0; $i2 < count($mymaintainers); $i2++) {
                if ($mymaintainers[$i2]['handle'] == $yourmaintainers[$i1]['handle']) {
                    if ($mymaintainers[$i2]['role'] != $yourmaintainers[$i1]['role']) {
                        $this->_differentRole($mymaintainers[$i2]['handle'],
                            $yourmaintainers[$i1]['role'], $mymaintainers[$i2]['role']);
                        $pass = false;
                    }
                    if ($mymaintainers[$i2]['email'] != $yourmaintainers[$i1]['email']) {
                        $this->_differentEmail($mymaintainers[$i2]['handle'],
                            $yourmaintainers[$i1]['email'], $mymaintainers[$i2]['email']);
                        $pass = false;
                    }
                    if ($mymaintainers[$i2]['name'] != $yourmaintainers[$i1]['name']) {
                        $this->_differentName($mymaintainers[$i2]['handle'],
                            $yourmaintainers[$i1]['name'], $mymaintainers[$i2]['name']);
                        $pass = false;
                    }
                    unset($mymaintainers[$i2]);
                    $mymaintainers = array_values($mymaintainers);
                    unset($yourmaintainers[$i1]);
                    $yourmaintainers = array_values($yourmaintainers);
                    $reset = true;
                    break;
                }
            }
            if ($reset) {
                $i1 = -1;
            }
        }
        $this->_unmatchedMaintainers($mymaintainers, $yourmaintainers);
        $filelist = $this->getFilelist();
        foreach ($pf1->getFilelist() as $file => $atts) {
            if (!isset($filelist[$file])) {
                $this->_missingFile($file);
                $pass = false;
            }
        }
        return $pass;
    }

    function _differentPackage($package)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('package' => $package,
            'self' => $this->getPackage()),
            'package.xml 1.0 package "%package%" does not match "%self%"');
    }

    function _differentVersion($version)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('version' => $version,
            'self' => $this->getVersion()),
            'package.xml 1.0 version "%version%" does not match "%self%"');
    }

    function _differentState($state)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('state' => $state,
            'self' => $this->getState()),
            'package.xml 1.0 state "%state%" does not match "%self%"');
    }

    function _differentRole($handle, $role, $selfrole)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
            'role' => $role, 'self' => $selfrole),
            'package.xml 1.0 maintainer "%handle%" role "%role%" does not match "%self%"');
    }

    function _differentEmail($handle, $email, $selfemail)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
            'email' => $email, 'self' => $selfemail),
            'package.xml 1.0 maintainer "%handle%" email "%email%" does not match "%self%"');
    }

    function _differentName($handle, $name, $selfname)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
            'name' => $name, 'self' => $selfname),
            'package.xml 1.0 maintainer "%handle%" name "%name%" does not match "%self%"');
    }

    function _unmatchedMaintainers($my, $yours)
    {
        if ($my) {
            array_walk($my, create_function('&$i, $k', '$i = $i["handle"];'));
            $this->_stack->push(__FUNCTION__, 'error', array('handles' => $my),
                'package.xml 2.0 has unmatched extra maintainers "%handles%"');
        }
        if ($yours) {
            array_walk($yours, create_function('&$i, $k', '$i = $i["handle"];'));
            $this->_stack->push(__FUNCTION__, 'error', array('handles' => $yours),
                'package.xml 1.0 has unmatched extra maintainers "%handles%"');
        }
    }

    function _differentNotes($notes)
    {
        $truncnotes = strlen($notes) < 25 ? $notes : substr($notes, 0, 24) . '...';
        $truncmynotes = strlen($this->getNotes()) < 25 ? $this->getNotes() :
            substr($this->getNotes(), 0, 24) . '...';
        $this->_stack->push(__FUNCTION__, 'error', array('notes' => $truncnotes,
            'self' => $truncmynotes),
            'package.xml 1.0 release notes "%notes%" do not match "%self%"');
    }

    function _differentSummary($summary)
    {
        $truncsummary = strlen($summary) < 25 ? $summary : substr($summary, 0, 24) . '...';
        $truncmysummary = strlen($this->getsummary()) < 25 ? $this->getSummary() :
            substr($this->getsummary(), 0, 24) . '...';
        $this->_stack->push(__FUNCTION__, 'error', array('summary' => $truncsummary,
            'self' => $truncmysummary),
            'package.xml 1.0 summary "%summary%" does not match "%self%"');
    }

    function _differentDescription($description)
    {
        $truncdescription = trim(strlen($description) < 25 ? $description : substr($description, 0, 24) . '...');
        $truncmydescription = trim(strlen($this->getDescription()) < 25 ? $this->getDescription() :
            substr($this->getdescription(), 0, 24) . '...');
        $this->_stack->push(__FUNCTION__, 'error', array('description' => $truncdescription,
            'self' => $truncmydescription),
            'package.xml 1.0 description "%description%" does not match "%self%"');
    }

    function _missingFile($file)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
            'package.xml 1.0 file "%file%" is not present in <contents>');
    }

    /**
     * WARNING - do not use this function unless you know what you're doing
     */
    function setRawState($state)
    {
        if (!isset($this->_packageInfo['stability'])) {
            $this->_packageInfo['stability'] = array();
        }
        $this->_packageInfo['stability']['release'] = $state;
    }

    /**
     * WARNING - do not use this function unless you know what you're doing
     */
    function setRawCompatible($compatible)
    {
        $this->_packageInfo['compatible'] = $compatible;
    }

    /**
     * WARNING - do not use this function unless you know what you're doing
     */
    function setRawPackage($package)
    {
        $this->_packageInfo['name'] = $package;
    }

    /**
     * WARNING - do not use this function unless you know what you're doing
     */
    function setRawChannel($channel)
    {
        $this->_packageInfo['channel'] = $channel;
    }

    function setRequestedGroup($group)
    {
        $this->_requestedGroup = $group;
    }

    function getRequestedGroup()
    {
        if (isset($this->_requestedGroup)) {
            return $this->_requestedGroup;
        }
        return false;
    }

    /**
     * For saving in the registry.
     *
     * Set the last version that was installed
     * @param string
     */
    function setLastInstalledVersion($version)
    {
        $this->_packageInfo['_lastversion'] = $version;
    }

    /**
     * @return string|false
     */
    function getLastInstalledVersion()
    {
        if (isset($this->_packageInfo['_lastversion'])) {
            return $this->_packageInfo['_lastversion'];
        }
        return false;
    }

    /**
     * Determines whether this package.xml has post-install scripts or not
     * @return array|false
     */
    function listPostinstallScripts()
    {
        $filelist = $this->getFilelist();
        $contents = $this->getContents();
        $contents = $contents['dir']['file'];
        if (!is_array($contents) || !isset($contents[0])) {
            $contents = array($contents);
        }
        $taskfiles = array();
        foreach ($contents as $file) {
            $atts = $file['attribs'];
            unset($file['attribs']);
            if (count($file)) {
                $taskfiles[$atts['name']] = $file;
            }
        }
        $common = new PEAR_Common;
        $common->debug = $this->_config->get('verbose');
        $this->_scripts = array();
        $ret = array();
        foreach ($taskfiles as $name => $tasks) {
            if (!isset($filelist[$name])) {
                // ignored files will not be in the filelist
                continue;
            }
            $atts = $filelist[$name];
            foreach ($tasks as $tag => $raw) {
                $task = $this->getTask($tag);
                $task = &new $task($this->_config, $common, PEAR_TASK_INSTALL);
                if ($task->isScript()) {
                    $ret[] = $filelist[$name]['installed_as'];
                }
            }
        }
        if (count($ret)) {
            return $ret;
        }
        return false;
    }

    /**
     * Initialize post-install scripts for running
     *
     * This method can be used to detect post-install scripts, as the return value
     * indicates whether any exist
     * @return bool
     */
    function initPostinstallScripts()
    {
        $filelist = $this->getFilelist();
        $contents = $this->getContents();
        $contents = $contents['dir']['file'];
        if (!is_array($contents) || !isset($contents[0])) {
            $contents = array($contents);
        }
        $taskfiles = array();
        foreach ($contents as $file) {
            $atts = $file['attribs'];
            unset($file['attribs']);
            if (count($file)) {
                $taskfiles[$atts['name']] = $file;
            }
        }
        $common = new PEAR_Common;
        $common->debug = $this->_config->get('verbose');
        $this->_scripts = array();
        foreach ($taskfiles as $name => $tasks) {
            if (!isset($filelist[$name])) {
                // file was not installed due to installconditions
                continue;
            }
            $atts = $filelist[$name];
            foreach ($tasks as $tag => $raw) {
                $taskname = $this->getTask($tag);
                $task = &new $taskname($this->_config, $common, PEAR_TASK_INSTALL);
                if (!$task->isScript()) {
                    continue; // scripts are only handled after installation
                }
                $lastversion = isset($this->_packageInfo['_lastversion']) ?
                    $this->_packageInfo['_lastversion'] : null;
                $task->init($raw, $atts, $lastversion);
                $res = $task->startSession($this, $atts['installed_as']);
                if (!$res) {
                    continue; // skip this file
                }
                if (PEAR::isError($res)) {
                    return $res;
                }
                $assign = &$task;
                $this->_scripts[] = &$assign;
            }
        }
        if (count($this->_scripts)) {
            return true;
        }
        return false;
    }

    function runPostinstallScripts()
    {
        if ($this->initPostinstallScripts()) {
            $ui = &PEAR_Frontend::singleton();
            if ($ui) {
                $ui->runPostinstallScripts($this->_scripts, $this);
            }
        }
    }


    /**
     * Convert a recursive set of <dir> and <file> tags into a single <dir> tag with
     * <file> tags.
     */
    function flattenFilelist()
    {
        if (isset($this->_packageInfo['bundle'])) {
            return;
        }
        $filelist = array();
        if (isset($this->_packageInfo['contents']['dir']['dir'])) {
            $this->_getFlattenedFilelist($filelist, $this->_packageInfo['contents']['dir']);
            if (!isset($filelist[1])) {
                $filelist = $filelist[0];
            }
            $this->_packageInfo['contents']['dir']['file'] = $filelist;
            unset($this->_packageInfo['contents']['dir']['dir']);
        } else {
            // else already flattened but check for baseinstalldir propagation
            if (isset($this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'])) {
                if (isset($this->_packageInfo['contents']['dir']['file'][0])) {
                    foreach ($this->_packageInfo['contents']['dir']['file'] as $i => $file) {
                        if (isset($file['attribs']['baseinstalldir'])) {
                            continue;
                        }
                        $this->_packageInfo['contents']['dir']['file'][$i]['attribs']['baseinstalldir']
                            = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
                    }
                } else {
                    if (!isset($this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir'])) {
                       $this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir']
                            = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
                    }
                }
            }
        }
    }

    /**
     * @param array the final flattened file list
     * @param array the current directory being processed
     * @param string|false any recursively inherited baeinstalldir attribute
     * @param string private recursion variable
     * @return array
     * @access protected
     */
    function _getFlattenedFilelist(&$files, $dir, $baseinstall = false, $path = '')
    {
        if (isset($dir['attribs']) && isset($dir['attribs']['baseinstalldir'])) {
            $baseinstall = $dir['attribs']['baseinstalldir'];
        }
        if (isset($dir['dir'])) {
            if (!isset($dir['dir'][0])) {
                $dir['dir'] = array($dir['dir']);
            }
            foreach ($dir['dir'] as $subdir) {
                if (!isset($subdir['attribs']) || !isset($subdir['attribs']['name'])) {
                    $name = '*unknown*';
                } else {
                    $name = $subdir['attribs']['name'];
                }
                $newpath = empty($path) ? $name :
                    $path . '/' . $name;
                $this->_getFlattenedFilelist($files, $subdir,
                    $baseinstall, $newpath);
            }
        }
        if (isset($dir['file'])) {
            if (!isset($dir['file'][0])) {
                $dir['file'] = array($dir['file']);
            }
            foreach ($dir['file'] as $file) {
                $attrs = $file['attribs'];
                $name = $attrs['name'];
                if ($baseinstall && !isset($attrs['baseinstalldir'])) {
                    $attrs['baseinstalldir'] = $baseinstall;
                }
                $attrs['name'] = empty($path) ? $name : $path . '/' . $name;
                $attrs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
                    $attrs['name']);
                $file['attribs'] = $attrs;
                $files[] = $file;
            }
        }
    }

    function setConfig(&$config)
    {
        $this->_config = &$config;
        $this->_registry = &$config->getRegistry();
    }

    function setLogger(&$logger)
    {
        if (!is_object($logger) || !method_exists($logger, 'log')) {
            return PEAR::raiseError('Logger must be compatible with PEAR_Common::log');
        }
        $this->_logger = &$logger;
    }

    /**
     * WARNING - do not use this function directly unless you know what you're doing
     */
    function setDeps($deps)
    {
        $this->_packageInfo['dependencies'] = $deps;
    }

    /**
     * WARNING - do not use this function directly unless you know what you're doing
     */
    function setCompatible($compat)
    {
        $this->_packageInfo['compatible'] = $compat;
    }

    function setPackagefile($file, $archive = false)
    {
        $this->_packageFile = $file;
        $this->_archiveFile = $archive ? $archive : $file;
    }

    /**
     * Wrapper to {@link PEAR_ErrorStack::getErrors()}
     * @param boolean determines whether to purge the error stack after retrieving
     * @return array
     */
    function getValidationWarnings($purge = true)
    {
        return $this->_stack->getErrors($purge);
    }

    function getPackageFile()
    {
        return $this->_packageFile;
    }

    function getArchiveFile()
    {
        return $this->_archiveFile;
    }


    /**
     * Directly set the array that defines this packagefile
     *
     * WARNING: no validation.  This should only be performed by internal methods
     * inside PEAR or by inputting an array saved from an existing PEAR_PackageFile_v2
     * @param array
     */
    function fromArray($pinfo)
    {
        unset($pinfo['old']);
        unset($pinfo['xsdversion']);
        $this->_incomplete = false;
        $this->_packageInfo = $pinfo;
    }

    function isIncomplete()
    {
        return $this->_incomplete;
    }

    /**
     * @return array
     */
    function toArray($forreg = false)
    {
        if (!$this->validate(PEAR_VALIDATE_NORMAL)) {
            return false;
        }
        return $this->getArray($forreg);
    }

    function getArray($forReg = false)
    {
        if ($forReg) {
            $arr = $this->_packageInfo;
            $arr['old'] = array();
            $arr['old']['version'] = $this->getVersion();
            $arr['old']['release_date'] = $this->getDate();
            $arr['old']['release_state'] = $this->getState();
            $arr['old']['release_license'] = $this->getLicense();
            $arr['old']['release_notes'] = $this->getNotes();
            $arr['old']['release_deps'] = $this->getDeps();
            $arr['old']['maintainers'] = $this->getMaintainers();
            $arr['xsdversion'] = '2.0';
            return $arr;
        } else {
            $info = $this->_packageInfo;
            unset($info['dirtree']);
            if (isset($info['_lastversion'])) {
                unset($info['_lastversion']);
            }
            if (isset($info['#binarypackage'])) {
                unset($info['#binarypackage']);
            }
            return $info;
        }
    }

    function packageInfo($field)
    {
        $arr = $this->getArray(true);
        if ($field == 'state') {
            return $arr['stability']['release'];
        }
        if ($field == 'api-version') {
            return $arr['version']['api'];
        }
        if ($field == 'api-state') {
            return $arr['stability']['api'];
        }
        if (isset($arr['old'][$field])) {
            if (!is_string($arr['old'][$field])) {
                return null;
            }
            return $arr['old'][$field];
        }
        if (isset($arr[$field])) {
            if (!is_string($arr[$field])) {
                return null;
            }
            return $arr[$field];
        }
        return null;
    }

    function getName()
    {
        return $this->getPackage();
    }

    function getPackage()
    {
        if (isset($this->_packageInfo['name'])) {
            return $this->_packageInfo['name'];
        }
        return false;
    }

    function getChannel()
    {
        if (isset($this->_packageInfo['uri'])) {
            return '__uri';
        }
        if (isset($this->_packageInfo['channel'])) {
            return strtolower($this->_packageInfo['channel']);
        }
        return false;
    }

    function getUri()
    {
        if (isset($this->_packageInfo['uri'])) {
            return $this->_packageInfo['uri'];
        }
        return false;
    }

    function getExtends()
    {
        if (isset($this->_packageInfo['extends'])) {
            return $this->_packageInfo['extends'];
        }
        return false;
    }

    function getSummary()
    {
        if (isset($this->_packageInfo['summary'])) {
            return $this->_packageInfo['summary'];
        }
        return false;
    }

    function getDescription()
    {
        if (isset($this->_packageInfo['description'])) {
            return $this->_packageInfo['description'];
        }
        return false;
    }

    function getMaintainers($raw = false)
    {
        if (!isset($this->_packageInfo['lead'])) {
            return false;
        }
        if ($raw) {
            $ret = array('lead' => $this->_packageInfo['lead']);
            (isset($this->_packageInfo['developer'])) ?
                $ret['developer'] = $this->_packageInfo['developer'] :null;
            (isset($this->_packageInfo['contributor'])) ?
                $ret['contributor'] = $this->_packageInfo['contributor'] :null;
            (isset($this->_packageInfo['helper'])) ?
                $ret['helper'] = $this->_packageInfo['helper'] :null;
            return $ret;
        } else {
            $ret = array();
            $leads = isset($this->_packageInfo['lead'][0]) ? $this->_packageInfo['lead'] :
                array($this->_packageInfo['lead']);
            foreach ($leads as $lead) {
                $s = $lead;
                $s['handle'] = $s['user'];
                unset($s['user']);
                $s['role'] = 'lead';
                $ret[] = $s;
            }
            if (isset($this->_packageInfo['developer'])) {
                $leads = isset($this->_packageInfo['developer'][0]) ?
                    $this->_packageInfo['developer'] :
                    array($this->_packageInfo['developer']);
                foreach ($leads as $maintainer) {
                    $s = $maintainer;
                    $s['handle'] = $s['user'];
                    unset($s['user']);
                    $s['role'] = 'developer';
                    $ret[] = $s;
                }
            }
            if (isset($this->_packageInfo['contributor'])) {
                $leads = isset($this->_packageInfo['contributor'][0]) ?
                    $this->_packageInfo['contributor'] :
                    array($this->_packageInfo['contributor']);
                foreach ($leads as $maintainer) {
                    $s = $maintainer;
                    $s['handle'] = $s['user'];
                    unset($s['user']);
                    $s['role'] = 'contributor';
                    $ret[] = $s;
                }
            }
            if (isset($this->_packageInfo['helper'])) {
                $leads = isset($this->_packageInfo['helper'][0]) ?
                    $this->_packageInfo['helper'] :
                    array($this->_packageInfo['helper']);
                foreach ($leads as $maintainer) {
                    $s = $maintainer;
                    $s['handle'] = $s['user'];
                    unset($s['user']);
                    $s['role'] = 'helper';
                    $ret[] = $s;
                }
            }
            return $ret;
        }
        return false;
    }

    function getLeads()
    {
        if (isset($this->_packageInfo['lead'])) {
            return $this->_packageInfo['lead'];
        }
        return false;
    }

    function getDevelopers()
    {
        if (isset($this->_packageInfo['developer'])) {
            return $this->_packageInfo['developer'];
        }
        return false;
    }

    function getContributors()
    {
        if (isset($this->_packageInfo['contributor'])) {
            return $this->_packageInfo['contributor'];
        }
        return false;
    }

    function getHelpers()
    {
        if (isset($this->_packageInfo['helper'])) {
            return $this->_packageInfo['helper'];
        }
        return false;
    }

    function setDate($date)
    {
        if (!isset($this->_packageInfo['date'])) {
            // ensure that the extends tag is set up in the right location
            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
                array('time', 'version',
                    'stability', 'license', 'notes', 'contents', 'compatible',
                    'dependencies', 'providesextension', 'srcpackage', 'srcuri',
                    'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
                    'zendextbinrelease', 'bundle', 'changelog'), array(), 'date');
        }
        $this->_packageInfo['date'] = $date;
        $this->_isValid = 0;
    }

    function setTime($time)
    {
        $this->_isValid = 0;
        if (!isset($this->_packageInfo['time'])) {
            // ensure that the time tag is set up in the right location
            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
                    array('version',
                    'stability', 'license', 'notes', 'contents', 'compatible',
                    'dependencies', 'providesextension', 'srcpackage', 'srcuri',
                    'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
                    'zendextbinrelease', 'bundle', 'changelog'), $time, 'time');
        }
        $this->_packageInfo['time'] = $time;
    }

    function getDate()
    {
        if (isset($this->_packageInfo['date'])) {
            return $this->_packageInfo['date'];
        }
        return false;
    }

    function getTime()
    {
        if (isset($this->_packageInfo['time'])) {
            return $this->_packageInfo['time'];
        }
        return false;
    }

    /**
     * @param package|api version category to return
     */
    function getVersion($key = 'release')
    {
        if (isset($this->_packageInfo['version'][$key])) {
            return $this->_packageInfo['version'][$key];
        }
        return false;
    }

    function getStability()
    {
        if (isset($this->_packageInfo['stability'])) {
            return $this->_packageInfo['stability'];
        }
        return false;
    }

    function getState($key = 'release')
    {
        if (isset($this->_packageInfo['stability'][$key])) {
            return $this->_packageInfo['stability'][$key];
        }
        return false;
    }

    function getLicense($raw = false)
    {
        if (isset($this->_packageInfo['license'])) {
            if ($raw) {
                return $this->_packageInfo['license'];
            }
            if (is_array($this->_packageInfo['license'])) {
                return $this->_packageInfo['license']['_content'];
            } else {
                return $this->_packageInfo['license'];
            }
        }
        return false;
    }

    function getLicenseLocation()
    {
        if (!isset($this->_packageInfo['license']) || !is_array($this->_packageInfo['license'])) {
            return false;
        }
        return $this->_packageInfo['license']['attribs'];
    }

    function getNotes()
    {
        if (isset($this->_packageInfo['notes'])) {
            return $this->_packageInfo['notes'];
        }
        return false;
    }

    /**
     * Return the <usesrole> tag contents, if any
     * @return array|false
     */
    function getUsesrole()
    {
        if (isset($this->_packageInfo['usesrole'])) {
            return $this->_packageInfo['usesrole'];
        }
        return false;
    }

    /**
     * Return the <usestask> tag contents, if any
     * @return array|false
     */
    function getUsestask()
    {
        if (isset($this->_packageInfo['usestask'])) {
            return $this->_packageInfo['usestask'];
        }
        return false;
    }

    /**
     * This should only be used to retrieve filenames and install attributes
     */
    function getFilelist($preserve = false)
    {
        if (isset($this->_packageInfo['filelist']) && !$preserve) {
            return $this->_packageInfo['filelist'];
        }
        $this->flattenFilelist();
        if ($contents = $this->getContents()) {
            $ret = array();
            if (!isset($contents['dir'])) {
                return false;
            }
            if (!isset($contents['dir']['file'][0])) {
                $contents['dir']['file'] = array($contents['dir']['file']);
            }
            foreach ($contents['dir']['file'] as $file) {
                $name = $file['attribs']['name'];
                if (!$preserve) {
                    $file = $file['attribs'];
                }
                $ret[$name] = $file;
            }
            if (!$preserve) {
                $this->_packageInfo['filelist'] = $ret;
            }
            return $ret;
        }
        return false;
    }

    /**
     * Return configure options array, if any
     *
     * @return array|false
     */
    function getConfigureOptions()
    {
        if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
            return false;
        }
        $releases = $this->getReleases();
        if (isset($releases[0])) {
            $releases = $releases[0];
        }
        if (isset($releases['configureoption'])) {
            if (!isset($releases['configureoption'][0])) {
                $releases['configureoption'] = array($releases['configureoption']);
            }
            for ($i = 0; $i < count($releases['configureoption']); $i++) {
                $releases['configureoption'][$i] = $releases['configureoption'][$i]['attribs'];
            }
            return $releases['configureoption'];
        }
        return false;
    }

    /**
     * This is only used at install-time, after all serialization
     * is over.
     */
    function resetFilelist()
    {
        $this->_packageInfo['filelist'] = array();
    }

    /**
     * Retrieve a list of files that should be installed on this computer
     * @return array
     */
    function getInstallationFilelist($forfilecheck = false)
    {
        $contents = $this->getFilelist(true);
        if (isset($contents['dir']['attribs']['baseinstalldir'])) {
            $base = $contents['dir']['attribs']['baseinstalldir'];
        }
        if (isset($this->_packageInfo['bundle'])) {
            return PEAR::raiseError(
                'Exception: bundles should be handled in download code only');
        }
        $release = $this->getReleases();
        if ($release) {
            if (!isset($release[0])) {
                if (!isset($release['installconditions']) && !isset($release['filelist'])) {
                    if ($forfilecheck) {
                        return $this->getFilelist();
                    }
                    return $contents;
                }
                $release = array($release);
            }
            $depchecker = &$this->getPEARDependency2($this->_config, array(),
                array('channel' => $this->getChannel(), 'package' => $this->getPackage()),
                PEAR_VALIDATE_INSTALLING);
            foreach ($release as $instance) {
                if (isset($instance['installconditions'])) {
                    $installconditions = $instance['installconditions'];
                    if (is_array($installconditions)) {
                        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                        foreach ($installconditions as $type => $conditions) {
                            if (!isset($conditions[0])) {
                                $conditions = array($conditions);
                            }
                            foreach ($conditions as $condition) {
                                $ret = $depchecker->{"validate{$type}Dependency"}($condition);
                                if (PEAR::isError($ret)) {
                                    PEAR::popErrorHandling();
                                    continue 3; // skip this release
                                }
                            }
                        }
                        PEAR::popErrorHandling();
                    }
                }
                // this is the release to use
                if (isset($instance['filelist'])) {
                    // ignore files
                    if (isset($instance['filelist']['ignore'])) {
                        $ignore = isset($instance['filelist']['ignore'][0]) ?
                            $instance['filelist']['ignore'] :
                            array($instance['filelist']['ignore']);
                        foreach ($ignore as $ig) {
                            unset ($contents[$ig['attribs']['name']]);
                        }
                    }
                    // install files as this name
                    if (isset($instance['filelist']['install'])) {
                        $installas = isset($instance['filelist']['install'][0]) ?
                            $instance['filelist']['install'] :
                            array($instance['filelist']['install']);
                        foreach ($installas as $as) {
                            $contents[$as['attribs']['name']]['attribs']['install-as'] =
                                $as['attribs']['as'];
                        }
                    }
                }
                if ($forfilecheck) {
                    foreach ($contents as $file => $attrs) {
                        $contents[$file] = $attrs['attribs'];
                    }
                }
                return $contents;
            }
        } else { // simple release - no installconditions or install-as
            if ($forfilecheck) {
                return $this->getFilelist();
            }
            return $contents;
        }
        // no releases matched
        return PEAR::raiseError('No releases in package.xml matched the existing operating ' .
            'system, extensions installed, or architecture, cannot install');
    }

    /**
     * This is only used at install-time, after all serialization
     * is over.
     * @param string file name
     * @param string installed path
     */
    function setInstalledAs($file, $path)
    {
        if ($path) {
            return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
        }
        unset($this->_packageInfo['filelist'][$file]['installed_as']);
    }

    function getInstalledLocation($file)
    {
        if (isset($this->_packageInfo['filelist'][$file]['installed_as'])) {
            return $this->_packageInfo['filelist'][$file]['installed_as'];
        }
        return false;
    }

    /**
     * This is only used at install-time, after all serialization
     * is over.
     */
    function installedFile($file, $atts)
    {
        if (isset($this->_packageInfo['filelist'][$file])) {
            $this->_packageInfo['filelist'][$file] =
                array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']);
        } else {
            $this->_packageInfo['filelist'][$file] = $atts['attribs'];
        }
    }

    /**
     * Retrieve the contents tag
     */
    function getContents()
    {
        if (isset($this->_packageInfo['contents'])) {
            return $this->_packageInfo['contents'];
        }
        return false;
    }

    /**
     * @param string full path to file
     * @param string attribute name
     * @param string attribute value
     * @param int risky but fast - use this to choose a file based on its position in the list
     *            of files.  Index is zero-based like PHP arrays.
     * @return bool success of operation
     */
    function setFileAttribute($filename, $attr, $value, $index = false)
    {
        $this->_isValid = 0;
        if (in_array($attr, array('role', 'name', 'baseinstalldir'))) {
            $this->_filesValid = false;
        }
        if ($index !== false &&
              isset($this->_packageInfo['contents']['dir']['file'][$index]['attribs'])) {
            $this->_packageInfo['contents']['dir']['file'][$index]['attribs'][$attr] = $value;
            return true;
        }
        if (!isset($this->_packageInfo['contents']['dir']['file'])) {
            return false;
        }
        $files = $this->_packageInfo['contents']['dir']['file'];
        if (!isset($files[0])) {
            $files = array($files);
            $ind = false;
        } else {
            $ind = true;
        }
        foreach ($files as $i => $file) {
            if (isset($file['attribs'])) {
                if ($file['attribs']['name'] == $filename) {
                    if ($ind) {
                        $this->_packageInfo['contents']['dir']['file'][$i]['attribs'][$attr] = $value;
                    } else {
                        $this->_packageInfo['contents']['dir']['file']['attribs'][$attr] = $value;
                    }
                    return true;
                }
            }
        }
        return false;
    }

    function setDirtree($path)
    {
        if (!isset($this->_packageInfo['dirtree'])) {
            $this->_packageInfo['dirtree'] = array();
        }
        $this->_packageInfo['dirtree'][$path] = true;
    }

    function getDirtree()
    {
        if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) {
            return $this->_packageInfo['dirtree'];
        }
        return false;
    }

    function resetDirtree()
    {
        unset($this->_packageInfo['dirtree']);
    }

    /**
     * Determines whether this package claims it is compatible with the version of
     * the package that has a recommended version dependency
     * @param PEAR_PackageFile_v2|PEAR_PackageFile_v1|PEAR_Downloader_Package
     * @return boolean
     */
    function isCompatible($pf)
    {
        if (!isset($this->_packageInfo['compatible'])) {
            return false;
        }
        if (!isset($this->_packageInfo['channel'])) {
            return false;
        }
        $me = $pf->getVersion();
        $compatible = $this->_packageInfo['compatible'];
        if (!isset($compatible[0])) {
            $compatible = array($compatible);
        }
        $found = false;
        foreach ($compatible as $info) {
            if (strtolower($info['name']) == strtolower($pf->getPackage())) {
                if (strtolower($info['channel']) == strtolower($pf->getChannel())) {
                    $found = true;
                    break;
                }
            }
        }
        if (!$found) {
            return false;
        }
        if (isset($info['exclude'])) {
            if (!isset($info['exclude'][0])) {
                $info['exclude'] = array($info['exclude']);
            }
            foreach ($info['exclude'] as $exclude) {
                if (version_compare($me, $exclude, '==')) {
                    return false;
                }
            }
        }
        if (version_compare($me, $info['min'], '>=') && version_compare($me, $info['max'], '<=')) {
            return true;
        }
        return false;
    }

    /**
     * @return array|false
     */
    function getCompatible()
    {
        if (isset($this->_packageInfo['compatible'])) {
            return $this->_packageInfo['compatible'];
        }
        return false;
    }

    function getDependencies()
    {
        if (isset($this->_packageInfo['dependencies'])) {
            return $this->_packageInfo['dependencies'];
        }
        return false;
    }

    function isSubpackageOf($p)
    {
        return $p->isSubpackage($this);
    }

    /**
     * Determines whether the passed in package is a subpackage of this package.
     *
     * No version checking is done, only name verification.
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @return bool
     */
    function isSubpackage($p)
    {
        $sub = array();
        if (isset($this->_packageInfo['dependencies']['required']['subpackage'])) {
            $sub = $this->_packageInfo['dependencies']['required']['subpackage'];
            if (!isset($sub[0])) {
                $sub = array($sub);
            }
        }
        if (isset($this->_packageInfo['dependencies']['optional']['subpackage'])) {
            $sub1 = $this->_packageInfo['dependencies']['optional']['subpackage'];
            if (!isset($sub1[0])) {
                $sub1 = array($sub1);
            }
            $sub = array_merge($sub, $sub1);
        }
        if (isset($this->_packageInfo['dependencies']['group'])) {
            $group = $this->_packageInfo['dependencies']['group'];
            if (!isset($group[0])) {
                $group = array($group);
            }
            foreach ($group as $deps) {
                if (isset($deps['subpackage'])) {
                    $sub2 = $deps['subpackage'];
                    if (!isset($sub2[0])) {
                        $sub2 = array($sub2);
                    }
                    $sub = array_merge($sub, $sub2);
                }
            }
        }
        foreach ($sub as $dep) {
            if (strtolower($dep['name']) == strtolower($p->getPackage())) {
                if (isset($dep['channel'])) {
                    if (strtolower($dep['channel']) == strtolower($p->getChannel())) {
                        return true;
                    }
                } else {
                    if ($dep['uri'] == $p->getURI()) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    function dependsOn($package, $channel)
    {
        if (!($deps = $this->getDependencies())) {
            return false;
        }
        foreach (array('package', 'subpackage') as $type) {
            foreach (array('required', 'optional') as $needed) {
                if (isset($deps[$needed][$type])) {
                    if (!isset($deps[$needed][$type][0])) {
                        $deps[$needed][$type] = array($deps[$needed][$type]);
                    }
                    foreach ($deps[$needed][$type] as $dep) {
                        $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
                        if (strtolower($dep['name']) == strtolower($package) &&
                              $depchannel == $channel) {
                            return true;
                        }
                    }
                }
            }
            if (isset($deps['group'])) {
                if (!isset($deps['group'][0])) {
                    $dep['group'] = array($deps['group']);
                }
                foreach ($deps['group'] as $group) {
                    if (isset($group[$type])) {
                        if (!is_array($group[$type])) {
                            $group[$type] = array($group[$type]);
                        }
                        foreach ($group[$type] as $dep) {
                            $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
                            if (strtolower($dep['name']) == strtolower($package) &&
                                  $depchannel == $channel) {
                                return true;
                            }
                        }
                    }
                }
            }
        }
        return false;
    }

    /**
     * Get the contents of a dependency group
     * @param string
     * @return array|false
     */
    function getDependencyGroup($name)
    {
        $name = strtolower($name);
        if (!isset($this->_packageInfo['dependencies']['group'])) {
            return false;
        }
        $groups = $this->_packageInfo['dependencies']['group'];
        if (!isset($groups[0])) {
            $groups = array($groups);
        }
        foreach ($groups as $group) {
            if (strtolower($group['attribs']['name']) == $name) {
                return $group;
            }
        }
        return false;
    }

    /**
     * Retrieve a partial package.xml 1.0 representation of dependencies
     *
     * a very limited representation of dependencies is returned by this method.
     * The <exclude> tag for excluding certain versions of a dependency is
     * completely ignored.  In addition, dependency groups are ignored, with the
     * assumption that all dependencies in dependency groups are also listed in
     * the optional group that work with all dependency groups
     * @param boolean return package.xml 2.0 <dependencies> tag
     * @return array|false
     */
    function getDeps($raw = false, $nopearinstaller = false)
    {
        if (isset($this->_packageInfo['dependencies'])) {
            if ($raw) {
                return $this->_packageInfo['dependencies'];
            }
            $ret = array();
            $map = array(
                'php' => 'php',
                'package' => 'pkg',
                'subpackage' => 'pkg',
                'extension' => 'ext',
                'os' => 'os',
                'pearinstaller' => 'pkg',
                );
            foreach (array('required', 'optional') as $type) {
                $optional = ($type == 'optional') ? 'yes' : 'no';
                if (!isset($this->_packageInfo['dependencies'][$type])
                    || empty($this->_packageInfo['dependencies'][$type])) {
                    continue;
                }
                foreach ($this->_packageInfo['dependencies'][$type] as $dtype => $deps) {
                    if ($dtype == 'pearinstaller' && $nopearinstaller) {
                        continue;
                    }
                    if (!isset($deps[0])) {
                        $deps = array($deps);
                    }
                    foreach ($deps as $dep) {
                        if (!isset($map[$dtype])) {
                            // no support for arch type
                            continue;
                        }
                        if ($dtype == 'pearinstaller') {
                            $dep['name'] = 'PEAR';
                            $dep['channel'] = 'pear.php.net';
                        }
                        $s = array('type' => $map[$dtype]);
                        if (isset($dep['channel'])) {
                            $s['channel'] = $dep['channel'];
                        }
                        if (isset($dep['uri'])) {
                            $s['uri'] = $dep['uri'];
                        }
                        if (isset($dep['name'])) {
                            $s['name'] = $dep['name'];
                        }
                        if (isset($dep['conflicts'])) {
                            $s['rel'] = 'not';
                        } else {
                            if (!isset($dep['min']) &&
                                  !isset($dep['max'])) {
                                $s['rel'] = 'has';
                                $s['optional'] = $optional;
                            } elseif (isset($dep['min']) &&
                                  isset($dep['max'])) {
                                $s['rel'] = 'ge';
                                $s1 = $s;
                                $s1['rel'] = 'le';
                                $s['version'] = $dep['min'];
                                $s1['version'] = $dep['max'];
                                if (isset($dep['channel'])) {
                                    $s1['channel'] = $dep['channel'];
                                }
                                if ($dtype != 'php') {
                                    $s['name'] = $dep['name'];
                                    $s1['name'] = $dep['name'];
                                }
                                $s['optional'] = $optional;
                                $s1['optional'] = $optional;
                                $ret[] = $s1;
                            } elseif (isset($dep['min'])) {
                                if (isset($dep['exclude']) &&
                                      $dep['exclude'] == $dep['min']) {
                                    $s['rel'] = 'gt';
                                } else {
                                    $s['rel'] = 'ge';
                                }
                                $s['version'] = $dep['min'];
                                $s['optional'] = $optional;
                                if ($dtype != 'php') {
                                    $s['name'] = $dep['name'];
                                }
                            } elseif (isset($dep['max'])) {
                                if (isset($dep['exclude']) &&
                                      $dep['exclude'] == $dep['max']) {
                                    $s['rel'] = 'lt';
                                } else {
                                    $s['rel'] = 'le';
                                }
                                $s['version'] = $dep['max'];
                                $s['optional'] = $optional;
                                if ($dtype != 'php') {
                                    $s['name'] = $dep['name'];
                                }
                            }
                        }
                        $ret[] = $s;
                    }
                }
            }
            if (count($ret)) {
                return $ret;
            }
        }
        return false;
    }

    /**
     * @return php|extsrc|extbin|zendextsrc|zendextbin|bundle|false
     */
    function getPackageType()
    {
        if (isset($this->_packageInfo['phprelease'])) {
            return 'php';
        }
        if (isset($this->_packageInfo['extsrcrelease'])) {
            return 'extsrc';
        }
        if (isset($this->_packageInfo['extbinrelease'])) {
            return 'extbin';
        }
        if (isset($this->_packageInfo['zendextsrcrelease'])) {
            return 'zendextsrc';
        }
        if (isset($this->_packageInfo['zendextbinrelease'])) {
            return 'zendextbin';
        }
        if (isset($this->_packageInfo['bundle'])) {
            return 'bundle';
        }
        return false;
    }

    /**
     * @return array|false
     */
    function getReleases()
    {
        $type = $this->getPackageType();
        if ($type != 'bundle') {
            $type .= 'release';
        }
        if ($this->getPackageType() && isset($this->_packageInfo[$type])) {
            return $this->_packageInfo[$type];
        }
        return false;
    }

    /**
     * @return array
     */
    function getChangelog()
    {
        if (isset($this->_packageInfo['changelog'])) {
            return $this->_packageInfo['changelog'];
        }
        return false;
    }

    function hasDeps()
    {
        return isset($this->_packageInfo['dependencies']);
    }

    function getPackagexmlVersion()
    {
        if (isset($this->_packageInfo['zendextsrcrelease'])) {
            return '2.1';
        }
        if (isset($this->_packageInfo['zendextbinrelease'])) {
            return '2.1';
        }
        return '2.0';
    }

    /**
     * @return array|false
     */
    function getSourcePackage()
    {
        if (isset($this->_packageInfo['extbinrelease']) ||
              isset($this->_packageInfo['zendextbinrelease'])) {
            return array('channel' => $this->_packageInfo['srcchannel'],
                         'package' => $this->_packageInfo['srcpackage']);
        }
        return false;
    }

    function getBundledPackages()
    {
        if (isset($this->_packageInfo['bundle'])) {
            return $this->_packageInfo['contents']['bundledpackage'];
        }
        return false;
    }

    function getLastModified()
    {
        if (isset($this->_packageInfo['_lastmodified'])) {
            return $this->_packageInfo['_lastmodified'];
        }
        return false;
    }

    /**
     * Get the contents of a file listed within the package.xml
     * @param string
     * @return string
     */
    function getFileContents($file)
    {
        if ($this->_archiveFile == $this->_packageFile) { // unpacked
            $dir = dirname($this->_packageFile);
            $file = $dir . DIRECTORY_SEPARATOR . $file;
            $file = str_replace(array('/', '\\'),
                array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file);
            if (file_exists($file) && is_readable($file)) {
                return implode('', file($file));
            }
        } else { // tgz
            $tar = &new Archive_Tar($this->_archiveFile);
            $tar->pushErrorHandling(PEAR_ERROR_RETURN);
            if ($file != 'package.xml' && $file != 'package2.xml') {
                $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file;
            }
            $file = $tar->extractInString($file);
            $tar->popErrorHandling();
            if (PEAR::isError($file)) {
                return PEAR::raiseError("Cannot locate file '$file' in archive");
            }
            return $file;
        }
    }

    function &getRW()
    {
        if (!class_exists('PEAR_PackageFile_v2_rw')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2/rw.php';
        }
        $a = new PEAR_PackageFile_v2_rw;
        foreach (get_object_vars($this) as $name => $unused) {
            if (!isset($this->$name)) {
                continue;
            }
            if ($name == '_config' || $name == '_logger'|| $name == '_registry' ||
                  $name == '_stack') {
                $a->$name = &$this->$name;
            } else {
                $a->$name = $this->$name;
            }
        }
        return $a;
    }

    function &getDefaultGenerator()
    {
        if (!class_exists('PEAR_PackageFile_Generator_v2')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/Generator/v2.php';
        }
        $a = &new PEAR_PackageFile_Generator_v2($this);
        return $a;
    }

    function analyzeSourceCode($file, $string = false)
    {
        if (!isset($this->_v2Validator) ||
              !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) {
            if (!class_exists('PEAR_PackageFile_v2_Validator')) {
                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2/Validator.php';
            }
            $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
        }
        return $this->_v2Validator->analyzeSourceCode($file, $string);
    }

    function validate($state = PEAR_VALIDATE_NORMAL)
    {
        if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) {
            return false;
        }
        if (!isset($this->_v2Validator) ||
              !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) {
            if (!class_exists('PEAR_PackageFile_v2_Validator')) {
                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2/Validator.php';
            }
            $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
        }
        if (isset($this->_packageInfo['xsdversion'])) {
            unset($this->_packageInfo['xsdversion']);
        }
        return $this->_v2Validator->validate($this, $state);
    }

    function getTasksNs()
    {
        if (!isset($this->_tasksNs)) {
            if (isset($this->_packageInfo['attribs'])) {
                foreach ($this->_packageInfo['attribs'] as $name => $value) {
                    if ($value == 'http://pear.php.net/dtd/tasks-1.0') {
                        $this->_tasksNs = str_replace('xmlns:', '', $name);
                        break;
                    }
                }
            }
        }
        return $this->_tasksNs;
    }

    /**
     * Determine whether a task name is a valid task.  Custom tasks may be defined
     * using subdirectories by putting a "-" in the name, as in <tasks:mycustom-task>
     *
     * Note that this method will auto-load the task class file and test for the existence
     * of the name with "-" replaced by "_" as in PEAR/Task/mycustom/task.php makes class
     * PEAR_Task_mycustom_task
     * @param string
     * @return boolean
     */
    function getTask($task)
    {
        $this->getTasksNs();
        // transform all '-' to '/' and 'tasks:' to '' so tasks:replace becomes replace
        $task = str_replace(array($this->_tasksNs . ':', '-'), array('', ' '), $task);
        $taskfile = str_replace(' ', '/', ucwords($task));
        $task = str_replace(array(' ', '/'), '_', ucwords($task));
        if (class_exists("PEAR_Task_$task")) {
            return "PEAR_Task_$task";
        }
        $fp = @fopen("phar://install-pear-nozlib.phar/PEAR/Task/$taskfile.php", 'r', true);
        if ($fp) {
            fclose($fp);
            require_once  'phar://install-pear-nozlib.phar/' . "PEAR/Task/$taskfile.php";
            return "PEAR_Task_$task";
        }
        return false;
    }

    /**
     * Key-friendly array_splice
     * @param tagname to splice a value in before
     * @param mixed the value to splice in
     * @param string the new tag name
     */
    function _ksplice($array, $key, $value, $newkey)
    {
        $offset = array_search($key, array_keys($array));
        $after = array_slice($array, $offset);
        $before = array_slice($array, 0, $offset);
        $before[$newkey] = $value;
        return array_merge($before, $after);
    }

    /**
     * @param array a list of possible keys, in the order they may occur
     * @param mixed contents of the new package.xml tag
     * @param string tag name
     * @access private
     */
    function _insertBefore($array, $keys, $contents, $newkey)
    {
        foreach ($keys as $key) {
            if (isset($array[$key])) {
                return $array = $this->_ksplice($array, $key, $contents, $newkey);
            }
        }
        $array[$newkey] = $contents;
        return $array;
    }

    /**
     * @param subsection of {@link $_packageInfo}
     * @param array|string tag contents
     * @param array format:
     * <pre>
     * array(
     *   tagname => array(list of tag names that follow this one),
     *   childtagname => array(list of child tag names that follow this one),
     * )
     * </pre>
     *
     * This allows construction of nested tags
     * @access private
     */
    function _mergeTag($manip, $contents, $order)
    {
        if (count($order)) {
            foreach ($order as $tag => $curorder) {
                if (!isset($manip[$tag])) {
                    // ensure that the tag is set up
                    $manip = $this->_insertBefore($manip, $curorder, array(), $tag);
                }
                if (count($order) > 1) {
                    $manip[$tag] = $this->_mergeTag($manip[$tag], $contents, array_slice($order, 1));
                    return $manip;
                }
            }
        } else {
            return $manip;
        }
        if (is_array($manip[$tag]) && !empty($manip[$tag]) && isset($manip[$tag][0])) {
            $manip[$tag][] = $contents;
        } else {
            if (!count($manip[$tag])) {
                $manip[$tag] = $contents;
            } else {
                $manip[$tag] = array($manip[$tag]);
                $manip[$tag][] = $contents;
            }
        }
        return $manip;
    }
}
?>
<?php
/**
 * PEAR_PackageFile_v2, package.xml version 2.0, read/write version
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Validator.php,v 1.106 2008/03/28 22:23:41 dufuz Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a8
 */
/**
 * Private validation class used by PEAR_PackageFile_v2 - do not use directly, its
 * sole purpose is to split up the PEAR/PackageFile/v2.php file to make it smaller
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a8
 * @access private
 */
class PEAR_PackageFile_v2_Validator
{
    /**
     * @var array
     */
    var $_packageInfo;
    /**
     * @var PEAR_PackageFile_v2
     */
    var $_pf;
    /**
     * @var PEAR_ErrorStack
     */
    var $_stack;
    /**
     * @var int
     */
    var $_isValid = 0;
    /**
     * @var int
     */
    var $_filesValid = 0;
    /**
     * @var int
     */
    var $_curState = 0;
    /**
     * @param PEAR_PackageFile_v2
     * @param int
     */
    function validate(&$pf, $state = PEAR_VALIDATE_NORMAL)
    {
        $this->_pf = &$pf;
        $this->_curState = $state;
        $this->_packageInfo = $this->_pf->getArray();
        $this->_isValid = $this->_pf->_isValid;
        $this->_filesValid = $this->_pf->_filesValid;
        $this->_stack = &$pf->_stack;
        $this->_stack->getErrors(true);
        if (($this->_isValid & $state) == $state) {
            return true;
        }
        if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) {
            return false;
        }
        if (!isset($this->_packageInfo['attribs']['version']) ||
              ($this->_packageInfo['attribs']['version'] != '2.0' &&
               $this->_packageInfo['attribs']['version'] != '2.1')
        ) {
            $this->_noPackageVersion();
        }
        $structure =
        array(
            'name',
            'channel|uri',
            '*extends', // can't be multiple, but this works fine
            'summary',
            'description',
            '+lead', // these all need content checks
            '*developer',
            '*contributor',
            '*helper',
            'date',
            '*time',
            'version',
            'stability',
            'license->?uri->?filesource',
            'notes',
            'contents', //special validation needed
            '*compatible',
            'dependencies', //special validation needed
            '*usesrole',
            '*usestask', // reserve these for 1.4.0a1 to implement
                         // this will allow a package.xml to gracefully say it
                         // needs a certain package installed in order to implement a role or task
            '*providesextension',
            '*srcpackage|*srcuri',
            '+phprelease|+extsrcrelease|+extbinrelease|' .
                '+zendextsrcrelease|+zendextbinrelease|bundle', //special validation needed
            '*changelog',
        );
        $test = $this->_packageInfo;
        if (isset($test['dependencies']) &&
              isset($test['dependencies']['required']) &&
              isset($test['dependencies']['required']['pearinstaller']) &&
              isset($test['dependencies']['required']['pearinstaller']['min']) &&
              version_compare('1.7.2',
                $test['dependencies']['required']['pearinstaller']['min'], '<')
        ) {
            $this->_pearVersionTooLow($test['dependencies']['required']['pearinstaller']['min']);
            return false;
        }
        // ignore post-installation array fields
        if (array_key_exists('filelist', $test)) {
            unset($test['filelist']);
        }
        if (array_key_exists('_lastmodified', $test)) {
            unset($test['_lastmodified']);
        }
        if (array_key_exists('#binarypackage', $test)) {
            unset($test['#binarypackage']);
        }
        if (array_key_exists('old', $test)) {
            unset($test['old']);
        }
        if (array_key_exists('_lastversion', $test)) {
            unset($test['_lastversion']);
        }
        if (!$this->_stupidSchemaValidate($structure, $test, '<package>')) {
            return false;
        }
        if (empty($this->_packageInfo['name'])) {
            $this->_tagCannotBeEmpty('name');
        }
        $test = isset($this->_packageInfo['uri']) ? 'uri' :'channel';
        if (empty($this->_packageInfo[$test])) {
            $this->_tagCannotBeEmpty($test);
        }
        if (is_array($this->_packageInfo['license']) &&
              (!isset($this->_packageInfo['license']['_content']) ||
              empty($this->_packageInfo['license']['_content']))) {
            $this->_tagCannotBeEmpty('license');
        } elseif (empty($this->_packageInfo['license'])) {
            $this->_tagCannotBeEmpty('license');
        }
        if (empty($this->_packageInfo['summary'])) {
            $this->_tagCannotBeEmpty('summary');
        }
        if (empty($this->_packageInfo['description'])) {
            $this->_tagCannotBeEmpty('description');
        }
        if (empty($this->_packageInfo['date'])) {
            $this->_tagCannotBeEmpty('date');
        }
        if (empty($this->_packageInfo['notes'])) {
            $this->_tagCannotBeEmpty('notes');
        }
        if (isset($this->_packageInfo['time']) && empty($this->_packageInfo['time'])) {
            $this->_tagCannotBeEmpty('time');
        }
        if (isset($this->_packageInfo['dependencies'])) {
            $this->_validateDependencies();
        }
        if (isset($this->_packageInfo['compatible'])) {
            $this->_validateCompatible();
        }
        if (!isset($this->_packageInfo['bundle'])) {
            if (empty($this->_packageInfo['contents'])) {
                $this->_tagCannotBeEmpty('contents');
            }
            if (!isset($this->_packageInfo['contents']['dir'])) {
                $this->_filelistMustContainDir('contents');
                return false;
            }
            if (isset($this->_packageInfo['contents']['file'])) {
                $this->_filelistCannotContainFile('contents');
                return false;
            }
        }
        $this->_validateMaintainers();
        $this->_validateStabilityVersion();
        $fail = false;
        if (array_key_exists('usesrole', $this->_packageInfo)) {
            $roles = $this->_packageInfo['usesrole'];
            if (!is_array($roles) || !isset($roles[0])) {
                $roles = array($roles);
            }
            foreach ($roles as $role) {
                if (!isset($role['role'])) {
                    $this->_usesroletaskMustHaveRoleTask('usesrole', 'role');
                    $fail = true;
                } else {
                    if (!isset($role['channel'])) {
                        if (!isset($role['uri'])) {
                            $this->_usesroletaskMustHaveChannelOrUri($role['role'], 'usesrole');
                            $fail = true;
                        }
                    } elseif (!isset($role['package'])) {
                        $this->_usesroletaskMustHavePackage($role['role'], 'usesrole');
                        $fail = true;
                    }
                }
            }
        }
        if (array_key_exists('usestask', $this->_packageInfo)) {
            $roles = $this->_packageInfo['usestask'];
            if (!is_array($roles) || !isset($roles[0])) {
                $roles = array($roles);
            }
            foreach ($roles as $role) {
                if (!isset($role['task'])) {
                    $this->_usesroletaskMustHaveRoleTask('usestask', 'task');
                    $fail = true;
                } else {
                    if (!isset($role['channel'])) {
                        if (!isset($role['uri'])) {
                            $this->_usesroletaskMustHaveChannelOrUri($role['task'], 'usestask');
                            $fail = true;
                        }
                    } elseif (!isset($role['package'])) {
                        $this->_usesroletaskMustHavePackage($role['task'], 'usestask');
                        $fail = true;
                    }
                }
            }
        }
        if ($fail) {
            return false;
        }
        $list = $this->_packageInfo['contents'];
        if (isset($list['dir']) && is_array($list['dir']) && isset($list['dir'][0])) {
            $this->_multipleToplevelDirNotAllowed();
            return $this->_isValid = 0;
        }
        $this->_validateFilelist();
        $this->_validateRelease();
        if (!$this->_stack->hasErrors()) {
            $chan = $this->_pf->_registry->getChannel($this->_pf->getChannel(), true);
            if (PEAR::isError($chan)) {
                $this->_unknownChannel($this->_pf->getChannel());
            } else {
                $valpack = $chan->getValidationPackage();
                // for channel validator packages, always use the default PEAR validator.
                // otherwise, they can't be installed or packaged
                $validator = $chan->getValidationObject($this->_pf->getPackage());
                if (!$validator) {
                    $this->_stack->push(__FUNCTION__, 'error',
                        array_merge(
                            array('channel' => $chan->getName(),
                                  'package' => $this->_pf->getPackage()),
                              $valpack
                            ),
                        'package "%channel%/%package%" cannot be properly validated without ' .
                        'validation package "%channel%/%name%-%version%"');
                    return $this->_isValid = 0;
                }
                $validator->setPackageFile($this->_pf);
                $validator->validate($state);
                $failures = $validator->getFailures();
                foreach ($failures['errors'] as $error) {
                    $this->_stack->push(__FUNCTION__, 'error', $error,
                        'Channel validator error: field "%field%" - %reason%');
                }
                foreach ($failures['warnings'] as $warning) {
                    $this->_stack->push(__FUNCTION__, 'warning', $warning,
                        'Channel validator warning: field "%field%" - %reason%');
                }
            }
        }
        $this->_pf->_isValid = $this->_isValid = !$this->_stack->hasErrors('error');
        if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$this->_filesValid) {
            if ($this->_pf->getPackageType() == 'bundle') {
                if ($this->_analyzeBundledPackages()) {
                    $this->_filesValid = $this->_pf->_filesValid = true;
                } else {
                    $this->_pf->_isValid = $this->_isValid = 0;
                }
            } else {
                if (!$this->_analyzePhpFiles()) {
                    $this->_pf->_isValid = $this->_isValid = 0;
                } else {
                    $this->_filesValid = $this->_pf->_filesValid = true;
                }
            }
        }
        if ($this->_isValid) {
            return $this->_pf->_isValid = $this->_isValid = $state;
        }
        return $this->_pf->_isValid = $this->_isValid = 0;
    }

    function _stupidSchemaValidate($structure, $xml, $root)
    {
        if (!is_array($xml)) {
            $xml = array();
        }
        $keys = array_keys($xml);
        reset($keys);
        $key = current($keys);
        while ($key == 'attribs' || $key == '_contents') {
            $key = next($keys);
        }
        $unfoundtags = $optionaltags = array();
        $ret = true;
        $mismatch = false;
        foreach ($structure as $struc) {
            if ($key) {
                $tag = $xml[$key];
            }
            $test = $this->_processStructure($struc);
            if (isset($test['choices'])) {
                $loose = true;
                foreach ($test['choices'] as $choice) {
                    if ($key == $choice['tag']) {
                        $key = next($keys);
                        while ($key == 'attribs' || $key == '_contents') {
                            $key = next($keys);
                        }
                        $unfoundtags = $optionaltags = array();
                        $mismatch = false;
                        if ($key && $key != $choice['tag'] && isset($choice['multiple'])) {
                            $unfoundtags[] = $choice['tag'];
                            $optionaltags[] = $choice['tag'];
                            if ($key) {
                                $mismatch = true;
                            }
                        }
                        $ret &= $this->_processAttribs($choice, $tag, $root);
                        continue 2;
                    } else {
                        $unfoundtags[] = $choice['tag'];
                        $mismatch = true;
                    }
                    if (!isset($choice['multiple']) || $choice['multiple'] != '*') {
                        $loose = false;
                    } else {
                        $optionaltags[] = $choice['tag'];
                    }
                }
                if (!$loose) {
                    $this->_invalidTagOrder($unfoundtags, $key, $root);
                    return false;
                }
            } else {
                if ($key != $test['tag']) {
                    if (isset($test['multiple']) && $test['multiple'] != '*') {
                        $unfoundtags[] = $test['tag'];
                        $this->_invalidTagOrder($unfoundtags, $key, $root);
                        return false;
                    } else {
                        if ($key) {
                            $mismatch = true;
                        }
                        $unfoundtags[] = $test['tag'];
                        $optionaltags[] = $test['tag'];
                    }
                    if (!isset($test['multiple'])) {
                        $this->_invalidTagOrder($unfoundtags, $key, $root);
                        return false;
                    }
                    continue;
                } else {
                    $unfoundtags = $optionaltags = array();
                    $mismatch = false;
                }
                $key = next($keys);
                while ($key == 'attribs' || $key == '_contents') {
                    $key = next($keys);
                }
                if ($key && $key != $test['tag'] && isset($test['multiple'])) {
                    $unfoundtags[] = $test['tag'];
                    $optionaltags[] = $test['tag'];
                    $mismatch = true;
                }
                $ret &= $this->_processAttribs($test, $tag, $root);
                continue;
            }
        }
        if (!$mismatch && count($optionaltags)) {
            // don't error out on any optional tags
            $unfoundtags = array_diff($unfoundtags, $optionaltags);
        }
        if (count($unfoundtags)) {
            $this->_invalidTagOrder($unfoundtags, $key, $root);
        } elseif ($key) {
            // unknown tags
            $this->_invalidTagOrder('*no tags allowed here*', $key, $root);
            while ($key = next($keys)) {
                $this->_invalidTagOrder('*no tags allowed here*', $key, $root);
            }
        }
        return $ret;
    }

    function _processAttribs($choice, $tag, $context)
    {
        if (isset($choice['attribs'])) {
            if (!is_array($tag)) {
                $tag = array($tag);
            }
            $tags = $tag;
            if (!isset($tags[0])) {
                $tags = array($tags);
            }
            $ret = true;
            foreach ($tags as $i => $tag) {
                if (!is_array($tag) || !isset($tag['attribs'])) {
                    foreach ($choice['attribs'] as $attrib) {
                        if ($attrib{0} != '?') {
                            $ret &= $this->_tagHasNoAttribs($choice['tag'],
                                $context);
                            continue 2;
                        }
                    }
                }
                foreach ($choice['attribs'] as $attrib) {
                    if ($attrib{0} != '?') {
                        if (!isset($tag['attribs'][$attrib])) {
                            $ret &= $this->_tagMissingAttribute($choice['tag'],
                                $attrib, $context);
                        }
                    }
                }
            }
            return $ret;
        }
        return true;
    }

    function _processStructure($key)
    {
        $ret = array();
        if (count($pieces = explode('|', $key)) > 1) {
            $ret['choices'] = array();
            foreach ($pieces as $piece) {
                $ret['choices'][] = $this->_processStructure($piece);
            }
            return $ret;
        }
        $multi = $key{0};
        if ($multi == '+' || $multi == '*') {
            $ret['multiple'] = $key{0};
            $key = substr($key, 1);
        }
        if (count($attrs = explode('->', $key)) > 1) {
            $ret['tag'] = array_shift($attrs);
            $ret['attribs'] = $attrs;
        } else {
            $ret['tag'] = $key;
        }
        return $ret;
    }

    function _validateStabilityVersion()
    {
        $structure = array('release', 'api');
        $a = $this->_stupidSchemaValidate($structure, $this->_packageInfo['version'], '<version>');
        $a &= $this->_stupidSchemaValidate($structure, $this->_packageInfo['stability'], '<stability>');
        if ($a) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                  $this->_packageInfo['version']['release'])) {
                $this->_invalidVersion('release', $this->_packageInfo['version']['release']);
            }
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                  $this->_packageInfo['version']['api'])) {
                $this->_invalidVersion('api', $this->_packageInfo['version']['api']);
            }
            if (!in_array($this->_packageInfo['stability']['release'],
                  array('snapshot', 'devel', 'alpha', 'beta', 'stable'))) {
                $this->_invalidState('release', $this->_packageInfo['stability']['release']);
            }
            if (!in_array($this->_packageInfo['stability']['api'],
                  array('devel', 'alpha', 'beta', 'stable'))) {
                $this->_invalidState('api', $this->_packageInfo['stability']['api']);
            }
        }
    }

    function _validateMaintainers()
    {
        $structure =
            array(
                'name',
                'user',
                'email',
                'active',
            );
        foreach (array('lead', 'developer', 'contributor', 'helper') as $type) {
            if (!isset($this->_packageInfo[$type])) {
                continue;
            }
            if (isset($this->_packageInfo[$type][0])) {
                foreach ($this->_packageInfo[$type] as $lead) {
                    $this->_stupidSchemaValidate($structure, $lead, '<' . $type . '>');
                }
            } else {
                $this->_stupidSchemaValidate($structure, $this->_packageInfo[$type],
                    '<' . $type . '>');
            }
        }
    }

    function _validatePhpDep($dep, $installcondition = false)
    {
        $structure = array(
            'min',
            '*max',
            '*exclude',
        );
        $type = $installcondition ? '<installcondition><php>' : '<dependencies><required><php>';
        $this->_stupidSchemaValidate($structure, $dep, $type);
        if (isset($dep['min'])) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
                  $dep['min'])) {
                $this->_invalidVersion($type . '<min>', $dep['min']);
            }
        }
        if (isset($dep['max'])) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
                  $dep['max'])) {
                $this->_invalidVersion($type . '<max>', $dep['max']);
            }
        }
        if (isset($dep['exclude'])) {
            if (!is_array($dep['exclude'])) {
                $dep['exclude'] = array($dep['exclude']);
            }
            foreach ($dep['exclude'] as $exclude) {
                if (!preg_match(
                     '/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
                     $exclude)) {
                    $this->_invalidVersion($type . '<exclude>', $exclude);
                }
            }
        }
    }

    function _validatePearinstallerDep($dep)
    {
        $structure = array(
            'min',
            '*max',
            '*recommended',
            '*exclude',
        );
        $this->_stupidSchemaValidate($structure, $dep, '<dependencies><required><pearinstaller>');
        if (isset($dep['min'])) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                  $dep['min'])) {
                $this->_invalidVersion('<dependencies><required><pearinstaller><min>',
                    $dep['min']);
            }
        }
        if (isset($dep['max'])) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                  $dep['max'])) {
                $this->_invalidVersion('<dependencies><required><pearinstaller><max>',
                    $dep['max']);
            }
        }
        if (isset($dep['recommended'])) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                  $dep['recommended'])) {
                $this->_invalidVersion('<dependencies><required><pearinstaller><recommended>',
                    $dep['recommended']);
            }
        }
        if (isset($dep['exclude'])) {
            if (!is_array($dep['exclude'])) {
                $dep['exclude'] = array($dep['exclude']);
            }
            foreach ($dep['exclude'] as $exclude) {
                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                      $exclude)) {
                    $this->_invalidVersion('<dependencies><required><pearinstaller><exclude>',
                        $exclude);
                }
            }
        }
    }

    function _validatePackageDep($dep, $group, $type = '<package>')
    {
        if (isset($dep['uri'])) {
            if (isset($dep['conflicts'])) {
                $structure = array(
                    'name',
                    'uri',
                    'conflicts',
                    '*providesextension',
                );
            } else {
                $structure = array(
                    'name',
                    'uri',
                    '*providesextension',
                );
            }
        } else {
            if (isset($dep['conflicts'])) {
                $structure = array(
                    'name',
                    'channel',
                    '*min',
                    '*max',
                    '*exclude',
                    'conflicts',
                    '*providesextension',
                );
            } else {
                $structure = array(
                    'name',
                    'channel',
                    '*min',
                    '*max',
                    '*recommended',
                    '*exclude',
                    '*nodefault',
                    '*providesextension',
                );
            }
        }
        if (isset($dep['name'])) {
            $type .= '<name>' . $dep['name'] . '</name>';
        }
        $this->_stupidSchemaValidate($structure, $dep, '<dependencies>' . $group . $type);
        if (isset($dep['uri']) && (isset($dep['min']) || isset($dep['max']) ||
              isset($dep['recommended']) || isset($dep['exclude']))) {
            $this->_uriDepsCannotHaveVersioning('<dependencies>' . $group . $type);
        }
        if (isset($dep['channel']) && strtolower($dep['channel']) == '__uri') {
            $this->_DepchannelCannotBeUri('<dependencies>' . $group . $type);
        }
        if (isset($dep['min'])) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                  $dep['min'])) {
                $this->_invalidVersion('<dependencies>' . $group . $type . '<min>', $dep['min']);
            }
        }
        if (isset($dep['max'])) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                  $dep['max'])) {
                $this->_invalidVersion('<dependencies>' . $group . $type . '<max>', $dep['max']);
            }
        }
        if (isset($dep['recommended'])) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                  $dep['recommended'])) {
                $this->_invalidVersion('<dependencies>' . $group . $type . '<recommended>',
                    $dep['recommended']);
            }
        }
        if (isset($dep['exclude'])) {
            if (!is_array($dep['exclude'])) {
                $dep['exclude'] = array($dep['exclude']);
            }
            foreach ($dep['exclude'] as $exclude) {
                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                      $exclude)) {
                    $this->_invalidVersion('<dependencies>' . $group . $type . '<exclude>',
                        $exclude);
                }
            }
        }
    }

    function _validateSubpackageDep($dep, $group)
    {
        $this->_validatePackageDep($dep, $group, '<subpackage>');
        if (isset($dep['providesextension'])) {
            $this->_subpackageCannotProvideExtension(isset($dep['name']) ? $dep['name'] : '');
        }
        if (isset($dep['conflicts'])) {
            $this->_subpackagesCannotConflict(isset($dep['name']) ? $dep['name'] : '');
        }
    }

    function _validateExtensionDep($dep, $group = false, $installcondition = false)
    {
        if (isset($dep['conflicts'])) {
            $structure = array(
                'name',
                '*min',
                '*max',
                '*exclude',
                'conflicts',
            );
        } else {
            $structure = array(
                'name',
                '*min',
                '*max',
                '*recommended',
                '*exclude',
            );
        }
        if ($installcondition) {
            $type = '<installcondition><extension>';
        } else {
            $type = '<dependencies>' . $group . '<extension>';
        }
        if (isset($dep['name'])) {
            $type .= '<name>' . $dep['name'] . '</name>';
        }
        $this->_stupidSchemaValidate($structure, $dep, $type);
        if (isset($dep['min'])) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                  $dep['min'])) {
                $this->_invalidVersion(substr($type, 1) . '<min', $dep['min']);
            }
        }
        if (isset($dep['max'])) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                  $dep['max'])) {
                $this->_invalidVersion(substr($type, 1) . '<max', $dep['max']);
            }
        }
        if (isset($dep['recommended'])) {
            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                  $dep['recommended'])) {
                $this->_invalidVersion(substr($type, 1) . '<recommended', $dep['recommended']);
            }
        }
        if (isset($dep['exclude'])) {
            if (!is_array($dep['exclude'])) {
                $dep['exclude'] = array($dep['exclude']);
            }
            foreach ($dep['exclude'] as $exclude) {
                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                      $exclude)) {
                    $this->_invalidVersion(substr($type, 1) . '<exclude', $exclude);
                }
            }
        }
    }

    function _validateOsDep($dep, $installcondition = false)
    {
        $structure = array(
            'name',
            '*conflicts',
        );
        $type = $installcondition ? '<installcondition><os>' : '<dependencies><required><os>';
        if ($this->_stupidSchemaValidate($structure, $dep, $type)) {
            if ($dep['name'] == '*') {
                if (array_key_exists('conflicts', $dep)) {
                    $this->_cannotConflictWithAllOs($type);
                }
            }
        }
    }

    function _validateArchDep($dep, $installcondition = false)
    {
        $structure = array(
            'pattern',
            '*conflicts',
        );
        $type = $installcondition ? '<installcondition><arch>' : '<dependencies><required><arch>';
        $this->_stupidSchemaValidate($structure, $dep, $type);
    }

    function _validateInstallConditions($cond, $release)
    {
        $structure = array(
            '*php',
            '*extension',
            '*os',
            '*arch',
        );
        if (!$this->_stupidSchemaValidate($structure,
              $cond, $release)) {
            return false;
        }
        foreach (array('php', 'extension', 'os', 'arch') as $type) {
            if (isset($cond[$type])) {
                $iter = $cond[$type];
                if (!is_array($iter) || !isset($iter[0])) {
                    $iter = array($iter);
                }
                foreach ($iter as $package) {
                    if ($type == 'extension') {
                        $this->{"_validate{$type}Dep"}($package, false, true);
                    } else {
                        $this->{"_validate{$type}Dep"}($package, true);
                    }
                }
            }
        }
    }

    function _validateDependencies()
    {
        $structure = array(
            'required',
            '*optional',
            '*group->name->hint'
        );
        if (!$this->_stupidSchemaValidate($structure,
              $this->_packageInfo['dependencies'], '<dependencies>')) {
            return false;
        }
        foreach (array('required', 'optional') as $simpledep) {
            if (isset($this->_packageInfo['dependencies'][$simpledep])) {
                if ($simpledep == 'optional') {
                    $structure = array(
                        '*package',
                        '*subpackage',
                        '*extension',
                    );
                } else {
                    $structure = array(
                        'php',
                        'pearinstaller',
                        '*package',
                        '*subpackage',
                        '*extension',
                        '*os',
                        '*arch',
                    );
                }
                if ($this->_stupidSchemaValidate($structure,
                      $this->_packageInfo['dependencies'][$simpledep],
                      "<dependencies><$simpledep>")) {
                    foreach (array('package', 'subpackage', 'extension') as $type) {
                        if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) {
                            $iter = $this->_packageInfo['dependencies'][$simpledep][$type];
                            if (!isset($iter[0])) {
                                $iter = array($iter);
                            }
                            foreach ($iter as $package) {
                                if ($type != 'extension') {
                                    if (isset($package['uri'])) {
                                        if (isset($package['channel'])) {
                                            $this->_UrlOrChannel($type,
                                                $package['name']);
                                        }
                                    } else {
                                        if (!isset($package['channel'])) {
                                            $this->_NoChannel($type, $package['name']);
                                        }
                                    }
                                }
                                $this->{"_validate{$type}Dep"}($package, "<$simpledep>");
                            }
                        }
                    }
                    if ($simpledep == 'optional') {
                        continue;
                    }
                    foreach (array('php', 'pearinstaller', 'os', 'arch') as $type) {
                        if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) {
                            $iter = $this->_packageInfo['dependencies'][$simpledep][$type];
                            if (!isset($iter[0])) {
                                $iter = array($iter);
                            }
                            foreach ($iter as $package) {
                                $this->{"_validate{$type}Dep"}($package);
                            }
                        }
                    }
                }
            }
        }
        if (isset($this->_packageInfo['dependencies']['group'])) {
            $groups = $this->_packageInfo['dependencies']['group'];
            if (!isset($groups[0])) {
                $groups = array($groups);
            }
            $structure = array(
                '*package',
                '*subpackage',
                '*extension',
            );
            foreach ($groups as $group) {
                if ($this->_stupidSchemaValidate($structure, $group, '<group>')) {
                    if (!PEAR_Validate::validGroupName($group['attribs']['name'])) {
                        $this->_invalidDepGroupName($group['attribs']['name']);
                    }
                    foreach (array('package', 'subpackage', 'extension') as $type) {
                        if (isset($group[$type])) {
                            $iter = $group[$type];
                            if (!isset($iter[0])) {
                                $iter = array($iter);
                            }
                            foreach ($iter as $package) {
                                if ($type != 'extension') {
                                    if (isset($package['uri'])) {
                                        if (isset($package['channel'])) {
                                            $this->_UrlOrChannelGroup($type,
                                                $package['name'],
                                                $group['name']);
                                        }
                                    } else {
                                        if (!isset($package['channel'])) {
                                            $this->_NoChannelGroup($type,
                                                $package['name'],
                                                $group['name']);
                                        }
                                    }
                                }
                                $this->{"_validate{$type}Dep"}($package, '<group name="' .
                                    $group['attribs']['name'] . '">');
                            }
                        }
                    }
                }
            }
        }
    }

    function _validateCompatible()
    {
        $compat = $this->_packageInfo['compatible'];
        if (!isset($compat[0])) {
            $compat = array($compat);
        }
        $required = array('name', 'channel', 'min', 'max', '*exclude');
        foreach ($compat as $package) {
            $type = '<compatible>';
            if (is_array($package) && array_key_exists('name', $package)) {
                $type .= '<name>' . $package['name'] . '</name>';
            }
            $this->_stupidSchemaValidate($required, $package, $type);
            if (is_array($package) && array_key_exists('min', $package)) {
                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                      $package['min'])) {
                    $this->_invalidVersion(substr($type, 1) . '<min', $package['min']);
                }
            }
            if (is_array($package) && array_key_exists('max', $package)) {
                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                      $package['max'])) {
                    $this->_invalidVersion(substr($type, 1) . '<max', $package['max']);
                }
            }
            if (is_array($package) && array_key_exists('exclude', $package)) {
                if (!is_array($package['exclude'])) {
                    $package['exclude'] = array($package['exclude']);
                }
                foreach ($package['exclude'] as $exclude) {
                    if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
                          $exclude)) {
                        $this->_invalidVersion(substr($type, 1) . '<exclude', $exclude);
                    }
                }
            }
        }
    }

    function _validateBundle($list)
    {
        if (!is_array($list) || !isset($list['bundledpackage'])) {
            return $this->_NoBundledPackages();
        }
        if (!is_array($list['bundledpackage']) || !isset($list['bundledpackage'][0])) {
            return $this->_AtLeast2BundledPackages();
        }
        foreach ($list['bundledpackage'] as $package) {
            if (!is_string($package)) {
                $this->_bundledPackagesMustBeFilename();
            }
        }
    }

    function _validateFilelist($list = false, $allowignore = false, $dirs = '')
    {
        $iscontents = false;
        if (!$list) {
            $iscontents = true;
            $list = $this->_packageInfo['contents'];
            if (isset($this->_packageInfo['bundle'])) {
                return $this->_validateBundle($list);
            }
        }
        if ($allowignore) {
            $struc = array(
                '*install->name->as',
                '*ignore->name'
            );
        } else {
            $struc = array(
                '*dir->name->?baseinstalldir',
                '*file->name->role->?baseinstalldir->?md5sum'
            );
            if (isset($list['dir']) && isset($list['file'])) {
                // stave off validation errors without requiring a set order.
                $_old = $list;
                if (isset($list['attribs'])) {
                    $list = array('attribs' => $_old['attribs']);
                }
                $list['dir'] = $_old['dir'];
                $list['file'] = $_old['file'];
            }
        }
        if (!isset($list['attribs']) || !isset($list['attribs']['name'])) {
            $unknown = $allowignore ? '<filelist>' : '<dir name="*unknown*">';
            $dirname = $iscontents ? '<contents>' : $unknown;
        } else {
            $dirname = '<dir name="' . $list['attribs']['name'] . '">';
            if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
                          str_replace('\\', '/', $list['attribs']['name']))) {
                // file contains .. parent directory or . cur directory
                $this->_invalidDirName($list['attribs']['name']);
            }
        }
        $res = $this->_stupidSchemaValidate($struc, $list, $dirname);
        if ($allowignore && $res) {
            $ignored_or_installed = array();
            $this->_pf->getFilelist();
            $fcontents = $this->_pf->getContents();
            $filelist = array();
            if (!isset($fcontents['dir']['file'][0])) {
                $fcontents['dir']['file'] = array($fcontents['dir']['file']);
            }
            foreach ($fcontents['dir']['file'] as $file) {
                $filelist[$file['attribs']['name']] = true;
            }
            if (isset($list['install'])) {
                if (!isset($list['install'][0])) {
                    $list['install'] = array($list['install']);
                }
                foreach ($list['install'] as $file) {
                    if (!isset($filelist[$file['attribs']['name']])) {
                        $this->_notInContents($file['attribs']['name'], 'install');
                        continue;
                    }
                    if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) {
                        $this->_multipleInstallAs($file['attribs']['name']);
                    }
                    if (!isset($ignored_or_installed[$file['attribs']['name']])) {
                        $ignored_or_installed[$file['attribs']['name']] = array();
                    }
                    $ignored_or_installed[$file['attribs']['name']][] = 1;
                    if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
                                  str_replace('\\', '/', $file['attribs']['as']))) {
                        // file contains .. parent directory or . cur directory references
                        $this->_invalidFileInstallAs($file['attribs']['name'],
                            $file['attribs']['as']);
                    }
                }
            }
            if (isset($list['ignore'])) {
                if (!isset($list['ignore'][0])) {
                    $list['ignore'] = array($list['ignore']);
                }
                foreach ($list['ignore'] as $file) {
                    if (!isset($filelist[$file['attribs']['name']])) {
                        $this->_notInContents($file['attribs']['name'], 'ignore');
                        continue;
                    }
                    if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) {
                        $this->_ignoreAndInstallAs($file['attribs']['name']);
                    }
                }
            }
        }
        if (!$allowignore && isset($list['file'])) {
            if (is_string($list['file'])) {
                $this->_oldStyleFileNotAllowed();
                return false;
            }
            if (!isset($list['file'][0])) {
                // single file
                $list['file'] = array($list['file']);
            }
            foreach ($list['file'] as $i => $file)
            {
                if (isset($file['attribs']) && isset($file['attribs']['name'])) {
                    if ($file['attribs']['name']{0} == '.' &&
                          $file['attribs']['name']{1} == '/') {
                        // name is something like "./doc/whatever.txt"
                        $this->_invalidFileName($file['attribs']['name'], $dirname);
                    }
                    if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
                                  str_replace('\\', '/', $file['attribs']['name']))) {
                        // file contains .. parent directory or . cur directory
                        $this->_invalidFileName($file['attribs']['name'], $dirname);
                    }
                }
                if (isset($file['attribs']) && isset($file['attribs']['role'])) {
                    if (!$this->_validateRole($file['attribs']['role'])) {
                        if (isset($this->_packageInfo['usesrole'])) {
                            $roles = $this->_packageInfo['usesrole'];
                            if (!isset($roles[0])) {
                                $roles = array($roles);
                            }
                            foreach ($roles as $role) {
                                if ($role['role'] = $file['attribs']['role']) {
                                    $msg = 'This package contains role "%role%" and requires ' .
                                        'package "%package%" to be used';
                                    if (isset($role['uri'])) {
                                        $params = array('role' => $role['role'],
                                            'package' => $role['uri']);
                                    } else {
                                        $params = array('role' => $role['role'],
                                            'package' => $this->_pf->_registry->
                                            parsedPackageNameToString(array('package' =>
                                                $role['package'], 'channel' => $role['channel']),
                                                true));
                                    }
                                    $this->_stack->push('_mustInstallRole', 'error', $params, $msg);
                                }
                            }
                        }
                        $this->_invalidFileRole($file['attribs']['name'],
                            $dirname, $file['attribs']['role']);
                    }
                }
                if (!isset($file['attribs'])) {
                    continue;
                }
                $save = $file['attribs'];
                if ($dirs) {
                    $save['name'] = $dirs . '/' . $save['name'];
                }
                unset($file['attribs']);
                if (count($file) && $this->_curState != PEAR_VALIDATE_DOWNLOADING) { // has tasks
                    foreach ($file as $task => $value) {
                        if ($tagClass = $this->_pf->getTask($task)) {
                            if (!is_array($value) || !isset($value[0])) {
                                $value = array($value);
                            }
                            foreach ($value as $v) {
                                $ret = call_user_func(array($tagClass, 'validateXml'),
                                    $this->_pf, $v, $this->_pf->_config, $save);
                                if (is_array($ret)) {
                                    $this->_invalidTask($task, $ret, isset($save['name']) ?
                                        $save['name'] : '');
                                }
                            }
                        } else {
                            if (isset($this->_packageInfo['usestask'])) {
                                $roles = $this->_packageInfo['usestask'];
                                if (!isset($roles[0])) {
                                    $roles = array($roles);
                                }
                                foreach ($roles as $role) {
                                    if ($role['task'] = $task) {
                                        $msg = 'This package contains task "%task%" and requires ' .
                                            'package "%package%" to be used';
                                        if (isset($role['uri'])) {
                                            $params = array('task' => $role['task'],
                                                'package' => $role['uri']);
                                        } else {
                                            $params = array('task' => $role['task'],
                                                'package' => $this->_pf->_registry->
                                                parsedPackageNameToString(array('package' =>
                                                    $role['package'], 'channel' => $role['channel']),
                                                    true));
                                        }
                                        $this->_stack->push('_mustInstallTask', 'error',
                                            $params, $msg);
                                    }
                                }
                            }
                            $this->_unknownTask($task, $save['name']);
                        }
                    }
                }
            }
        }
        if (isset($list['ignore'])) {
            if (!$allowignore) {
                $this->_ignoreNotAllowed('ignore');
            }
        }
        if (isset($list['install'])) {
            if (!$allowignore) {
                $this->_ignoreNotAllowed('install');
            }
        }
        if (isset($list['file'])) {
            if ($allowignore) {
                $this->_fileNotAllowed('file');
            }
        }
        if (isset($list['dir'])) {
            if ($allowignore) {
                $this->_fileNotAllowed('dir');
            } else {
                if (!isset($list['dir'][0])) {
                    $list['dir'] = array($list['dir']);
                }
                foreach ($list['dir'] as $dir) {
                    if (isset($dir['attribs']) && isset($dir['attribs']['name'])) {
                        if ($dir['attribs']['name'] == '/' ||
                              !isset($this->_packageInfo['contents']['dir']['dir'])) {
                            // always use nothing if the filelist has already been flattened
                            $newdirs = '';
                        } elseif ($dirs == '') {
                            $newdirs = $dir['attribs']['name'];
                        } else {
                            $newdirs = $dirs . '/' . $dir['attribs']['name'];
                        }
                    } else {
                        $newdirs = $dirs;
                    }
                    $this->_validateFilelist($dir, $allowignore, $newdirs);
                }
            }
        }
    }

    function _validateRelease()
    {
        if (isset($this->_packageInfo['phprelease'])) {
            $release = 'phprelease';
            if (isset($this->_packageInfo['providesextension'])) {
                $this->_cannotProvideExtension($release);
            }
            if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
                $this->_cannotHaveSrcpackage($release);
            }
            $releases = $this->_packageInfo['phprelease'];
            if (!is_array($releases)) {
                return true;
            }
            if (!isset($releases[0])) {
                $releases = array($releases);
            }
            foreach ($releases as $rel) {
                $this->_stupidSchemaValidate(array(
                    '*installconditions',
                    '*filelist',
                ), $rel, '<phprelease>');
            }
        }
        foreach (array('', 'zend') as $prefix) {
            $releasetype = $prefix . 'extsrcrelease';
            if (isset($this->_packageInfo[$releasetype])) {
                $release = $releasetype;
                if (!isset($this->_packageInfo['providesextension'])) {
                    $this->_mustProvideExtension($release);
                }
                if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
                    $this->_cannotHaveSrcpackage($release);
                }
                $releases = $this->_packageInfo[$releasetype];
                if (!is_array($releases)) {
                    return true;
                }
                if (!isset($releases[0])) {
                    $releases = array($releases);
                }
                foreach ($releases as $rel) {
                    $this->_stupidSchemaValidate(array(
                        '*installconditions',
                        '*configureoption->name->prompt->?default',
                        '*binarypackage',
                        '*filelist',
                    ), $rel, '<' . $releasetype . '>');
                    if (isset($rel['binarypackage'])) {
                        if (!is_array($rel['binarypackage']) || !isset($rel['binarypackage'][0])) {
                            $rel['binarypackage'] = array($rel['binarypackage']);
                        }
                        foreach ($rel['binarypackage'] as $bin) {
                            if (!is_string($bin)) {
                                $this->_binaryPackageMustBePackagename();
                            }
                        }
                    }
                }
            }
            $releasetype = 'extbinrelease';
            if (isset($this->_packageInfo[$releasetype])) {
                $release = $releasetype;
                if (!isset($this->_packageInfo['providesextension'])) {
                    $this->_mustProvideExtension($release);
                }
                if (isset($this->_packageInfo['channel']) &&
                      !isset($this->_packageInfo['srcpackage'])) {
                    $this->_mustSrcPackage($release);
                }
                if (isset($this->_packageInfo['uri']) && !isset($this->_packageInfo['srcuri'])) {
                    $this->_mustSrcuri($release);
                }
                $releases = $this->_packageInfo[$releasetype];
                if (!is_array($releases)) {
                    return true;
                }
                if (!isset($releases[0])) {
                    $releases = array($releases);
                }
                foreach ($releases as $rel) {
                    $this->_stupidSchemaValidate(array(
                        '*installconditions',
                        '*filelist',
                    ), $rel, '<' . $releasetype . '>');
                }
            }
        }
        if (isset($this->_packageInfo['bundle'])) {
            $release = 'bundle';
            if (isset($this->_packageInfo['providesextension'])) {
                $this->_cannotProvideExtension($release);
            }
            if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
                $this->_cannotHaveSrcpackage($release);
            }
            $releases = $this->_packageInfo['bundle'];
            if (!is_array($releases) || !isset($releases[0])) {
                $releases = array($releases);
            }
            foreach ($releases as $rel) {
                $this->_stupidSchemaValidate(array(
                    '*installconditions',
                    '*filelist',
                ), $rel, '<bundle>');
            }
        }
        foreach ($releases as $rel) {
            if (is_array($rel) && array_key_exists('installconditions', $rel)) {
                $this->_validateInstallConditions($rel['installconditions'],
                    "<$release><installconditions>");
            }
            if (is_array($rel) && array_key_exists('filelist', $rel)) {
                if ($rel['filelist']) {

                    $this->_validateFilelist($rel['filelist'], true);
                }
            }
        }
    }

    /**
     * This is here to allow role extension through plugins
     * @param string
     */
    function _validateRole($role)
    {
        return in_array($role, PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType()));
    }

    function _pearVersionTooLow($version)
    {
        $this->_stack->push(__FUNCTION__, 'error',
            array('version' => $version),
            'This package.xml requires PEAR version %version% to parse properly, we are ' .
            'version 1.7.2');
    }

    function _invalidTagOrder($oktags, $actual, $root)
    {
        $this->_stack->push(__FUNCTION__, 'error',
            array('oktags' => $oktags, 'actual' => $actual, 'root' => $root),
            'Invalid tag order in %root%, found <%actual%> expected one of "%oktags%"');
    }

    function _ignoreNotAllowed($type)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
            '<%type%> is not allowed inside global <contents>, only inside ' .
            '<phprelease>/<extbinrelease>/<zendextbinrelease>, use <dir> and <file> only');
    }

    function _fileNotAllowed($type)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
            '<%type%> is not allowed inside release <filelist>, only inside ' .
            '<contents>, use <ignore> and <install> only');
    }

    function _oldStyleFileNotAllowed()
    {
        $this->_stack->push(__FUNCTION__, 'error', array(),
            'Old-style <file>name</file> is not allowed.  Use' .
            '<file name="name" role="role"/>');
    }

    function _tagMissingAttribute($tag, $attr, $context)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag,
            'attribute' => $attr, 'context' => $context),
            'tag <%tag%> in context "%context%" has no attribute "%attribute%"');
    }

    function _tagHasNoAttribs($tag, $context)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag,
            'context' => $context),
            'tag <%tag%> has no attributes in context "%context%"');
    }

    function _invalidInternalStructure()
    {
        $this->_stack->push(__FUNCTION__, 'exception', array(),
            'internal array was not generated by compatible parser, or extreme parser error, cannot continue');
    }

    function _invalidFileRole($file, $dir, $role)
    {
        $this->_stack->push(__FUNCTION__, 'error', array(
            'file' => $file, 'dir' => $dir, 'role' => $role,
            'roles' => PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType())),
            'File "%file%" in directory "%dir%" has invalid role "%role%", should be one of %roles%');
    }

    function _invalidFileName($file, $dir)
    {
        $this->_stack->push(__FUNCTION__, 'error', array(
            'file' => $file),
            'File "%file%" in directory "%dir%" cannot begin with "./" or contain ".."');
    }

    function _invalidFileInstallAs($file, $as)
    {
        $this->_stack->push(__FUNCTION__, 'error', array(
            'file' => $file, 'as' => $as),
            'File "%file%" <install as="%as%"/> cannot contain "./" or contain ".."');
    }

    function _invalidDirName($dir)
    {
        $this->_stack->push(__FUNCTION__, 'error', array(
            'dir' => $file),
            'Directory "%dir%" cannot begin with "./" or contain ".."');
    }

    function _filelistCannotContainFile($filelist)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist),
            '<%tag%> can only contain <dir>, contains <file>.  Use ' .
            '<dir name="/"> as the first dir element');
    }

    function _filelistMustContainDir($filelist)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist),
            '<%tag%> must contain <dir>.  Use <dir name="/"> as the ' .
            'first dir element');
    }

    function _tagCannotBeEmpty($tag)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag),
            '<%tag%> cannot be empty (<%tag%/>)');
    }

    function _UrlOrChannel($type, $name)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
            'name' => $name),
            'Required dependency <%type%> "%name%" can have either url OR ' .
            'channel attributes, and not both');
    }

    function _NoChannel($type, $name)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
            'name' => $name),
            'Required dependency <%type%> "%name%" must have either url OR ' .
            'channel attributes');
    }

    function _UrlOrChannelGroup($type, $name, $group)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
            'name' => $name, 'group' => $group),
            'Group "%group%" dependency <%type%> "%name%" can have either url OR ' .
            'channel attributes, and not both');
    }

    function _NoChannelGroup($type, $name, $group)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
            'name' => $name, 'group' => $group),
            'Group "%group%" dependency <%type%> "%name%" must have either url OR ' .
            'channel attributes');
    }

    function _unknownChannel($channel)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('channel' => $channel),
            'Unknown channel "%channel%"');
    }

    function _noPackageVersion()
    {
        $this->_stack->push(__FUNCTION__, 'error', array(),
            'package.xml <package> tag has no version attribute, or version is not 2.0');
    }

    function _NoBundledPackages()
    {
        $this->_stack->push(__FUNCTION__, 'error', array(),
            'No <bundledpackage> tag was found in <contents>, required for bundle packages');
    }

    function _AtLeast2BundledPackages()
    {
        $this->_stack->push(__FUNCTION__, 'error', array(),
            'At least 2 packages must be bundled in a bundle package');
    }

    function _ChannelOrUri($name)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
            'Bundled package "%name%" can have either a uri or a channel, not both');
    }

    function _noChildTag($child, $tag)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('child' => $child, 'tag' => $tag),
            'Tag <%tag%> is missing child tag <%child%>');
    }

    function _invalidVersion($type, $value)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value),
            'Version type <%type%> is not a valid version (%value%)');
    }

    function _invalidState($type, $value)
    {
        $states = array('stable', 'beta', 'alpha', 'devel');
        if ($type != 'api') {
            $states[] = 'snapshot';
        }
        if (strtolower($value) == 'rc') {
            $this->_stack->push(__FUNCTION__, 'error',
                array('version' => $this->_packageInfo['version']['release']),
                'RC is not a state, it is a version postfix, try %version%RC1, stability beta');
        }
        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value,
            'types' => $states),
            'Stability type <%type%> is not a valid stability (%value%), must be one of ' .
            '%types%');
    }

    function _invalidTask($task, $ret, $file)
    {
        switch ($ret[0]) {
            case PEAR_TASK_ERROR_MISSING_ATTRIB :
                $info = array('attrib' => $ret[1], 'task' => $task, 'file' => $file);
                $msg = 'task <%task%> is missing attribute "%attrib%" in file %file%';
            break;
            case PEAR_TASK_ERROR_NOATTRIBS :
                $info = array('task' => $task, 'file' => $file);
                $msg = 'task <%task%> has no attributes in file %file%';
            break;
            case PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE :
                $info = array('attrib' => $ret[1], 'values' => $ret[3],
                    'was' => $ret[2], 'task' => $task, 'file' => $file);
                $msg = 'task <%task%> attribute "%attrib%" has the wrong value "%was%" '.
                    'in file %file%, expecting one of "%values%"';
            break;
            case PEAR_TASK_ERROR_INVALID :
                $info = array('reason' => $ret[1], 'task' => $task, 'file' => $file);
                $msg = 'task <%task%> in file %file% is invalid because of "%reason%"';
            break;
        }
        $this->_stack->push(__FUNCTION__, 'error', $info, $msg);
    }

    function _unknownTask($task, $file)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('task' => $task, 'file' => $file),
            'Unknown task "%task%" passed in file <file name="%file%">');
    }

    function _subpackageCannotProvideExtension($name)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
            'Subpackage dependency "%name%" cannot use <providesextension>, ' .
            'only package dependencies can use this tag');
    }

    function _subpackagesCannotConflict($name)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
            'Subpackage dependency "%name%" cannot use <conflicts/>, ' .
            'only package dependencies can use this tag');
    }

    function _cannotProvideExtension($release)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
            '<%release%> packages cannot use <providesextension>, only extbinrelease, extsrcrelease, zendextsrcrelease, and zendextbinrelease can provide a PHP extension');
    }

    function _mustProvideExtension($release)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
            '<%release%> packages must use <providesextension> to indicate which PHP extension is provided');
    }

    function _cannotHaveSrcpackage($release)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
            '<%release%> packages cannot specify a source code package, only extension binaries may use the <srcpackage> tag');
    }

    function _mustSrcPackage($release)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
            '<extbinrelease>/<zendextbinrelease> packages must specify a source code package with <srcpackage>');
    }

    function _mustSrcuri($release)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
            '<extbinrelease>/<zendextbinrelease> packages must specify a source code package with <srcuri>');
    }

    function _uriDepsCannotHaveVersioning($type)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
            '%type%: dependencies with a <uri> tag cannot have any versioning information');
    }

    function _conflictingDepsCannotHaveVersioning($type)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
            '%type%: conflicting dependencies cannot have versioning info, use <exclude> to ' .
            'exclude specific versions of a dependency');
    }

    function _DepchannelCannotBeUri($type)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
            '%type%: channel cannot be __uri, this is a pseudo-channel reserved for uri ' .
            'dependencies only');
    }

    function _bundledPackagesMustBeFilename()
    {
        $this->_stack->push(__FUNCTION__, 'error', array(),
            '<bundledpackage> tags must contain only the filename of a package release ' .
            'in the bundle');
    }

    function _binaryPackageMustBePackagename()
    {
        $this->_stack->push(__FUNCTION__, 'error', array(),
            '<binarypackage> tags must contain the name of a package that is ' .
            'a compiled version of this extsrc/zendextsrc package');
    }

    function _fileNotFound($file)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
            'File "%file%" in package.xml does not exist');
    }

    function _notInContents($file, $tag)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file, 'tag' => $tag),
            '<%tag% name="%file%"> is invalid, file is not in <contents>');
    }

    function _cannotValidateNoPathSet()
    {
        $this->_stack->push(__FUNCTION__, 'error', array(),
            'Cannot validate files, no path to package file is set (use setPackageFile())');
    }

    function _usesroletaskMustHaveChannelOrUri($role, $tag)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag),
            '<%tag%> for role "%role%" must contain either <uri>, or <channel> and <package>');
    }

    function _usesroletaskMustHavePackage($role, $tag)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag),
            '<%tag%> for role "%role%" must contain <package>');
    }

    function _usesroletaskMustHaveRoleTask($tag, $type)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag, 'type' => $type),
            '<%tag%> must contain <%type%> defining the %type% to be used');
    }

    function _cannotConflictWithAllOs($type)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag),
            '%tag% cannot conflict with all OSes');
    }

    function _invalidDepGroupName($name)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
            'Invalid dependency group name "%name%"');
    }

    function _multipleToplevelDirNotAllowed()
    {
        $this->_stack->push(__FUNCTION__, 'error', array(),
            'Multiple top-level <dir> tags are not allowed.  Enclose them ' .
                'in a <dir name="/">');
    }

    function _multipleInstallAs($file)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
            'Only one <install> tag is allowed for file "%file%"');
    }

    function _ignoreAndInstallAs($file)
    {
        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
            'Cannot have both <ignore> and <install> tags for file "%file%"');
    }

    function _analyzeBundledPackages()
    {
        if (!$this->_isValid) {
            return false;
        }
        if (!$this->_pf->getPackageType() == 'bundle') {
            return false;
        }
        if (!isset($this->_pf->_packageFile)) {
            return false;
        }
        $dir_prefix = dirname($this->_pf->_packageFile);
        $common = new PEAR_Common;
        $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') :
            array($common, 'log');
        $info = $this->_pf->getContents();
        $info = $info['bundledpackage'];
        if (!is_array($info)) {
            $info = array($info);
        }
        $pkg = &new PEAR_PackageFile($this->_pf->_config);
        foreach ($info as $package) {
            if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $package)) {
                $this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $package);
                $this->_isValid = 0;
                continue;
            }
            call_user_func_array($log, array(1, "Analyzing bundled package $package"));
            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
            $ret = $pkg->fromAnyFile($dir_prefix . DIRECTORY_SEPARATOR . $package,
                PEAR_VALIDATE_NORMAL);
            PEAR::popErrorHandling();
            if (PEAR::isError($ret)) {
                call_user_func_array($log, array(0, "ERROR: package $package is not a valid " .
                    'package'));
                $inf = $ret->getUserInfo();
                if (is_array($inf)) {
                    foreach ($inf as $err) {
                        call_user_func_array($log, array(1, $err['message']));
                    }
                }
                return false;
            }
        }
        return true;
    }

    function _analyzePhpFiles()
    {
        if (!$this->_isValid) {
            return false;
        }
        if (!isset($this->_pf->_packageFile)) {
            $this->_cannotValidateNoPathSet();
            return false;
        }
        $dir_prefix = dirname($this->_pf->_packageFile);
        $common = new PEAR_Common;
        $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') :
            array(&$common, 'log');
        $info = $this->_pf->getContents();
        if (!$info || !isset($info['dir']['file'])) {
            $this->_tagCannotBeEmpty('contents><dir');
            return false;
        }
        $info = $info['dir']['file'];
        if (isset($info['attribs'])) {
            $info = array($info);
        }
        $provides = array();
        foreach ($info as $fa) {
            $fa = $fa['attribs'];
            $file = $fa['name'];
            if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $file)) {
                $this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $file);
                $this->_isValid = 0;
                continue;
            }
            if (in_array($fa['role'], PEAR_Installer_Role::getPhpRoles()) && $dir_prefix) {
                call_user_func_array($log, array(1, "Analyzing $file"));
                $srcinfo = $this->analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file);
                if ($srcinfo) {
                    $provides = array_merge($provides, $this->_buildProvidesArray($srcinfo));
                }
            }
        }
        $this->_packageName = $pn = $this->_pf->getPackage();
        $pnl = strlen($pn);
        foreach ($provides as $key => $what) {
            if (isset($what['explicit']) || !$what) {
                // skip conformance checks if the provides entry is
                // specified in the package.xml file
                continue;
            }
            extract($what);
            if ($type == 'class') {
                if (!strncasecmp($name, $pn, $pnl)) {
                    continue;
                }
                $this->_stack->push(__FUNCTION__, 'warning',
                    array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn),
                    'in %file%: %type% "%name%" not prefixed with package name "%package%"');
            } elseif ($type == 'function') {
                if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) {
                    continue;
                }
                $this->_stack->push(__FUNCTION__, 'warning',
                    array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn),
                    'in %file%: %type% "%name%" not prefixed with package name "%package%"');
            }
        }
        return $this->_isValid;
    }

    /**
     * Analyze the source code of the given PHP file
     *
     * @param  string Filename of the PHP file
     * @param  boolean whether to analyze $file as the file contents
     * @return mixed
     */
    function analyzeSourceCode($file, $string = false)
    {
        if (!function_exists("token_get_all")) {
            $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
                'Parser error: token_get_all() function must exist to analyze source code, PHP may have been compiled with --disable-tokenizer');
            return false;
        }
        if (!defined('T_DOC_COMMENT')) {
            define('T_DOC_COMMENT', T_COMMENT);
        }
        if (!defined('T_INTERFACE')) {
            define('T_INTERFACE', -1);
        }
        if (!defined('T_IMPLEMENTS')) {
            define('T_IMPLEMENTS', -1);
        }
        if ($string) {
            $contents = $file;
        } else {
            if (!$fp = @fopen($file, "r")) {
                return false;
            }
            fclose($fp);
            $contents = file_get_contents($file);
        }

        // Silence this function so we can catch PHP Warnings and show our own custom message
        $tokens = @token_get_all($contents);
        if (isset($php_errormsg)) {
            $pn = $this->_pf->getPackage();
            $this->_stack->push(__FUNCTION__, 'warning',
                    array('file' => $file, 'package' => $pn),
                    'in %file%: Could not process file for unkown reasons,' .
                    ' possibly a PHP parse error in %file% from %package%');

        }
/*
        for ($i = 0; $i < sizeof($tokens); $i++) {
            @list($token, $data) = $tokens[$i];
            if (is_string($token)) {
                var_dump($token);
            } else {
                print token_name($token) . ' ';
                var_dump(rtrim($data));
            }
        }
*/
        $look_for = 0;
        $paren_level = 0;
        $bracket_level = 0;
        $brace_level = 0;
        $lastphpdoc = '';
        $current_class = '';
        $current_interface = '';
        $current_class_level = -1;
        $current_function = '';
        $current_function_level = -1;
        $declared_classes = array();
        $declared_interfaces = array();
        $declared_functions = array();
        $declared_methods = array();
        $used_classes = array();
        $used_functions = array();
        $extends = array();
        $implements = array();
        $nodeps = array();
        $inquote = false;
        $interface = false;
        for ($i = 0; $i < sizeof($tokens); $i++) {
            if (is_array($tokens[$i])) {
                list($token, $data) = $tokens[$i];
            } else {
                $token = $tokens[$i];
                $data = '';
            }
            if ($inquote) {
                if ($token != '"' && $token != T_END_HEREDOC) {
                    continue;
                } else {
                    $inquote = false;
                    continue;
                }
            }
            switch ($token) {
                case T_WHITESPACE :
                    continue;
                case ';':
                    if ($interface) {
                        $current_function = '';
                        $current_function_level = -1;
                    }
                    break;
                case '"':
                case T_START_HEREDOC:
                    $inquote = true;
                    break;
                case T_CURLY_OPEN:
                case T_DOLLAR_OPEN_CURLY_BRACES:
                case '{': $brace_level++; continue 2;
                case '}':
                    $brace_level--;
                    if ($current_class_level == $brace_level) {
                        $current_class = '';
                        $current_class_level = -1;
                    }
                    if ($current_function_level == $brace_level) {
                        $current_function = '';
                        $current_function_level = -1;
                    }
                    continue 2;
                case '[': $bracket_level++; continue 2;
                case ']': $bracket_level--; continue 2;
                case '(': $paren_level++;   continue 2;
                case ')': $paren_level--;   continue 2;
                case T_INTERFACE:
                    $interface = true;
                case T_CLASS:
                    if (($current_class_level != -1) || ($current_function_level != -1)) {
                        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
                        'Parser error: invalid PHP found in file "%file%"');
                        return false;
                    }
                case T_FUNCTION:
                case T_NEW:
                case T_EXTENDS:
                case T_IMPLEMENTS:
                    $look_for = $token;
                    continue 2;
                case T_STRING:
                    if (version_compare(zend_version(), '2.0', '<')) {
                        if (in_array(strtolower($data),
                            array('public', 'private', 'protected', 'abstract',
                                  'interface', 'implements', 'throw')
                                 )) {
                            $this->_stack->push(__FUNCTION__, 'warning', array(
                                'file' => $file),
                                'Error, PHP5 token encountered in %file%,' .
                                ' analysis should be in PHP5');
                        }
                    }
                    if ($look_for == T_CLASS) {
                        $current_class = $data;
                        $current_class_level = $brace_level;
                        $declared_classes[] = $current_class;
                    } elseif ($look_for == T_INTERFACE) {
                        $current_interface = $data;
                        $current_class_level = $brace_level;
                        $declared_interfaces[] = $current_interface;
                    } elseif ($look_for == T_IMPLEMENTS) {
                        $implements[$current_class] = $data;
                    } elseif ($look_for == T_EXTENDS) {
                        $extends[$current_class] = $data;
                    } elseif ($look_for == T_FUNCTION) {
                        if ($current_class) {
                            $current_function = "$current_class::$data";
                            $declared_methods[$current_class][] = $data;
                        } elseif ($current_interface) {
                            $current_function = "$current_interface::$data";
                            $declared_methods[$current_interface][] = $data;
                        } else {
                            $current_function = $data;
                            $declared_functions[] = $current_function;
                        }
                        $current_function_level = $brace_level;
                        $m = array();
                    } elseif ($look_for == T_NEW) {
                        $used_classes[$data] = true;
                    }
                    $look_for = 0;
                    continue 2;
                case T_VARIABLE:
                    $look_for = 0;
                    continue 2;
                case T_DOC_COMMENT:
                case T_COMMENT:
                    if (preg_match('!^/\*\*\s!', $data)) {
                        $lastphpdoc = $data;
                        if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
                            $nodeps = array_merge($nodeps, $m[1]);
                        }
                    }
                    continue 2;
                case T_DOUBLE_COLON:
                    if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) {
                        $this->_stack->push(__FUNCTION__, 'warning', array('file' => $file),
                            'Parser error: invalid PHP found in file "%file%"');
                        return false;
                    }
                    $class = $tokens[$i - 1][1];
                    if (strtolower($class) != 'parent') {
                        $used_classes[$class] = true;
                    }
                    continue 2;
            }
        }
        return array(
            "source_file" => $file,
            "declared_classes" => $declared_classes,
            "declared_interfaces" => $declared_interfaces,
            "declared_methods" => $declared_methods,
            "declared_functions" => $declared_functions,
            "used_classes" => array_diff(array_keys($used_classes), $nodeps),
            "inheritance" => $extends,
            "implements" => $implements,
            );
    }

    /**
     * Build a "provides" array from data returned by
     * analyzeSourceCode().  The format of the built array is like
     * this:
     *
     *  array(
     *    'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
     *    ...
     *  )
     *
     *
     * @param array $srcinfo array with information about a source file
     * as returned by the analyzeSourceCode() method.
     *
     * @return void
     *
     * @access private
     *
     */
    function _buildProvidesArray($srcinfo)
    {
        if (!$this->_isValid) {
            return array();
        }
        $providesret = array();
        $file = basename($srcinfo['source_file']);
        $pn = $this->_pf->getPackage();
        $pnl = strlen($pn);
        foreach ($srcinfo['declared_classes'] as $class) {
            $key = "class;$class";
            if (isset($providesret[$key])) {
                continue;
            }
            $providesret[$key] =
                array('file'=> $file, 'type' => 'class', 'name' => $class);
            if (isset($srcinfo['inheritance'][$class])) {
                $providesret[$key]['extends'] =
                    $srcinfo['inheritance'][$class];
            }
        }
        foreach ($srcinfo['declared_methods'] as $class => $methods) {
            foreach ($methods as $method) {
                $function = "$class::$method";
                $key = "function;$function";
                if ($method{0} == '_' || !strcasecmp($method, $class) ||
                    isset($providesret[$key])) {
                    continue;
                }
                $providesret[$key] =
                    array('file'=> $file, 'type' => 'function', 'name' => $function);
            }
        }

        foreach ($srcinfo['declared_functions'] as $function) {
            $key = "function;$function";
            if ($function{0} == '_' || isset($providesret[$key])) {
                continue;
            }
            if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
                $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
            }
            $providesret[$key] =
                array('file'=> $file, 'type' => 'function', 'name' => $function);
        }
        return $providesret;
    }
}
?><?php
/**
 * PEAR_Registry
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V. V. Cox <cox@idecnet.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Registry.php,v 1.171 2008/05/14 04:16:08 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * for PEAR_Error
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/DependencyDB.php';

define('PEAR_REGISTRY_ERROR_LOCK',   -2);
define('PEAR_REGISTRY_ERROR_FORMAT', -3);
define('PEAR_REGISTRY_ERROR_FILE',   -4);
define('PEAR_REGISTRY_ERROR_CONFLICT', -5);
define('PEAR_REGISTRY_ERROR_CHANNEL_FILE', -6);

/**
 * Administration class used to maintain the installed package database.
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V. V. Cox <cox@idecnet.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Registry extends PEAR
{
    // {{{ properties

    /**
     * File containing all channel information.
     * @var string
     */
    var $channels = '';

    /** Directory where registry files are stored.
     * @var string
     */
    var $statedir = '';

    /** File where the file map is stored
     * @var string
     */
    var $filemap = '';

    /** Directory where registry files for channels are stored.
     * @var string
     */
    var $channelsdir = '';

    /** Name of file used for locking the registry
     * @var string
     */
    var $lockfile = '';

    /** File descriptor used during locking
     * @var resource
     */
    var $lock_fp = null;

    /** Mode used during locking
     * @var int
     */
    var $lock_mode = 0; // XXX UNUSED

    /** Cache of package information.  Structure:
     * array(
     *   'package' => array('id' => ... ),
     *   ... )
     * @var array
     */
    var $pkginfo_cache = array();

    /** Cache of file map.  Structure:
     * array( '/path/to/file' => 'package', ... )
     * @var array
     */
    var $filemap_cache = array();

    /**
     * @var false|PEAR_ChannelFile
     */
    var $_pearChannel;

    /**
     * @var false|PEAR_ChannelFile
     */
    var $_peclChannel;

    /**
     * @var PEAR_DependencyDB
     */
    var $_dependencyDB;

    /**
     * @var PEAR_Config
     */
    var $_config;
    // }}}

    // {{{ constructor

    /**
     * PEAR_Registry constructor.
     *
     * @param string (optional) PEAR install directory (for .php files)
     * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PEAR channel, if
     *        default values are not desired.  Only used the very first time a PEAR
     *        repository is initialized
     * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PECL channel, if
     *        default values are not desired.  Only used the very first time a PEAR
     *        repository is initialized
     *
     * @access public
     */
    function PEAR_Registry($pear_install_dir = PEAR_INSTALL_DIR, $pear_channel = false,
                           $pecl_channel = false)
    {
        parent::PEAR();
        $this->setInstallDir($pear_install_dir);
        $this->_pearChannel = $pear_channel;
        $this->_peclChannel = $pecl_channel;
        $this->_config = false;
    }

    function setInstallDir($pear_install_dir = PEAR_INSTALL_DIR)
    {
        $ds = DIRECTORY_SEPARATOR;
        $this->install_dir = $pear_install_dir;
        $this->channelsdir = $pear_install_dir.$ds.'.channels';
        $this->statedir = $pear_install_dir.$ds.'.registry';
        $this->filemap  = $pear_install_dir.$ds.'.filemap';
        $this->lockfile = $pear_install_dir.$ds.'.lock';
    }

    function hasWriteAccess()
    {
        if (!file_exists($this->install_dir)) {
            $dir = $this->install_dir;
            while ($dir && $dir != '.') {
                $olddir = $dir;
                $dir = dirname($dir); // cd ..
                if ($dir != '.' && file_exists($dir)) {
                    if (is_writeable($dir)) {
                        return true;
                    } else {
                        return false;
                    }
                }
                if ($dir == $olddir) { // this can happen in safe mode
                    return @is_writable($dir);
                }
            }
            return false;
        }
        return is_writeable($this->install_dir);
    }

    function setConfig(&$config, $resetInstallDir = true)
    {
        $this->_config = &$config;
        if ($resetInstallDir) {
            $this->setInstallDir($config->get('php_dir'));
        }
    }

    function _initializeChannelDirs()
    {
        static $running = false;
        if (!$running) {
            $running = true;
            $ds = DIRECTORY_SEPARATOR;
            if (!is_dir($this->channelsdir) ||
                  !file_exists($this->channelsdir . $ds . 'pear.php.net.reg') ||
                  !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') ||
                  !file_exists($this->channelsdir . $ds . '__uri.reg')) {
                if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
                    $pear_channel = $this->_pearChannel;
                    if (!is_a($pear_channel, 'PEAR_ChannelFile') || !$pear_channel->validate()) {
                        if (!class_exists('PEAR_ChannelFile')) {
                            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
                        }
                        $pear_channel = new PEAR_ChannelFile;
                        $pear_channel->setName('pear.php.net');
                        $pear_channel->setAlias('pear');
                        $pear_channel->setServer('pear.php.net');
                        $pear_channel->setSummary('PHP Extension and Application Repository');
                        $pear_channel->setDefaultPEARProtocols();
                        $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/');
                        $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/');
                    } else {
                        $pear_channel->setName('pear.php.net');
                        $pear_channel->setAlias('pear');
                    }
                    $pear_channel->validate();
                    $this->_addChannel($pear_channel);
                }
                if (!file_exists($this->channelsdir . $ds . 'pecl.php.net.reg')) {
                    $pecl_channel = $this->_peclChannel;
                    if (!is_a($pecl_channel, 'PEAR_ChannelFile') || !$pecl_channel->validate()) {
                        if (!class_exists('PEAR_ChannelFile')) {
                            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
                        }
                        $pecl_channel = new PEAR_ChannelFile;
                        $pecl_channel->setName('pecl.php.net');
                        $pecl_channel->setAlias('pecl');
                        $pecl_channel->setServer('pecl.php.net');
                        $pecl_channel->setSummary('PHP Extension Community Library');
                        $pecl_channel->setDefaultPEARProtocols();
                        $pecl_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/');
                        $pecl_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/');
                        $pecl_channel->setValidationPackage('PEAR_Validator_PECL', '1.0');
                    } else {
                        $pecl_channel->setName('pecl.php.net');
                        $pecl_channel->setAlias('pecl');
                    }
                    $pecl_channel->validate();
                    $this->_addChannel($pecl_channel);
                }
                if (!file_exists($this->channelsdir . $ds . '__uri.reg')) {
                    if (!class_exists('PEAR_ChannelFile')) {
                        require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
                    }
                    $private = new PEAR_ChannelFile;
                    $private->setName('__uri');
                    $private->addFunction('xmlrpc', '1.0', '****');
                    $private->setSummary('Pseudo-channel for static packages');
                    $this->_addChannel($private);
                }
                $this->_rebuildFileMap();
            }
            $running = false;
        }
    }

    function _initializeDirs()
    {
        $ds = DIRECTORY_SEPARATOR;
        // XXX Compatibility code should be removed in the future
        // rename all registry files if any to lowercase
        if (!OS_WINDOWS && file_exists($this->statedir) && is_dir($this->statedir) &&
              $handle = opendir($this->statedir)) {
            $dest = $this->statedir . $ds;
            while (false !== ($file = readdir($handle))) {
                if (preg_match('/^.*[A-Z].*\.reg\\z/', $file)) {
                    rename($dest . $file, $dest . strtolower($file));
                }
            }
            closedir($handle);
        }
        $this->_initializeChannelDirs();
        if (!file_exists($this->filemap)) {
            $this->_rebuildFileMap();
        }
        $this->_initializeDepDB();
    }

    function _initializeDepDB()
    {
        if (!isset($this->_dependencyDB)) {
            static $initializing = false;
            if (!$initializing) {
                $initializing = true;
                if (!$this->_config) { // never used?
                    if (OS_WINDOWS) {
                        $file = 'pear.ini';
                    } else {
                        $file = '.pearrc';
                    }
                    $this->_config = &new PEAR_Config($this->statedir . DIRECTORY_SEPARATOR .
                        $file);
                    $this->_config->setRegistry($this);
                    $this->_config->set('php_dir', $this->install_dir);
                }
                $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config);
                if (PEAR::isError($this->_dependencyDB)) {
                    // attempt to recover by removing the dep db
                    if (file_exists($this->_config->get('php_dir', null, 'pear.php.net') .
                        DIRECTORY_SEPARATOR . '.depdb')) {
                        @unlink($this->_config->get('php_dir', null, 'pear.php.net') .
                            DIRECTORY_SEPARATOR . '.depdb');
                    }
                    $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config);
                    if (PEAR::isError($this->_dependencyDB)) {
                        echo $this->_dependencyDB->getMessage();
                        echo 'Unrecoverable error';
                        exit(1);
                    }
                }
                $initializing = false;
            }
        }
    }
    // }}}
    // {{{ destructor

    /**
     * PEAR_Registry destructor.  Makes sure no locks are forgotten.
     *
     * @access private
     */
    function _PEAR_Registry()
    {
        parent::_PEAR();
        if (is_resource($this->lock_fp)) {
            $this->_unlock();
        }
    }

    // }}}

    // {{{ _assertStateDir()

    /**
     * Make sure the directory where we keep registry files exists.
     *
     * @return bool TRUE if directory exists, FALSE if it could not be
     * created
     *
     * @access private
     */
    function _assertStateDir($channel = false)
    {
        if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
            return $this->_assertChannelStateDir($channel);
        }
        static $init = false;
        if (!file_exists($this->statedir)) {
            if (!$this->hasWriteAccess()) {
                return false;
            }
            require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
            if (!System::mkdir(array('-p', $this->statedir))) {
                return $this->raiseError("could not create directory '{$this->statedir}'");
            }
            $init = true;
        } elseif (!is_dir($this->statedir)) {
            return $this->raiseError('Cannot create directory ' . $this->statedir . ', ' .
                'it already exists and is not a directory');
        }
        $ds = DIRECTORY_SEPARATOR;
        if (!file_exists($this->channelsdir)) {
            if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg') ||
                  !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') ||
                  !file_exists($this->channelsdir . $ds . '__uri.reg')) {
                $init = true;
            }
        } elseif (!is_dir($this->channelsdir)) {
            return $this->raiseError('Cannot create directory ' . $this->channelsdir . ', ' .
                'it already exists and is not a directory');
        }
        if ($init) {
            static $running = false;
            if (!$running) {
                $running = true;
                $this->_initializeDirs();
                $running = false;
                $init = false;
            }
        } else {
            $this->_initializeDepDB();
        }
        return true;
    }

    // }}}
    // {{{ _assertChannelStateDir()

    /**
     * Make sure the directory where we keep registry files exists for a non-standard channel.
     *
     * @param string channel name
     * @return bool TRUE if directory exists, FALSE if it could not be
     * created
     *
     * @access private
     */
    function _assertChannelStateDir($channel)
    {
        $ds = DIRECTORY_SEPARATOR;
        if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
            if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
                $this->_initializeChannelDirs();
            }
            return $this->_assertStateDir($channel);
        }
        $channelDir = $this->_channelDirectoryName($channel);
        if (!is_dir($this->channelsdir) ||
              !file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
            $this->_initializeChannelDirs();
        }
        if (!file_exists($channelDir)) {
            if (!$this->hasWriteAccess()) {
                return false;
            }
            require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
            if (!System::mkdir(array('-p', $channelDir))) {
                return $this->raiseError("could not create directory '" . $channelDir .
                    "'");
            }
        } elseif (!is_dir($channelDir)) {
            return $this->raiseError("could not create directory '" . $channelDir .
                "', already exists and is not a directory");
        }
        return true;
    }

    // }}}
    // {{{ _assertChannelDir()

    /**
     * Make sure the directory where we keep registry files for channels exists
     *
     * @return bool TRUE if directory exists, FALSE if it could not be
     * created
     *
     * @access private
     */
    function _assertChannelDir()
    {
        if (!file_exists($this->channelsdir)) {
            if (!$this->hasWriteAccess()) {
                return false;
            }
            require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
            if (!System::mkdir(array('-p', $this->channelsdir))) {
                return $this->raiseError("could not create directory '{$this->channelsdir}'");
            }
        } elseif (!is_dir($this->channelsdir)) {
            return $this->raiseError("could not create directory '{$this->channelsdir}" .
                "', it already exists and is not a directory");
            
        }
        if (!file_exists($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) {
            if (!$this->hasWriteAccess()) {
                return false;
            }
            require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
            if (!System::mkdir(array('-p', $this->channelsdir . DIRECTORY_SEPARATOR . '.alias'))) {
                return $this->raiseError("could not create directory '{$this->channelsdir}/.alias'");
            }
        } elseif (!is_dir($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) {
            return $this->raiseError("could not create directory '{$this->channelsdir}" .
                "/.alias', it already exists and is not a directory");
            
        }
        return true;
    }

    // }}}
    // {{{ _packageFileName()

    /**
     * Get the name of the file where data for a given package is stored.
     *
     * @param string channel name, or false if this is a PEAR package
     * @param string package name
     *
     * @return string registry file name
     *
     * @access public
     */
    function _packageFileName($package, $channel = false)
    {
        if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
            return $this->_channelDirectoryName($channel) . DIRECTORY_SEPARATOR .
                strtolower($package) . '.reg';
        }
        return $this->statedir . DIRECTORY_SEPARATOR . strtolower($package) . '.reg';
    }

    // }}}
    // {{{ _channelFileName()

    /**
     * Get the name of the file where data for a given channel is stored.
     * @param string channel name
     * @return string registry file name
     */
    function _channelFileName($channel, $noaliases = false)
    {
        if (!$noaliases) {
            if (file_exists($this->_getChannelAliasFileName($channel))) {
                $channel = implode('', file($this->_getChannelAliasFileName($channel)));
            }
        }
        return $this->channelsdir . DIRECTORY_SEPARATOR . str_replace('/', '_',
            strtolower($channel)) . '.reg';
    }

    // }}}
    // {{{ getChannelAliasFileName()

    /**
     * @param string
     * @return string
     */
    function _getChannelAliasFileName($alias)
    {
        return $this->channelsdir . DIRECTORY_SEPARATOR . '.alias' .
              DIRECTORY_SEPARATOR . str_replace('/', '_', strtolower($alias)) . '.txt';
    }

    // }}}
    // {{{ _getChannelFromAlias()

    /**
     * Get the name of a channel from its alias
     */
    function _getChannelFromAlias($channel)
    {
        if (!$this->_channelExists($channel)) {
            if ($channel == 'pear.php.net') {
                return 'pear.php.net';
            }
            if ($channel == 'pecl.php.net') {
                return 'pecl.php.net';
            }
            if ($channel == '__uri') {
                return '__uri';
            }
            return false;
        }
        $channel = strtolower($channel);
        if (file_exists($this->_getChannelAliasFileName($channel))) {
            // translate an alias to an actual channel
            return implode('', file($this->_getChannelAliasFileName($channel)));
        } else {
            return $channel;
        }
    }    
    // }}}
    // {{{ _getChannelFromAlias()

    /**
     * Get the alias of a channel from its alias or its name
     */
    function _getAlias($channel)
    {
        if (!$this->_channelExists($channel)) {
            if ($channel == 'pear.php.net') {
                return 'pear';
            }
            if ($channel == 'pecl.php.net') {
                return 'pecl';
            }
            return false;
        }
        $channel = $this->_getChannel($channel);
        if (PEAR::isError($channel)) {
            return $channel;
        }
        return $channel->getAlias();
    }    
    // }}}
    // {{{ _channelDirectoryName()

    /**
     * Get the name of the file where data for a given package is stored.
     *
     * @param string channel name, or false if this is a PEAR package
     * @param string package name
     *
     * @return string registry file name
     *
     * @access public
     */
    function _channelDirectoryName($channel)
    {
        if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
            return $this->statedir;
        } else {
            $ch = $this->_getChannelFromAlias($channel);
            if (!$ch) {
                $ch = $channel;
            }
            return $this->statedir . DIRECTORY_SEPARATOR . strtolower('.channel.' .
                str_replace('/', '_', $ch));
        }
    }

    // }}}
    // {{{ _openPackageFile()

    function _openPackageFile($package, $mode, $channel = false)
    {
        if (!$this->_assertStateDir($channel)) {
            return null;
        }
        if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) {
            return null;
        }
        $file = $this->_packageFileName($package, $channel);
        if (!file_exists($file) && $mode == 'r' || $mode == 'rb') {
            return null;
        }
        $fp = @fopen($file, $mode);
        if (!$fp) {
            return null;
        }
        return $fp;
    }

    // }}}
    // {{{ _closePackageFile()

    function _closePackageFile($fp)
    {
        fclose($fp);
    }

    // }}}
    // {{{ _openChannelFile()

    function _openChannelFile($channel, $mode)
    {
        if (!$this->_assertChannelDir()) {
            return null;
        }
        if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) {
            return null;
        }
        $file = $this->_channelFileName($channel);
        if (!file_exists($file) && $mode == 'r' || $mode == 'rb') {
            return null;
        }
        $fp = @fopen($file, $mode);
        if (!$fp) {
            return null;
        }
        return $fp;
    }

    // }}}
    // {{{ _closePackageFile()

    function _closeChannelFile($fp)
    {
        fclose($fp);
    }

    // }}}
    // {{{ _rebuildFileMap()

    function _rebuildFileMap()
    {
        if (!class_exists('PEAR_Installer_Role')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Installer/Role.php';
        }
        $channels = $this->_listAllPackages();
        $files = array();
        foreach ($channels as $channel => $packages) {
            foreach ($packages as $package) {
                $version = $this->_packageInfo($package, 'version', $channel);
                $filelist = $this->_packageInfo($package, 'filelist', $channel);
                if (!is_array($filelist)) {
                    continue;
                }
                foreach ($filelist as $name => $attrs) {
                    if (isset($attrs['attribs'])) {
                        $attrs = $attrs['attribs'];
                    }
                    // it is possible for conflicting packages in different channels to
                    // conflict with data files/doc files
                    if ($name == 'dirtree') {
                        continue;
                    }
                    if (isset($attrs['role']) && !in_array($attrs['role'],
                          PEAR_Installer_Role::getInstallableRoles())) {
                        // these are not installed
                        continue;
                    }
                    if (isset($attrs['role']) && !in_array($attrs['role'],
                          PEAR_Installer_Role::getBaseinstallRoles())) {
                        $attrs['baseinstalldir'] = $package;
                    }
                    if (isset($attrs['baseinstalldir'])) {
                        $file = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name;
                    } else {
                        $file = $name;
                    }
                    $file = preg_replace(',^/+,', '', $file);
                    if ($channel != 'pear.php.net') {
                        if (!isset($files[$attrs['role']])) {
                            $files[$attrs['role']] = array();
                        }
                        $files[$attrs['role']][$file] = array(strtolower($channel),
                            strtolower($package));
                    } else {
                        if (!isset($files[$attrs['role']])) {
                            $files[$attrs['role']] = array();
                        }
                        $files[$attrs['role']][$file] = strtolower($package);
                    }
                }
            }
        }
        $this->_assertStateDir();
        if (!$this->hasWriteAccess()) {
            return false;
        }
        $fp = @fopen($this->filemap, 'wb');
        if (!$fp) {
            return false;
        }
        $this->filemap_cache = $files;
        fwrite($fp, serialize($files));
        fclose($fp);
        return true;
    }

    // }}}
    // {{{ _readFileMap()

    function _readFileMap()
    {
        if (!file_exists($this->filemap)) {
            return array();
        }
        $fp = @fopen($this->filemap, 'r');
        if (!$fp) {
            return $this->raiseError('PEAR_Registry: could not open filemap "' . $this->filemap . '"', PEAR_REGISTRY_ERROR_FILE, null, null, $php_errormsg);
        }
        clearstatcache();
        $rt = get_magic_quotes_runtime();
        set_magic_quotes_runtime(0);
        $fsize = filesize($this->filemap);
        fclose($fp);
        $data = file_get_contents($this->filemap);
        set_magic_quotes_runtime($rt);
        $tmp = unserialize($data);
        if (!$tmp && $fsize > 7) {
            return $this->raiseError('PEAR_Registry: invalid filemap data', PEAR_REGISTRY_ERROR_FORMAT, null, null, $data);
        }
        $this->filemap_cache = $tmp;
        return true;
    }

    // }}}
    // {{{ _lock()

    /**
     * Lock the registry.
     *
     * @param integer lock mode, one of LOCK_EX, LOCK_SH or LOCK_UN.
     *                See flock manual for more information.
     *
     * @return bool TRUE on success, FALSE if locking failed, or a
     *              PEAR error if some other error occurs (such as the
     *              lock file not being writable).
     *
     * @access private
     */
    function _lock($mode = LOCK_EX)
    {
        if (!eregi('Windows 9', php_uname())) {
            if ($mode != LOCK_UN && is_resource($this->lock_fp)) {
                // XXX does not check type of lock (LOCK_SH/LOCK_EX)
                return true;
            }
            if (!$this->_assertStateDir()) {
                if ($mode == LOCK_EX) {
                    return $this->raiseError('Registry directory is not writeable by the current user');
                } else {
                    return true;
                }
            }
            $open_mode = 'w';
            // XXX People reported problems with LOCK_SH and 'w'
            if ($mode === LOCK_SH || $mode === LOCK_UN) {
                if (!file_exists($this->lockfile)) {
                    touch($this->lockfile);
                }
                $open_mode = 'r';
            }

            if (!is_resource($this->lock_fp)) {
                $this->lock_fp = @fopen($this->lockfile, $open_mode);
            }

            if (!is_resource($this->lock_fp)) {
                $this->lock_fp = null;
                return $this->raiseError("could not create lock file" .
                                         (isset($php_errormsg) ? ": " . $php_errormsg : ""));
            }
            if (!(int)flock($this->lock_fp, $mode)) {
                switch ($mode) {
                    case LOCK_SH: $str = 'shared';    break;
                    case LOCK_EX: $str = 'exclusive'; break;
                    case LOCK_UN: $str = 'unlock';    break;
                    default:      $str = 'unknown';   break;
                }
                //is resource at this point, close it on error.
                fclose($this->lock_fp);
                $this->lock_fp = null;
                return $this->raiseError("could not acquire $str lock ($this->lockfile)",
                                         PEAR_REGISTRY_ERROR_LOCK);
            }
        }
        return true;
    }

    // }}}
    // {{{ _unlock()

    function _unlock()
    {
        $ret = $this->_lock(LOCK_UN);
        if (is_resource($this->lock_fp)) {
            fclose($this->lock_fp);
        }
        $this->lock_fp = null;
        return $ret;
    }

    // }}}
    // {{{ _packageExists()

    function _packageExists($package, $channel = false)
    {
        return file_exists($this->_packageFileName($package, $channel));
    }

    // }}}
    // {{{ _channelExists()

    /**
     * Determine whether a channel exists in the registry
     * @param string Channel name
     * @param bool if true, then aliases will be ignored
     * @return boolean
     */
    function _channelExists($channel, $noaliases = false)
    {
        $a = file_exists($this->_channelFileName($channel, $noaliases));
        if (!$a && $channel == 'pear.php.net') {
            return true;
        }
        if (!$a && $channel == 'pecl.php.net') {
            return true;
        }
        return $a;
    }

    // }}}
    // {{{ _addChannel()

    /**
     * @param PEAR_ChannelFile Channel object
     * @param donotuse
     * @param string Last-Modified HTTP tag from remote request
     * @return boolean|PEAR_Error True on creation, false if it already exists
     */
    function _addChannel($channel, $update = false, $lastmodified = false)
    {
        if (!is_a($channel, 'PEAR_ChannelFile')) {
            return false;
        }
        if (!$channel->validate()) {
            return false;
        }
        if (file_exists($this->_channelFileName($channel->getName()))) {
            if (!$update) {
                return false;
            }
            $checker = $this->_getChannel($channel->getName());
            if (PEAR::isError($checker)) {
                return $checker;
            }
            if ($channel->getAlias() != $checker->getAlias()) {
                if (file_exists($this->_getChannelAliasFileName($checker->getAlias()))) {
                    @unlink($this->_getChannelAliasFileName($checker->getAlias()));
                }
            }
        } else {
            if ($update && !in_array($channel->getName(), array('pear.php.net', 'pecl.php.net'))) {
                return false;
            }
        }
        $ret = $this->_assertChannelDir();
        if (PEAR::isError($ret)) {
            return $ret;
        }
        $ret = $this->_assertChannelStateDir($channel->getName());
        if (PEAR::isError($ret)) {
            return $ret;
        }
        if ($channel->getAlias() != $channel->getName()) {
            if (file_exists($this->_getChannelAliasFileName($channel->getAlias())) &&
                  $this->_getChannelFromAlias($channel->getAlias()) != $channel->getName()) {
                $channel->setAlias($channel->getName());
            }
            if (!$this->hasWriteAccess()) {
                return false;
            }
            $fp = @fopen($this->_getChannelAliasFileName($channel->getAlias()), 'w');
            if (!$fp) {
                return false;
            }
            fwrite($fp, $channel->getName());
            fclose($fp);
        }
        if (!$this->hasWriteAccess()) {
            return false;
        }
        $fp = @fopen($this->_channelFileName($channel->getName()), 'wb');
        if (!$fp) {
            return false;
        }
        $info = $channel->toArray();
        if ($lastmodified) {
            $info['_lastmodified'] = $lastmodified;
        } else {
            $info['_lastmodified'] = date('r');
        }
        fwrite($fp, serialize($info));
        fclose($fp);
        return true;
    }

    // }}}
    // {{{ _deleteChannel()

    /**
     * Deletion fails if there are any packages installed from the channel
     * @param string|PEAR_ChannelFile channel name
     * @return boolean|PEAR_Error True on deletion, false if it doesn't exist
     */
    function _deleteChannel($channel)
    {
        if (!is_string($channel)) {
            if (is_a($channel, 'PEAR_ChannelFile')) {
                if (!$channel->validate()) {
                    return false;
                }
                $channel = $channel->getName();
            } else {
                return false;
            }
        }
        if ($this->_getChannelFromAlias($channel) == '__uri') {
            return false;
        }
        if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') {
            return false;
        }
        if (!$this->_channelExists($channel)) {
            return false;
        }
        if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
            return false;
        }
        $channel = $this->_getChannelFromAlias($channel);
        if ($channel == 'pear.php.net') {
            return false;
        }
        $test = $this->_listChannelPackages($channel);
        if (count($test)) {
            return false;
        }
        $test = @rmdir($this->_channelDirectoryName($channel));
        if (!$test) {
            return false;
        }
        $file = $this->_getChannelAliasFileName($this->_getAlias($channel));
        if (file_exists($file)) {
            $test = @unlink($file);
            if (!$test) {
                return false;
            }
        }
        $file = $this->_channelFileName($channel);
        $ret = true;
        if (file_exists($file)) {
            $ret = @unlink($file);
        }
        return $ret;
    }

    // }}}
    // {{{ _isChannelAlias()

    /**
     * Determine whether a channel exists in the registry
     * @param string Channel Alias
     * @return boolean
     */
    function _isChannelAlias($alias)
    {
        return file_exists($this->_getChannelAliasFileName($alias));
    }

    // }}}
    // {{{ _packageInfo()

    /**
     * @param string|null
     * @param string|null
     * @param string|null
     * @return array|null
     * @access private
     */
    function _packageInfo($package = null, $key = null, $channel = 'pear.php.net')
    {
        if ($package === null) {
            if ($channel === null) {
                $channels = $this->_listChannels();
                $ret = array();
                foreach ($channels as $channel) {
                    $channel = strtolower($channel);
                    $ret[$channel] = array();
                    $packages = $this->_listPackages($channel);
                    foreach ($packages as $package) {
                        $ret[$channel][] = $this->_packageInfo($package, null, $channel);
                    }
                }
                return $ret;
            }
            $ps = $this->_listPackages($channel);
            if (!count($ps)) {
                return array();
            }
            return array_map(array(&$this, '_packageInfo'),
                             $ps, array_fill(0, count($ps), null),
                             array_fill(0, count($ps), $channel));
        }
        $fp = $this->_openPackageFile($package, 'r', $channel);
        if ($fp === null) {
            return null;
        }
        $rt = get_magic_quotes_runtime();
        set_magic_quotes_runtime(0);
        clearstatcache();
        $this->_closePackageFile($fp);
        $data = file_get_contents($this->_packageFileName($package, $channel));
        set_magic_quotes_runtime($rt);
        $data = unserialize($data);
        if ($key === null) {
            return $data;
        }
        // compatibility for package.xml version 2.0
        if (isset($data['old'][$key])) {
            return $data['old'][$key];
        }
        if (isset($data[$key])) {
            return $data[$key];
        }
        return null;
    }

    // }}}
    // {{{ _channelInfo()

    /**
     * @param string Channel name
     * @param bool whether to strictly retrieve info of channels, not just aliases
     * @return array|null
     */
    function _channelInfo($channel, $noaliases = false)
    {
        if (!$this->_channelExists($channel, $noaliases)) {
            return null;
        }
        $fp = $this->_openChannelFile($channel, 'r');
        if ($fp === null) {
            return null;
        }
        $rt = get_magic_quotes_runtime();
        set_magic_quotes_runtime(0);
        clearstatcache();
        $this->_closeChannelFile($fp);
        $data = file_get_contents($this->_channelFileName($channel));
        set_magic_quotes_runtime($rt);
        $data = unserialize($data);
        return $data;
    }

    // }}}
    // {{{ _listChannels()

    function _listChannels()
    {
        $channellist = array();
        if (!file_exists($this->channelsdir) || !is_dir($this->channelsdir)) {
            return array('pear.php.net', 'pecl.php.net', '__uri');
        }
        $dp = opendir($this->channelsdir);
        while ($ent = readdir($dp)) {
            if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
                continue;
            }
            if ($ent == '__uri.reg') {
                $channellist[] = '__uri';
                continue;
            }
            $channellist[] = str_replace('_', '/', substr($ent, 0, -4));
        }
        closedir($dp);
        if (!in_array('pear.php.net', $channellist)) {
            $channellist[] = 'pear.php.net';
        }
        if (!in_array('pecl.php.net', $channellist)) {
            $channellist[] = 'pecl.php.net';
        }
        if (!in_array('__uri', $channellist)) {
            $channellist[] = '__uri';
        } 
   
        natsort($channellist);
        return $channellist;
    }

    // }}}
    // {{{ _listPackages()

    function _listPackages($channel = false)
    {
        if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
            return $this->_listChannelPackages($channel);
        }
        if (!file_exists($this->statedir) || !is_dir($this->statedir)) {
            return array();
        }
        $pkglist = array();
        $dp = opendir($this->statedir);
        if (!$dp) {
            return $pkglist;
        }
        while ($ent = readdir($dp)) {
            if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
                continue;
            }
            $pkglist[] = substr($ent, 0, -4);
        }
        closedir($dp);
        return $pkglist;
    }

    // }}}
    // {{{ _listChannelPackages()

    function _listChannelPackages($channel)
    {
        $pkglist = array();
        if (!file_exists($this->_channelDirectoryName($channel)) ||
              !is_dir($this->_channelDirectoryName($channel))) {
            return array();
        }
        $dp = opendir($this->_channelDirectoryName($channel));
        if (!$dp) {
            return $pkglist;
        }
        while ($ent = readdir($dp)) {
            if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
                continue;
            }
            $pkglist[] = substr($ent, 0, -4);
        }
        closedir($dp);
        return $pkglist;
    }

    // }}}
    
    function _listAllPackages()
    {
        $ret = array();
        foreach ($this->_listChannels() as $channel) {
            $ret[$channel] = $this->_listPackages($channel);
        }
        return $ret;
    }

    /**
     * Add an installed package to the registry
     * @param string package name
     * @param array package info (parsed by PEAR_Common::infoFrom*() methods)
     * @return bool success of saving
     * @access private
     */
    function _addPackage($package, $info)
    {
        if ($this->_packageExists($package)) {
            return false;
        }
        $fp = $this->_openPackageFile($package, 'wb');
        if ($fp === null) {
            return false;
        }
        $info['_lastmodified'] = time();
        fwrite($fp, serialize($info));
        $this->_closePackageFile($fp);
        if (isset($info['filelist'])) {
            $this->_rebuildFileMap();
        }
        return true;
    }

    /**
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @return bool
     * @access private
     */
    function _addPackage2($info)
    {
        if (!is_a($info, 'PEAR_PackageFile_v1') && !is_a($info, 'PEAR_PackageFile_v2')) {
            return false;
        }

        if (!$info->validate()) {
            if (class_exists('PEAR_Common')) {
                $ui = PEAR_Frontend::singleton();
                if ($ui) {
                    foreach ($info->getValidationWarnings() as $err) {
                        $ui->log($err['message'], true);
                    }
                }
            }
            return false;
        }
        $channel = $info->getChannel();
        $package = $info->getPackage();
        $save = $info;
        if ($this->_packageExists($package, $channel)) {
            return false;
        }
        if (!$this->_channelExists($channel, true)) {
            return false;
        }
        $info = $info->toArray(true);
        if (!$info) {
            return false;
        }
        $fp = $this->_openPackageFile($package, 'wb', $channel);
        if ($fp === null) {
            return false;
        }
        $info['_lastmodified'] = time();
        fwrite($fp, serialize($info));
        $this->_closePackageFile($fp);
        $this->_rebuildFileMap();
        return true;
    }

    /**
     * @param string Package name
     * @param array parsed package.xml 1.0
     * @param bool this parameter is only here for BC.  Don't use it.
     * @access private
     */
    function _updatePackage($package, $info, $merge = true)
    {
        $oldinfo = $this->_packageInfo($package);
        if (empty($oldinfo)) {
            return false;
        }
        $fp = $this->_openPackageFile($package, 'w');
        if ($fp === null) {
            return false;
        }
        if (is_object($info)) {
            $info = $info->toArray();
        }
        $info['_lastmodified'] = time();
        $newinfo = $info;
        if ($merge) {
            $info = array_merge($oldinfo, $info);
        } else {
            $diff = $info;
        }
        fwrite($fp, serialize($info));
        $this->_closePackageFile($fp);
        if (isset($newinfo['filelist'])) {
            $this->_rebuildFileMap();
        }
        return true;
    }

    /**
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @return bool
     * @access private
     */
    function _updatePackage2($info)
    {
        if (!$this->_packageExists($info->getPackage(), $info->getChannel())) {
            return false;
        }
        $fp = $this->_openPackageFile($info->getPackage(), 'w', $info->getChannel());
        if ($fp === null) {
            return false;
        }
        $save = $info;
        $info = $save->getArray(true);
        $info['_lastmodified'] = time();
        fwrite($fp, serialize($info));
        $this->_closePackageFile($fp);
        $this->_rebuildFileMap();
        return true;
    }

    /**
     * @param string Package name
     * @param string Channel name
     * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null
     * @access private
     */
    function &_getPackage($package, $channel = 'pear.php.net')
    {
        $info = $this->_packageInfo($package, null, $channel);
        if ($info === null) {
            return $info;
        }
        $a = $this->_config;
        if (!$a) {
            $this->_config = &new PEAR_Config;
            $this->_config->set('php_dir', $this->statedir);
        }
        if (!class_exists('PEAR_PackageFile')) {
            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile.php';
        }
        $pkg = &new PEAR_PackageFile($this->_config);
        $pf = &$pkg->fromArray($info);
        return $pf;
    }

    /**
     * @param string channel name
     * @param bool whether to strictly retrieve channel names
     * @return PEAR_ChannelFile|PEAR_Error
     * @access private
     */
    function &_getChannel($channel, $noaliases = false)
    {
        $ch = false;
        if ($this->_channelExists($channel, $noaliases)) {
            $chinfo = $this->_channelInfo($channel, $noaliases);
            if ($chinfo) {
                if (!class_exists('PEAR_ChannelFile')) {
                    require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
                }
                $ch = &PEAR_ChannelFile::fromArrayWithErrors($chinfo);
            }
        }
        if ($ch) {
            if ($ch->validate()) {
                return $ch;
            }
            foreach ($ch->getErrors(true) as $err) {
                $message = $err['message'] . "\n";
            }
            $ch = PEAR::raiseError($message);
            return $ch;
        }
        if ($this->_getChannelFromAlias($channel) == 'pear.php.net') {
            // the registry is not properly set up, so use defaults
            if (!class_exists('PEAR_ChannelFile')) {
                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
            }
            $pear_channel = new PEAR_ChannelFile;
            $pear_channel->setName('pear.php.net');
            $pear_channel->setAlias('pear');
            $pear_channel->setSummary('PHP Extension and Application Repository');
            $pear_channel->setDefaultPEARProtocols();
            $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/');
            $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/');
            return $pear_channel;
        }
        if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') {
            // the registry is not properly set up, so use defaults
            if (!class_exists('PEAR_ChannelFile')) {
                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
            }
            $pear_channel = new PEAR_ChannelFile;
            $pear_channel->setName('pecl.php.net');
            $pear_channel->setAlias('pecl');
            $pear_channel->setSummary('PHP Extension Community Library');
            $pear_channel->setDefaultPEARProtocols();
            $pear_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/');
            $pear_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/');
            $pear_channel->setValidationPackage('PEAR_Validator_PECL', '1.0');
            return $pear_channel;
        }
        if ($this->_getChannelFromAlias($channel) == '__uri') {
            // the registry is not properly set up, so use defaults
            if (!class_exists('PEAR_ChannelFile')) {
                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
            }
            $private = new PEAR_ChannelFile;
            $private->setName('__uri');
            $private->addFunction('xmlrpc', '1.0', '****');
            $private->setSummary('Pseudo-channel for static packages');
            return $private;
        }
        return $ch;
    }

    // {{{ packageExists()

    /**
     * @param string Package name
     * @param string Channel name
     * @return bool
     */
    function packageExists($package, $channel = 'pear.php.net')
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $ret = $this->_packageExists($package, $channel);
        $this->_unlock();
        return $ret;
    }

    // }}}

    // {{{ channelExists()

    /**
     * @param string channel name
     * @param bool if true, then aliases will be ignored
     * @return bool
     */
    function channelExists($channel, $noaliases = false)
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $ret = $this->_channelExists($channel, $noaliases);
        $this->_unlock();
        return $ret;
    }

    // }}}

    // {{{ isAlias()

    /**
     * Determines whether the parameter is an alias of a channel
     * @param string
     * @return bool
     */
    function isAlias($alias)
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $ret = $this->_isChannelAlias($alias);
        $this->_unlock();
        return $ret;
    }

    // }}}
    // {{{ packageInfo()

    /**
     * @param string|null
     * @param string|null
     * @param string
     * @return array|null
     */
    function packageInfo($package = null, $key = null, $channel = 'pear.php.net')
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $ret = $this->_packageInfo($package, $key, $channel);
        $this->_unlock();
        return $ret;
    }

    // }}}
    // {{{ channelInfo()

    /**
     * Retrieve a raw array of channel data.
     *
     * Do not use this, instead use {@link getChannel()} for normal
     * operations.  Array structure is undefined in this method
     * @param string channel name
     * @param bool whether to strictly retrieve information only on non-aliases
     * @return array|null|PEAR_Error
     */
    function channelInfo($channel = null, $noaliases = false)
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $ret = $this->_channelInfo($channel, $noaliases);
        $this->_unlock();
        return $ret;
    }

    // }}}

    /**
     * @param string
     */
    function channelName($channel)
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $ret = $this->_getChannelFromAlias($channel);
        $this->_unlock();
        return $ret;
    }

    /**
     * @param string
     */
    function channelAlias($channel)
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $ret = $this->_getAlias($channel);
        $this->_unlock();
        return $ret;
    }
    // {{{ listPackages()

    function listPackages($channel = false)
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $ret = $this->_listPackages($channel);
        $this->_unlock();
        return $ret;
    }

    // }}}
    // {{{ listAllPackages()

    function listAllPackages()
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $ret = $this->_listAllPackages();
        $this->_unlock();
        return $ret;
    }

    // }}}
    // {{{ listChannel()

    function listChannels()
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $ret = $this->_listChannels();
        $this->_unlock();
        return $ret;
    }

    // }}}
    // {{{ addPackage()

    /**
     * Add an installed package to the registry
     * @param string|PEAR_PackageFile_v1|PEAR_PackageFile_v2 package name or object
     *               that will be passed to {@link addPackage2()}
     * @param array package info (parsed by PEAR_Common::infoFrom*() methods)
     * @return bool success of saving
     */
    function addPackage($package, $info)
    {
        if (is_object($info)) {
            return $this->addPackage2($info);
        }
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
            return $e;
        }
        $ret = $this->_addPackage($package, $info);
        $this->_unlock();
        if ($ret) {
            if (!class_exists('PEAR_PackageFile_v1')) {
                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v1.php';
            }
            $pf = new PEAR_PackageFile_v1;
            $pf->setConfig($this->_config);
            $pf->fromArray($info);
            $this->_dependencyDB->uninstallPackage($pf);
            $this->_dependencyDB->installPackage($pf);
        }
        return $ret;
    }

    // }}}
    // {{{ addPackage2()

    function addPackage2($info)
    {
        if (!is_object($info)) {
            return $this->addPackage($info['package'], $info);
        }
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
            return $e;
        }
        $ret = $this->_addPackage2($info);
        $this->_unlock();
        if ($ret) {
            $this->_dependencyDB->uninstallPackage($info);
            $this->_dependencyDB->installPackage($info);
        }
        return $ret;
    }

    // }}}
    // {{{ updateChannel()

    /**
     * For future expandibility purposes, separate this
     * @param PEAR_ChannelFile
     */
    function updateChannel($channel, $lastmodified = null)
    {
        if ($channel->getName() == '__uri') {
            return false;
        }
        return $this->addChannel($channel, $lastmodified, true);
    }

    // }}}
    // {{{ deleteChannel()

    /**
     * Deletion fails if there are any packages installed from the channel
     * @param string|PEAR_ChannelFile channel name
     * @return boolean|PEAR_Error True on deletion, false if it doesn't exist
     */
    function deleteChannel($channel)
    {
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
            return $e;
        }
        $ret = $this->_deleteChannel($channel);
        $this->_unlock();
        if ($ret && is_a($this->_config, 'PEAR_Config')) {
            $this->_config->setChannels($this->listChannels());
        }
        return $ret;
    }

    // }}}
    // {{{ addChannel()

    /**
     * @param PEAR_ChannelFile Channel object
     * @param string Last-Modified header from HTTP for caching
     * @return boolean|PEAR_Error True on creation, false if it already exists
     */
    function addChannel($channel, $lastmodified = false, $update = false)
    {
        if (!is_a($channel, 'PEAR_ChannelFile')) {
            return false;
        }
        if (!$channel->validate()) {
            return false;
        }
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
            return $e;
        }
        $ret = $this->_addChannel($channel, $update, $lastmodified);
        $this->_unlock();
        if (!$update && $ret && is_a($this->_config, 'PEAR_Config')) {
            $this->_config->setChannels($this->listChannels());
        }
        return $ret;
    }

    // }}}
    // {{{ deletePackage()

    function deletePackage($package, $channel = 'pear.php.net')
    {
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
            return $e;
        }
        $file = $this->_packageFileName($package, $channel);
        if (file_exists($file)) {
            $ret = @unlink($file);
        } else {
            $ret = false;
        }
        $this->_rebuildFileMap();
        $this->_unlock();
        $p = array('channel' => $channel, 'package' => $package);
        $this->_dependencyDB->uninstallPackage($p);
        return $ret;
    }

    // }}}
    // {{{ updatePackage()

    function updatePackage($package, $info, $merge = true)
    {
        if (is_object($info)) {
            return $this->updatePackage2($info, $merge);
        }
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
            return $e;
        }
        $ret = $this->_updatePackage($package, $info, $merge);
        $this->_unlock();
        if ($ret) {
            if (!class_exists('PEAR_PackageFile_v1')) {
                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v1.php';
            }
            $pf = new PEAR_PackageFile_v1;
            $pf->setConfig($this->_config);
            $pf->fromArray($this->packageInfo($package));
            $this->_dependencyDB->uninstallPackage($pf);
            $this->_dependencyDB->installPackage($pf);
        }
        return $ret;
    }

    // }}}
    // {{{ updatePackage2()

    function updatePackage2($info)
    {
        if (!is_object($info)) {
            return $this->updatePackage($info['package'], $info, $merge);
        }
        if (!$info->validate(PEAR_VALIDATE_DOWNLOADING)) {
            return false;
        }
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
            return $e;
        }
        $ret = $this->_updatePackage2($info);
        $this->_unlock();
        if ($ret) {
            $this->_dependencyDB->uninstallPackage($info);
            $this->_dependencyDB->installPackage($info);
        }
        return $ret;
    }

    // }}}
    // {{{ getChannel()
    /**
     * @param string channel name
     * @param bool whether to strictly return raw channels (no aliases)
     * @return PEAR_ChannelFile|PEAR_Error
     */
    function &getChannel($channel, $noaliases = false)
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $ret = &$this->_getChannel($channel, $noaliases);
        $this->_unlock();
        if (!$ret) {
            return PEAR::raiseError('Unknown channel: ' . $channel);
        }
        return $ret;
    }

    // }}}
    // {{{ getPackage()
    /**
     * @param string package name
     * @param string channel name
     * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null
     */
    function &getPackage($package, $channel = 'pear.php.net')
    {
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        $pf = &$this->_getPackage($package, $channel);
        $this->_unlock();
        return $pf;
    }

    // }}}

    /**
     * Get PEAR_PackageFile_v[1/2] objects representing the contents of
     * a dependency group that are installed.
     *
     * This is used at uninstall-time
     * @param array
     * @return array|false
     */
    function getInstalledGroup($group)
    {
        $ret = array();
        if (isset($group['package'])) {
            if (!isset($group['package'][0])) {
                $group['package'] = array($group['package']);
            }
            foreach ($group['package'] as $package) {
                $depchannel = isset($package['channel']) ? $package['channel'] : '__uri';
                $p = &$this->getPackage($package['name'], $depchannel);
                if ($p) {
                    $save = &$p;
                    $ret[] = &$save;
                }
            }
        }
        if (isset($group['subpackage'])) {
            if (!isset($group['subpackage'][0])) {
                $group['subpackage'] = array($group['subpackage']);
            }
            foreach ($group['subpackage'] as $package) {
                $depchannel = isset($package['channel']) ? $package['channel'] : '__uri';
                $p = &$this->getPackage($package['name'], $depchannel);
                if ($p) {
                    $save = &$p;
                    $ret[] = &$save;
                }
            }
        }
        if (!count($ret)) {
            return false;
        }
        return $ret;
    }

    // {{{ getChannelValidator()
    /**
     * @param string channel name
     * @return PEAR_Validate|false
     */
    function &getChannelValidator($channel)
    {
        $chan = $this->getChannel($channel);
        if (PEAR::isError($chan)) {
            return $chan;
        }
        $val = $chan->getValidationObject();
        return $val;
    }
    // }}}
    // {{{ getChannels()
    /**
     * @param string channel name
     * @return array an array of PEAR_ChannelFile objects representing every installed channel
     */
    function &getChannels()
    {
        $ret = array();
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
            return $e;
        }
        foreach ($this->_listChannels() as $channel) {
            $e = &$this->_getChannel($channel);
            if (!$e || PEAR::isError($e)) {
                continue;
            }
            $ret[] = $e;
        }
        $this->_unlock();
        return $ret;
    }

    // }}}
    // {{{ checkFileMap()

    /**
     * Test whether a file or set of files belongs to a package.
     *
     * If an array is passed in
     * @param string|array file path, absolute or relative to the pear
     *                     install dir
     * @param string|array name of PEAR package or array('package' => name, 'channel' =>
     *                     channel) of a package that will be ignored
     * @param string API version - 1.1 will exclude any files belonging to a package
     * @param array private recursion variable
     * @return array|false which package and channel the file belongs to, or an empty
     *                     string if the file does not belong to an installed package,
     *                     or belongs to the second parameter's package
     */
    function checkFileMap($path, $package = false, $api = '1.0', $attrs = false)
    {
        if (is_array($path)) {
            static $notempty;
            if (empty($notempty)) {
                if (!class_exists('PEAR_Installer_Role')) {
                    require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Installer/Role.php';
                }
                $notempty = create_function('$a','return !empty($a);');
            }
            $package = is_array($package) ? array(strtolower($package[0]), strtolower($package[1]))
                : strtolower($package);
            $pkgs = array();
            foreach ($path as $name => $attrs) {
                if (is_array($attrs)) {
                    if (isset($attrs['install-as'])) {
                        $name = $attrs['install-as'];
                    }
                    if (!in_array($attrs['role'], PEAR_Installer_Role::getInstallableRoles())) {
                        // these are not installed
                        continue;
                    }
                    if (!in_array($attrs['role'], PEAR_Installer_Role::getBaseinstallRoles())) {
                        $attrs['baseinstalldir'] = is_array($package) ? $package[1] : $package;
                    }
                    if (isset($attrs['baseinstalldir'])) {
                        $name = $attrs['baseinstalldir'] . DIRECTORY_SEPARATOR . $name;
                    }
                }
                $pkgs[$name] = $this->checkFileMap($name, $package, $api, $attrs);
                if (PEAR::isError($pkgs[$name])) {
                    return $pkgs[$name];
                }
            }
            return array_filter($pkgs, $notempty);
        }
        if (empty($this->filemap_cache)) {
            if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
                return $e;
            }
            $err = $this->_readFileMap();
            $this->_unlock();
            if (PEAR::isError($err)) {
                return $err;
            }
        }
        if (!$attrs) {
            $attrs = array('role' => 'php'); // any old call would be for PHP role only
        }
        if (isset($this->filemap_cache[$attrs['role']][$path])) {
            if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) {
                return false;
            }
            return $this->filemap_cache[$attrs['role']][$path];
        }
        $l = strlen($this->install_dir);
        if (substr($path, 0, $l) == $this->install_dir) {
            $path = preg_replace('!^'.DIRECTORY_SEPARATOR.'+!', '', substr($path, $l));
        }
        if (isset($this->filemap_cache[$attrs['role']][$path])) {
            if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) {
                return false;
            }
            return $this->filemap_cache[$attrs['role']][$path];
        }
        return false;
    }

    // }}}
    // {{{ flush()
    /**
     * Force a reload of the filemap
     * @since 1.5.0RC3
     */
    function flushFileMap()
    {
        $this->filemap_cache = null;
        clearstatcache(); // ensure that the next read gets the full, current filemap
    }

    // }}}
    // {{{ apiVersion()
    /**
     * Get the expected API version.  Channels API is version 1.1, as it is backwards
     * compatible with 1.0
     * @return string
     */
    function apiVersion()
    {
        return '1.1';
    }
    // }}}


    /**
     * Parse a package name, or validate a parsed package name array
     * @param string|array pass in an array of format
     *                     array(
     *                      'package' => 'pname',
     *                     ['channel' => 'channame',]
     *                     ['version' => 'version',]
     *                     ['state' => 'state',]
     *                     ['group' => 'groupname'])
     *                     or a string of format
     *                     [channel://][channame/]pname[-version|-state][/group=groupname]
     * @return array|PEAR_Error
     */
    function parsePackageName($param, $defaultchannel = 'pear.php.net')
    {
        $saveparam = $param;
        if (is_array($param)) {
            // convert to string for error messages
            $saveparam = $this->parsedPackageNameToString($param);
            // process the array
            if (!isset($param['package'])) {
                return PEAR::raiseError('parsePackageName(): array $param ' .
                    'must contain a valid package name in index "param"',
                    'package', null, null, $param);
            }
            if (!isset($param['uri'])) {
                if (!isset($param['channel'])) {
                    $param['channel'] = $defaultchannel;
                }
            } else {
                $param['channel'] = '__uri';
            }
        } else {
            $components = @parse_url((string) $param);
            if (isset($components['scheme'])) {
                if ($components['scheme'] == 'http') {
                    // uri package
                    $param = array('uri' => $param, 'channel' => '__uri');
                } elseif($components['scheme'] != 'channel') {
                    return PEAR::raiseError('parsePackageName(): only channel:// uris may ' .
                        'be downloaded, not "' . $param . '"', 'invalid', null, null, $param);
                }
            }
            if (!isset($components['path'])) {
                return PEAR::raiseError('parsePackageName(): array $param ' .
                    'must contain a valid package name in "' . $param . '"',
                    'package', null, null, $param);
            }
            if (isset($components['host'])) {
                // remove the leading "/"
                $components['path'] = substr($components['path'], 1);
            }
            if (!isset($components['scheme'])) {
                if (strpos($components['path'], '/') !== false) {
                    if ($components['path']{0} == '/') {
                        return PEAR::raiseError('parsePackageName(): this is not ' .
                            'a package name, it begins with "/" in "' . $param . '"',
                            'invalid', null, null, $param);
                    }
                    $parts = explode('/', $components['path']);
                    $components['host'] = array_shift($parts);
                    if (count($parts) > 1) {
                        $components['path'] = array_pop($parts);
                        $components['host'] .= '/' . implode('/', $parts);
                    } else {
                        $components['path'] = implode('/', $parts);
                    }
                } else {
                    $components['host'] = $defaultchannel;
                }
            } else {
                if (strpos($components['path'], '/')) {
                    $parts = explode('/', $components['path']);
                    $components['path'] = array_pop($parts);
                    $components['host'] .= '/' . implode('/', $parts);
                }
            }

            if (is_array($param)) {
                $param['package'] = $components['path'];
            } else {
                $param = array(
                    'package' => $components['path']
                    );
                if (isset($components['host'])) {
                    $param['channel'] = $components['host'];
                }
            }
            if (isset($components['fragment'])) {
                $param['group'] = $components['fragment'];
            }
            if (isset($components['user'])) {
                $param['user'] = $components['user'];
            }
            if (isset($components['pass'])) {
                $param['pass'] = $components['pass'];
            }
            if (isset($components['query'])) {
                parse_str($components['query'], $param['opts']);
            }
            // check for extension
            $pathinfo = pathinfo($param['package']);
            if (isset($pathinfo['extension']) &&
                  in_array(strtolower($pathinfo['extension']), array('tgz', 'tar'))) {
                $param['extension'] = $pathinfo['extension'];
                $param['package'] = substr($pathinfo['basename'], 0,
                    strlen($pathinfo['basename']) - 4);
            }
            // check for version
            if (strpos($param['package'], '-')) {
                $test = explode('-', $param['package']);
                if (count($test) != 2) {
                    return PEAR::raiseError('parsePackageName(): only one version/state ' .
                        'delimiter "-" is allowed in "' . $saveparam . '"',
                        'version', null, null, $param);
                }
                list($param['package'], $param['version']) = $test;
            }
        }
        // validation
        $info = $this->channelExists($param['channel']);
        if (PEAR::isError($info)) {
            return $info;
        }
        if (!$info) {
            return PEAR::raiseError('unknown channel "' . $param['channel'] .
                '" in "' . $saveparam . '"', 'channel', null, null, $param);
        }
        $chan = $this->getChannel($param['channel']);
        if (PEAR::isError($chan)) {
            return $chan;
        }
        if (!$chan) {
            return PEAR::raiseError("Exception: corrupt registry, could not " .
                "retrieve channel " . $param['channel'] . " information",
                'registry', null, null, $param);
        }
        $param['channel'] = $chan->getName();
        $validate = $chan->getValidationObject();
        $vpackage = $chan->getValidationPackage();
        // validate package name
        if (!$validate->validPackageName($param['package'], $vpackage['_content'])) {
            return PEAR::raiseError('parsePackageName(): invalid package name "' .
                $param['package'] . '" in "' . $saveparam . '"',
                'package', null, null, $param);
        }
        if (isset($param['group'])) {
            if (!PEAR_Validate::validGroupName($param['group'])) {
                return PEAR::raiseError('parsePackageName(): dependency group "' . $param['group'] .
                    '" is not a valid group name in "' . $saveparam . '"', 'group', null, null,
                    $param);
            }
        }
        if (isset($param['state'])) {
            if (!in_array(strtolower($param['state']), $validate->getValidStates())) {
                return PEAR::raiseError('parsePackageName(): state "' . $param['state']
                    . '" is not a valid state in "' . $saveparam . '"',
                    'state', null, null, $param);
            }
        }
        if (isset($param['version'])) {
            if (isset($param['state'])) {
                return PEAR::raiseError('parsePackageName(): cannot contain both ' .
                    'a version and a stability (state) in "' . $saveparam . '"',
                    'version/state', null, null, $param);
            }
            // check whether version is actually a state
            if (in_array(strtolower($param['version']), $validate->getValidStates())) {
                $param['state'] = strtolower($param['version']);
                unset($param['version']);
            } else {
                if (!$validate->validVersion($param['version'])) {
                    return PEAR::raiseError('parsePackageName(): "' . $param['version'] .
                        '" is neither a valid version nor a valid state in "' .
                        $saveparam . '"', 'version/state', null, null, $param);
                }                    
            }
        }
        return $param;
    }

    /**
     * @param array
     * @return string
     */
    function parsedPackageNameToString($parsed, $brief = false)
    {
        if (is_string($parsed)) {
            return $parsed;
        }
        if (is_object($parsed)) {
            $p = $parsed;
            $parsed = array(
                'package' => $p->getPackage(),
                'channel' => $p->getChannel(),
                'version' => $p->getVersion(),
            );
        }
        if (isset($parsed['uri'])) {
            return $parsed['uri'];
        }
        if ($brief) {
            if ($channel = $this->channelAlias($parsed['channel'])) {
                return $channel . '/' . $parsed['package'];
            }
        }
        $upass = '';
        if (isset($parsed['user'])) {
            $upass = $parsed['user'];
            if (isset($parsed['pass'])) {
                $upass .= ':' . $parsed['pass'];
            }
            $upass = "$upass@";
        }
        $ret = 'channel://' . $upass . $parsed['channel'] . '/' . $parsed['package'];
        if (isset($parsed['version']) || isset($parsed['state'])) {
            $ver = isset($parsed['version']) ? $parsed['version'] : '';
            $ver .= isset($parsed['state']) ? $parsed['state'] : '';
            $ret .= '-' . $ver;
        }
        if (isset($parsed['extension'])) {
            $ret .= '.' . $parsed['extension'];
        }
        if (isset($parsed['opts'])) {
            $ret .= '?';
            foreach ($parsed['opts'] as $name => $value) {
                $parsed['opts'][$name] = "$name=$value";
            }
            $ret .= implode('&', $parsed['opts']);
        }
        if (isset($parsed['group'])) {
            $ret .= '#' . $parsed['group'];
        }
        return $ret;
    }
}

?>
<?php
/**
 * PEAR_Remote
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Remote.php,v 1.80 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * needed for PEAR_Error
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Config.php';

/**
 * This is a class for doing remote operations against the central
 * PEAR database.
 *
 * @nodep XML_RPC_Value
 * @nodep XML_RPC_Message
 * @nodep XML_RPC_Client
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 0.1
 */
class PEAR_Remote extends PEAR
{
    // {{{ properties

    var $config = null;
    var $cache  = null;
    /**
     * @var PEAR_Registry
     * @access private
     */
    var $_registry;

    // }}}

    // {{{ PEAR_Remote(config_object)

    function PEAR_Remote(&$config)
    {
        $this->PEAR();
        $this->config = &$config;
        $this->_registry = &$this->config->getRegistry();
    }

    // }}}
    // {{{ setRegistry()
    
    function setRegistry(&$reg)
    {
        $this->_registry = &$reg;
    }
    // }}}
    // {{{ getCache()


    function getCache($args)
    {
        $id       = md5(serialize($args));
        $cachedir = $this->config->get('cache_dir');
        $filename = $cachedir . DIRECTORY_SEPARATOR . 'xmlrpc_cache_' . $id;
        if (!file_exists($filename)) {
            return null;
        }

        $fp = fopen($filename, 'rb');
        if (!$fp) {
            return null;
        }
        fclose($fp);
        $content = file_get_contents($filename);
        $result   = array(
            'age'        => time() - filemtime($filename),
            'lastChange' => filemtime($filename),
            'content'    => unserialize($content),
            );
        return $result;
    }

    // }}}

    // {{{ saveCache()

    function saveCache($args, $data)
    {
        $id       = md5(serialize($args));
        $cachedir = $this->config->get('cache_dir');
        if (!file_exists($cachedir)) {
            System::mkdir(array('-p', $cachedir));
        }
        $filename = $cachedir.'/xmlrpc_cache_'.$id;

        $fp = @fopen($filename, "wb");
        if ($fp) {
            fwrite($fp, serialize($data));
            fclose($fp);
        }
    }

    // }}}

    // {{{ clearCache()

    function clearCache($method, $args)
    {
        array_unshift($args, $method);
        array_unshift($args, $this->config->get('default_channel')); // cache by channel
        $id       = md5(serialize($args));
        $cachedir = $this->config->get('cache_dir');
        $filename = $cachedir.'/xmlrpc_cache_'.$id;
        if (file_exists($filename)) {
            @unlink($filename);
        }
    }

    // }}}
    // {{{ call(method, [args...])

    function call($method)
    {
        $_args = $args = func_get_args();

        $server_channel = $this->config->get('default_channel');
        $channel = $this->_registry->getChannel($server_channel);
        if (!PEAR::isError($channel)) {
            $mirror = $this->config->get('preferred_mirror');
            if ($channel->getMirror($mirror)) {
                if ($channel->supports('xmlrpc', $method, $mirror)) {
                    $server_channel = $server_host = $mirror; // use the preferred mirror
                    $server_port = $channel->getPort($mirror);
                } elseif (!$channel->supports('xmlrpc', $method)) {
                    return $this->raiseError("Channel $server_channel does not " .
                        "support xml-rpc method $method");
                }
            }
            if (!isset($server_host)) {
                if (!$channel->supports('xmlrpc', $method)) {
                    return $this->raiseError("Channel $server_channel does not support " .
                        "xml-rpc method $method");
                } else {
                    $server_host = $server_channel;
                    $server_port = $channel->getPort();
                }
            }
        } else {
            return $this->raiseError("Unknown channel '$server_channel'");
        }

        array_unshift($_args, $server_channel); // cache by channel
        $this->cache = $this->getCache($_args);
        $cachettl = $this->config->get('cache_ttl');
        // If cache is newer than $cachettl seconds, we use the cache!
        if ($this->cache !== null && $this->cache['age'] < $cachettl) {
            return $this->cache['content'];
        }
        $fp = false;
        if (extension_loaded("xmlrpc")) {
            $result = call_user_func_array(array(&$this, 'call_epi'), $args);
            if (!PEAR::isError($result)) {
                $this->saveCache($_args, $result);
            }
            return $result;
        } elseif (!($fp = fopen('XML/RPC.php', 'r', true))) {
            return $this->raiseError("For this remote PEAR operation you need to load the xmlrpc extension or install XML_RPC");
        }
        include_once 'phar://install-pear-nozlib.phar/' . 'XML/RPC.php';
        if ($fp) {
            fclose($fp);
        }

        array_shift($args);
        $username = $this->config->get('username');
        $password = $this->config->get('password');
        $eargs = array();
        foreach($args as $arg) {
            $eargs[] = $this->_encode($arg);
        }
        $f = new XML_RPC_Message($method, $eargs);
        if ($this->cache !== null) {
            $maxAge = '?maxAge='.$this->cache['lastChange'];
        } else {
            $maxAge = '';
        }
        $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
        if ($proxy = parse_url($this->config->get('http_proxy'))) {
            $proxy_host = isset($proxy['host']) ? $proxy['host'] : null;
            if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') {
                $proxy_host = 'https://' . $proxy_host;
            }
            $proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080;
            $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null;
            $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null;
        }
        $shost = $server_host;
        if ($channel->getSSL()) {
            $shost = "https://$shost";
        }
        $c = new XML_RPC_Client('/' . $channel->getPath('xmlrpc')
            . $maxAge, $shost, $server_port, $proxy_host, $proxy_port,
            $proxy_user, $proxy_pass);
        if ($username && $password) {
            $c->setCredentials($username, $password);
        }
        if ($this->config->get('verbose') >= 3) {
            $c->setDebug(1);
        }
        $r = $c->send($f);
        if (!$r) {
            return $this->raiseError("XML_RPC send failed");
        }
        $v = $r->value();
        if ($e = $r->faultCode()) {
            if ($e == $GLOBALS['XML_RPC_err']['http_error'] && strstr($r->faultString(), '304 Not Modified') !== false) {
                return $this->cache['content'];
            }
            return $this->raiseError($r->faultString(), $e);
        }

        $result = XML_RPC_decode($v);
        $this->saveCache($_args, $result);
        return $result;
    }

    // }}}

    // {{{ call_epi(method, [args...])

    function call_epi($method)
    {
        if (!extension_loaded("xmlrpc")) {
            return $this->raiseError("xmlrpc extension is not loaded");
        }
        $server_channel = $this->config->get('default_channel');
        $channel = $this->_registry->getChannel($server_channel);
        if (!PEAR::isError($channel)) {
            $mirror = $this->config->get('preferred_mirror');
            if ($channel->getMirror($mirror)) {
                if ($channel->supports('xmlrpc', $method, $mirror)) {
                    $server_channel = $server_host = $mirror; // use the preferred mirror
                    $server_port = $channel->getPort($mirror);
                } elseif (!$channel->supports('xmlrpc', $method)) {
                    return $this->raiseError("Channel $server_channel does not " .
                        "support xml-rpc method $method");
                }
            }
            if (!isset($server_host)) {
                if (!$channel->supports('xmlrpc', $method)) {
                    return $this->raiseError("Channel $server_channel does not support " .
                        "xml-rpc method $method");
                } else {
                    $server_host = $server_channel;
                    $server_port = $channel->getPort();
                }
            }
        } else {
            return $this->raiseError("Unknown channel '$server_channel'");
        }
        $params = func_get_args();
        array_shift($params);
        $method = str_replace("_", ".", $method);
        $request = xmlrpc_encode_request($method, $params);
        if ($http_proxy = $this->config->get('http_proxy')) {
            $proxy = parse_url($http_proxy);
            $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
            $proxy_host = isset($proxy['host']) ? $proxy['host'] : null;
            if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') {
                $proxy_host = 'https://' . $proxy_host;
            }
            $proxy_port = isset($proxy['port']) ? $proxy['port'] : null;
            $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null;
            $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null;
            $fp = @fsockopen($proxy_host, $proxy_port);
            $use_proxy = true;
            if ($channel->getSSL()) {
                $server_host = "https://$server_host";
            }
        } else {
            $use_proxy = false;
            $ssl = $channel->getSSL();
            $fp = @fsockopen(($ssl ? 'ssl://' : '') . $server_host, $server_port);
            if (!$fp) {
                $server_host = "$ssl$server_host"; // for error-reporting
            }
        }
        if (!$fp && $http_proxy) {
            return $this->raiseError("PEAR_Remote::call: fsockopen(`$proxy_host', $proxy_port) failed");
        } elseif (!$fp) {
            return $this->raiseError("PEAR_Remote::call: fsockopen(`$server_host', $server_port) failed");
        }
        $len = strlen($request);
        $req_headers = "Host: $server_host:$server_port\r\n" .
             "Content-type: text/xml\r\n" .
             "Content-length: $len\r\n";
        $username = $this->config->get('username');
        $password = $this->config->get('password');
        if ($username && $password) {
            $req_headers .= "Cookie: PEAR_USER=$username; PEAR_PW=$password\r\n";
            $tmp = base64_encode("$username:$password");
            $req_headers .= "Authorization: Basic $tmp\r\n";
        }
        if ($this->cache !== null) {
            $maxAge = '?maxAge='.$this->cache['lastChange'];
        } else {
            $maxAge = '';
        }

        if ($use_proxy && $proxy_host != '' && $proxy_user != '') {
            $req_headers .= 'Proxy-Authorization: Basic '
                .base64_encode($proxy_user.':'.$proxy_pass)
                ."\r\n";
        }

        if ($this->config->get('verbose') > 3) {
            print "XMLRPC REQUEST HEADERS:\n";
            var_dump($req_headers);
            print "XMLRPC REQUEST BODY:\n";
            var_dump($request);
        }

        if ($use_proxy && $proxy_host != '') {
            $post_string = "POST http://".$server_host;
            if ($proxy_port > '') {
                $post_string .= ':'.$server_port;
            }
        } else {
            $post_string = "POST ";
        }

        $path = '/' . $channel->getPath('xmlrpc');
        fwrite($fp, ($post_string . $path . "$maxAge HTTP/1.0\r\n$req_headers\r\n$request"));
        $response = '';
        $line1 = fgets($fp, 2048);
        if (!preg_match('!^HTTP/[0-9\.]+ (\d+) (.*)!', $line1, $matches)) {
            return $this->raiseError("PEAR_Remote: invalid HTTP response from XML-RPC server");
        }
        switch ($matches[1]) {
            case "200": // OK
                break;
            case "304": // Not Modified
                return $this->cache['content'];
            case "401": // Unauthorized
                if ($username && $password) {
                    return $this->raiseError("PEAR_Remote ($server_host:$server_port) " .
                        ": authorization failed", 401);
                } else {
                    return $this->raiseError("PEAR_Remote ($server_host:$server_port) " .
                        ": authorization required, please log in first", 401);
                }
            default:
                return $this->raiseError("PEAR_Remote ($server_host:$server_port) : " .
                    "unexpected HTTP response", (int)$matches[1], null, null,
                    "$matches[1] $matches[2]");
        }
        while (trim(fgets($fp, 2048)) != ''); // skip rest of headers
        while ($chunk = fread($fp, 10240)) {
            $response .= $chunk;
        }
        fclose($fp);
        if ($this->config->get('verbose') > 3) {
            print "XMLRPC RESPONSE:\n";
            var_dump($response);
        }
        $ret = xmlrpc_decode($response);
        if (is_array($ret) && isset($ret['__PEAR_TYPE__'])) {
            if ($ret['__PEAR_TYPE__'] == 'error') {
                if (isset($ret['__PEAR_CLASS__'])) {
                    $class = $ret['__PEAR_CLASS__'];
                } else {
                    $class = "PEAR_Error";
                }
                if ($ret['code']     === '') $ret['code']     = null;
                if ($ret['message']  === '') $ret['message']  = null;
                if ($ret['userinfo'] === '') $ret['userinfo'] = null;
                if (strtolower($class) == 'db_error') {
                    $ret = $this->raiseError(PEAR::errorMessage($ret['code']),
                                             $ret['code'], null, null,
                                             $ret['userinfo']);
                } else {
                    $ret = $this->raiseError($ret['message'], $ret['code'],
                                             null, null, $ret['userinfo']);
                }
            }
        } elseif (is_array($ret) && sizeof($ret) == 1 && isset($ret[0])
                  && is_array($ret[0]) &&
                  !empty($ret[0]['faultString']) &&
                  !empty($ret[0]['faultCode'])) {
            extract($ret[0]);
            $faultString = "XML-RPC Server Fault: " .
                 str_replace("\n", " ", $faultString);
            return $this->raiseError($faultString, $faultCode);
        } elseif (is_array($ret) && sizeof($ret) == 2 && !empty($ret['faultString']) &&
              !empty($ret['faultCode'])) {
            extract($ret);
            $faultString = "XML-RPC Server Fault: " .
                 str_replace("\n", " ", $faultString);
            return $this->raiseError($faultString, $faultCode);
        }
        return $ret;
    }

    // }}}

    // {{{ _encode

    // a slightly extended version of XML_RPC_encode
    function _encode($php_val)
    {
        global $XML_RPC_Boolean, $XML_RPC_Int, $XML_RPC_Double;
        global $XML_RPC_String, $XML_RPC_Array, $XML_RPC_Struct;

        $type = gettype($php_val);
        $xmlrpcval = new XML_RPC_Value;

        switch($type) {
            case "array":
                reset($php_val);
                $firstkey = key($php_val);
                end($php_val);
                $lastkey = key($php_val);
                reset($php_val);
                if ($firstkey === 0 && is_int($lastkey) &&
                    ($lastkey + 1) == count($php_val)) {
                    $is_continuous = true;
                    reset($php_val);
                    $size = count($php_val);
                    for ($expect = 0; $expect < $size; $expect++, next($php_val)) {
                        if (key($php_val) !== $expect) {
                            $is_continuous = false;
                            break;
                        }
                    }
                    if ($is_continuous) {
                        reset($php_val);
                        $arr = array();
                        while (list($k, $v) = each($php_val)) {
                            $arr[$k] = $this->_encode($v);
                        }
                        $xmlrpcval->addArray($arr);
                        break;
                    }
                }
                // fall though if not numerical and continuous
            case "object":
                $arr = array();
                while (list($k, $v) = each($php_val)) {
                    $arr[$k] = $this->_encode($v);
                }
                $xmlrpcval->addStruct($arr);
                break;
            case "integer":
                $xmlrpcval->addScalar($php_val, $XML_RPC_Int);
                break;
            case "double":
                $xmlrpcval->addScalar($php_val, $XML_RPC_Double);
                break;
            case "string":
            case "NULL":
                $xmlrpcval->addScalar($php_val, $XML_RPC_String);
                break;
            case "boolean":
                $xmlrpcval->addScalar($php_val, $XML_RPC_Boolean);
                break;
            case "unknown type":
            default:
                return null;
        }
        return $xmlrpcval;
    }

    // }}}

}

?>
<?php
/**
 * PEAR_REST
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: REST.php,v 1.31 2008/05/13 18:03:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * For downloading xml files
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/XMLParser.php';

/**
 * Intelligently retrieve data, following hyperlinks if necessary, and re-directing
 * as well
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_REST
{
    var $config;
    var $_options;
    function PEAR_REST(&$config, $options = array())
    {
        $this->config = &$config;
        $this->_options = $options;
    }

    /**
     * Retrieve REST data, but always retrieve the local cache if it is available.
     *
     * This is useful for elements that should never change, such as information on a particular
     * release
     * @param string full URL to this resource
     * @param array|false contents of the accept-encoding header
     * @param boolean     if true, xml will be returned as a string, otherwise, xml will be
     *                    parsed using PEAR_XMLParser
     * @return string|array
     */
    function retrieveCacheFirst($url, $accept = false, $forcestring = false, $channel = false)
    {
        $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
            md5($url) . 'rest.cachefile';
        if (file_exists($cachefile)) {
            return unserialize(implode('', file($cachefile)));
        }
        return $this->retrieveData($url, $accept, $forcestring, $channel);
    }

    /**
     * Retrieve a remote REST resource
     * @param string full URL to this resource
     * @param array|false contents of the accept-encoding header
     * @param boolean     if true, xml will be returned as a string, otherwise, xml will be
     *                    parsed using PEAR_XMLParser
     * @return string|array
     */
    function retrieveData($url, $accept = false, $forcestring = false, $channel = false)
    {
        $cacheId = $this->getCacheId($url);
        if ($ret = $this->useLocalCache($url, $cacheId)) {
            return $ret;
        }
        if (!isset($this->_options['offline'])) {
            $trieddownload = true;
            $file = $this->downloadHttp($url, $cacheId ? $cacheId['lastChange'] : false, $accept, $channel);
        } else {
            $trieddownload = false;
            $file = false;
        }
        if (PEAR::isError($file)) {
            if ($file->getCode() == -9276) {
                $trieddownload = false;
                $file = false; // use local copy if available on socket connect error
            } else {
                return $file;
            }
        }
        if (!$file) {
            $ret = $this->getCache($url);
            if (!PEAR::isError($ret) && $trieddownload) {
                // reset the age of the cache if the server says it was unmodified
                $this->saveCache($url, $ret, null, true, $cacheId);
            }
            return $ret;
        }
        if (is_array($file)) {
            $headers = $file[2];
            $lastmodified = $file[1];
            $content = $file[0];
        } else {
            $content = $file;
            $lastmodified = false;
            $headers = array();
        }
        if ($forcestring) {
            $this->saveCache($url, $content, $lastmodified, false, $cacheId);
            return $content;
        }
        if (isset($headers['content-type'])) {
            switch ($headers['content-type']) {
                case 'text/xml' :
                case 'application/xml' :
                    $parser = new PEAR_XMLParser;
                    PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
                    $err = $parser->parse($content);
                    PEAR::popErrorHandling();
                    if (PEAR::isError($err)) {
                        return PEAR::raiseError('Invalid xml downloaded from "' . $url . '": ' .
                            $err->getMessage());
                    }
                    $content = $parser->getData();
                case 'text/html' :
                default :
                    // use it as a string
            }
        } else {
            // assume XML
            $parser = new PEAR_XMLParser;
            $parser->parse($content);
            $content = $parser->getData();
        }
        $this->saveCache($url, $content, $lastmodified, false, $cacheId);
        return $content;
    }

    function useLocalCache($url, $cacheid = null)
    {
        if ($cacheid === null) {
            $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
                md5($url) . 'rest.cacheid';
            if (file_exists($cacheidfile)) {
                $cacheid = unserialize(implode('', file($cacheidfile)));
            } else {
                return false;
            }
        }
        $cachettl = $this->config->get('cache_ttl');
        // If cache is newer than $cachettl seconds, we use the cache!
        if (time() - $cacheid['age'] < $cachettl) {
            return $this->getCache($url);
        }
        return false;
    }

    function getCacheId($url)
    {
        $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
            md5($url) . 'rest.cacheid';
        if (file_exists($cacheidfile)) {
            $ret = unserialize(implode('', file($cacheidfile)));
            return $ret;
        } else {
            return false;
        }
    }

    function getCache($url)
    {
        $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
            md5($url) . 'rest.cachefile';
        if (file_exists($cachefile)) {
            return unserialize(implode('', file($cachefile)));
        } else {
            return PEAR::raiseError('No cached content available for "' . $url . '"');
        }
    }

    /**
     * @param string full URL to REST resource
     * @param string original contents of the REST resource
     * @param array  HTTP Last-Modified and ETag headers
     * @param bool   if true, then the cache id file should be regenerated to
     *               trigger a new time-to-live value
     */
    function saveCache($url, $contents, $lastmodified, $nochange = false, $cacheid = null)
    {
        $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
            md5($url) . 'rest.cacheid';
        $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
            md5($url) . 'rest.cachefile';
        if ($cacheid === null && $nochange) {
            $cacheid = unserialize(implode('', file($cacheidfile)));
        }

        $fp = @fopen($cacheidfile, 'wb');
        if (!$fp) {
            $cache_dir = $this->config->get('cache_dir');
            if (!is_dir($cache_dir)) {
                System::mkdir(array('-p', $cache_dir));
                $fp = @fopen($cacheidfile, 'wb');
                if (!$fp) {
                    return false;
                }
            } else {
                return false;
            }
        }

        if ($nochange) {
            fwrite($fp, serialize(array(
                'age'        => time(),
                'lastChange' => $cacheid['lastChange'],
                )));
            fclose($fp);
            return true;
        } else {
            fwrite($fp, serialize(array(
                'age'        => time(),
                'lastChange' => $lastmodified,
                )));
        }
        fclose($fp);
        $fp = @fopen($cachefile, 'wb');
        if (!$fp) {
            if (file_exists($cacheidfile)) {
                @unlink($cacheidfile);
            }
            return false;
        }
        fwrite($fp, serialize($contents));
        fclose($fp);
        return true;
    }

    /**
     * Efficiently Download a file through HTTP.  Returns downloaded file as a string in-memory
     * This is best used for small files
     *
     * If an HTTP proxy has been configured (http_proxy PEAR_Config
     * setting), the proxy will be used.
     *
     * @param string  $url       the URL to download
     * @param string  $save_dir  directory to save file in
     * @param false|string|array $lastmodified header values to check against for caching
     *                           use false to return the header values from this download
     * @param false|array $accept Accept headers to send
     * @return string|array  Returns the contents of the downloaded file or a PEAR
     *                       error on failure.  If the error is caused by
     *                       socket-related errors, the error object will
     *                       have the fsockopen error code available through
     *                       getCode().  If caching is requested, then return the header
     *                       values.
     *
     * @access public
     */
    function downloadHttp($url, $lastmodified = null, $accept = false, $channel = false)
    {
        $info = parse_url($url);
        if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) {
            return PEAR::raiseError('Cannot download non-http URL "' . $url . '"');
        }
        if (!isset($info['host'])) {
            return PEAR::raiseError('Cannot download from non-URL "' . $url . '"');
        } else {
            $host = $info['host'];
            if (!array_key_exists('port', $info)) {
                $info['port'] = null;
            }
            if (!array_key_exists('path', $info)) {
                $info['path'] = null;
            }
            $port = $info['port'];
            $path = $info['path'];
        }
        $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
        if ($this->config->get('http_proxy')&&
              $proxy = parse_url($this->config->get('http_proxy'))) {
            $proxy_host = isset($proxy['host']) ? $proxy['host'] : null;
            if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') {
                $proxy_host = 'ssl://' . $proxy_host;
            }
            $proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080;
            $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null;
            $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null;
        }
        if (empty($port)) {
            if (isset($info['scheme']) && $info['scheme'] == 'https') {
                $port = 443;
            } else {
                $port = 80;
            }
        }
        If (isset($proxy['host'])) {
            $request = "GET $url HTTP/1.1\r\n";
        } else {
            $request = "GET $path HTTP/1.1\r\n";
        }
        $request .= "Host: $host:$port\r\n";

        $ifmodifiedsince = '';
        if (is_array($lastmodified)) {
            if (isset($lastmodified['Last-Modified'])) {
                $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n";
            }
            if (isset($lastmodified['ETag'])) {
                $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n";
            }
        } else {
            $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : '');
        }
        $request .= $ifmodifiedsince .
            "User-Agent: PEAR/1.7.2/PHP/" . PHP_VERSION . "\r\n";
        $username = $this->config->get('username', null, $channel);
        $password = $this->config->get('password', null, $channel);
        if ($username && $password) {
            $tmp = base64_encode("$username:$password");
            $request .= "Authorization: Basic $tmp\r\n";
        }
        if ($proxy_host != '' && $proxy_user != '') {
            $request .= 'Proxy-Authorization: Basic ' .
                base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n";
        }
        if ($accept) {
            $request .= 'Accept: ' . implode(', ', $accept) . "\r\n";
        }
        $request .= "Accept-Encoding:\r\n";
        $request .= "Connection: close\r\n";
        $request .= "\r\n";
        if ($proxy_host != '') {
            $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr, 15);
            if (!$fp) {
                return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr",
                    -9276);
            }
        } else {
            if (isset($info['scheme']) && $info['scheme'] == 'https') {
                $host = 'ssl://' . $host;
            }
            $fp = @fsockopen($host, $port, $errno, $errstr);
            if (!$fp) {
                return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno);
            }
        }
        fwrite($fp, $request);
        $headers = array();
        while (trim($line = fgets($fp, 1024))) {
            if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) {
                $headers[strtolower($matches[1])] = trim($matches[2]);
            } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
                if ($matches[1] == 304 && ($lastmodified || ($lastmodified === false))) {
                    return false;
                }
                if ($matches[1] != 200) {
                    return PEAR::raiseError("File http://$host:$port$path not valid (received: $line)", (int) $matches[1]);
                }
            }
        }
        if (isset($headers['content-length'])) {
            $length = $headers['content-length'];
        } else {
            $length = -1;
        }
        $data = '';
        while ($chunk = @fread($fp, 8192)) {
            $data .= $chunk;
        }
        fclose($fp);
        if ($lastmodified === false || $lastmodified) {
            if (isset($headers['etag'])) {
                $lastmodified = array('ETag' => $headers['etag']);
            }
            if (isset($headers['last-modified'])) {
                if (is_array($lastmodified)) {
                    $lastmodified['Last-Modified'] = $headers['last-modified'];
                } else {
                    $lastmodified = $headers['last-modified'];
                }
            }
            return array($data, $lastmodified, $headers);
        }
        return $data;
    }
}
?>
<?php
/**
 * PEAR_REST_10
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: 10.php,v 1.53 2008/04/11 01:16:40 dufuz Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a12
 */

/**
 * For downloading REST xml/txt files
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/REST.php';

/**
 * Implement REST 1.0
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a12
 */
class PEAR_REST_10
{
    /**
     * @var PEAR_REST
     */
    var $_rest;
    function PEAR_REST_10($config, $options = array())
    {
        $this->_rest = &new PEAR_REST($config, $options);
    }

    /**
     * Retrieve information about a remote package to be downloaded from a REST server
     *
     * @param string $base The uri to prepend to all REST calls
     * @param array $packageinfo an array of format:
     * <pre>
     *  array(
     *   'package' => 'packagename',
     *   'channel' => 'channelname',
     *  ['state' => 'alpha' (or valid state),]
     *  -or-
     *  ['version' => '1.whatever']
     * </pre>
     * @param string $prefstate Current preferred_state config variable value
     * @param bool $installed the installed version of this package to compare against
     * @return array|false|PEAR_Error see {@link _returnDownloadURL()}
     */
    function getDownloadURL($base, $packageinfo, $prefstate, $installed, $channel = false)
    {
        $channel = $packageinfo['channel'];
        $package = $packageinfo['package'];
        $states = $this->betterStates($prefstate, true);
        if (!$states) {
            return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
        }
        $state   = isset($packageinfo['state'])   ? $packageinfo['state']   : null;
        $version = isset($packageinfo['version']) ? $packageinfo['version'] : null;
        $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . '/allreleases.xml', false, false, $channel);
        if (PEAR::isError($info)) {
            return PEAR::raiseError('No releases available for package "' .
                $channel . '/' . $package . '"');
        }
        if (!isset($info['r'])) {
            return false;
        }
        $release = $found = false;
        if (!is_array($info['r']) || !isset($info['r'][0])) {
            $info['r'] = array($info['r']);
        }
        foreach ($info['r'] as $release) {
            if (!isset($this->_rest->_options['force']) && ($installed &&
                  version_compare($release['v'], $installed, '<'))) {
                continue;
            }
            if (isset($state)) {
                // try our preferred state first
                if ($release['s'] == $state) {
                    $found = true;
                    break;
                }
                // see if there is something newer and more stable
                // bug #7221
                if (in_array($release['s'], $this->betterStates($state), true)) {
                    $found = true;
                    break;
                }
            } elseif (isset($version)) {
                if ($release['v'] == $version) {
                    $found = true;
                    break;
                }
            } else {
                if (in_array($release['s'], $states)) {
                    $found = true;
                    break;
                }
            }
        }
        return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel);
    }

    function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage,
                               $prefstate = 'stable', $installed = false, $channel = false)
    {
        $channel = $dependency['channel'];
        $package = $dependency['name'];
        $states = $this->betterStates($prefstate, true);
        if (!$states) {
            return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
        }
        $state   = isset($dependency['state'])   ? $dependency['state']   : null;
        $version = isset($dependency['version']) ? $dependency['version'] : null;
        $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . '/allreleases.xml', false, false, $channel);
        if (PEAR::isError($info)) {
            return PEAR::raiseError('Package "' . $deppackage['channel'] . '/' . $deppackage['package']
                . '" dependency "' . $channel . '/' . $package . '" has no releases');
        }
        if (!is_array($info) || !isset($info['r'])) {
            return false;
        }
        $exclude = array();
        $min = $max = $recommended = false;
        if ($xsdversion == '1.0') {
            switch ($dependency['rel']) {
                case 'ge' :
                    $min = $dependency['version'];
                break;
                case 'gt' :
                    $min = $dependency['version'];
                    $exclude = array($dependency['version']);
                break;
                case 'eq' :
                    $recommended = $dependency['version'];
                break;
                case 'lt' :
                    $max = $dependency['version'];
                    $exclude = array($dependency['version']);
                break;
                case 'le' :
                    $max = $dependency['version'];
                break;
                case 'ne' :
                    $exclude = array($dependency['version']);
                break;
            }
        } else {
            $min = isset($dependency['min']) ? $dependency['min'] : false;
            $max = isset($dependency['max']) ? $dependency['max'] : false;
            $recommended = isset($dependency['recommended']) ?
                $dependency['recommended'] : false;
            if (isset($dependency['exclude'])) {
                if (!isset($dependency['exclude'][0])) {
                    $exclude = array($dependency['exclude']);
                }
            }
        }
        $release = $found = false;
        if (!is_array($info['r']) || !isset($info['r'][0])) {
            $info['r'] = array($info['r']);
        }
        foreach ($info['r'] as $release) {
            if (!isset($this->_rest->_options['force']) && ($installed &&
                  version_compare($release['v'], $installed, '<'))) {
                continue;
            }
            if (in_array($release['v'], $exclude)) { // skip excluded versions
                continue;
            }
            // allow newer releases to say "I'm OK with the dependent package"
            if ($xsdversion == '2.0' && isset($release['co'])) {
                if (!is_array($release['co']) || !isset($release['co'][0])) {
                    $release['co'] = array($release['co']);
                }
                foreach ($release['co'] as $entry) {
                    if (isset($entry['x']) && !is_array($entry['x'])) {
                        $entry['x'] = array($entry['x']);
                    } elseif (!isset($entry['x'])) {
                        $entry['x'] = array();
                    }
                    if ($entry['c'] == $deppackage['channel'] &&
                          strtolower($entry['p']) == strtolower($deppackage['package']) &&
                          version_compare($deppackage['version'], $entry['min'], '>=') &&
                          version_compare($deppackage['version'], $entry['max'], '<=') &&
                          !in_array($release['v'], $entry['x'])) {
                        $recommended = $release['v'];
                        break;
                    }
                }
            }
            if ($recommended) {
                if ($release['v'] != $recommended) { // if we want a specific
                    // version, then skip all others
                    continue;
                } else {
                    if (!in_array($release['s'], $states)) {
                        // the stability is too low, but we must return the
                        // recommended version if possible
                        return $this->_returnDownloadURL($base, $package, $release, $info, true, false, $channel);
                    }
                }
            }
            if ($min && version_compare($release['v'], $min, 'lt')) { // skip too old versions
                continue;
            }
            if ($max && version_compare($release['v'], $max, 'gt')) { // skip too new versions
                continue;
            }
            if ($installed && version_compare($release['v'], $installed, '<')) {
                continue;
            }
            if (in_array($release['s'], $states)) { // if in the preferred state...
                $found = true; // ... then use it
                break;
            }
        }
        return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel);
    }

    /**
     * Take raw data and return the array needed for processing a download URL
     *
     * @param string $base REST base uri
     * @param string $package Package name
     * @param array $release an array of format array('v' => version, 's' => state)
     *                       describing the release to download
     * @param array $info list of all releases as defined by allreleases.xml
     * @param bool|null $found determines whether the release was found or this is the next
     *                    best alternative.  If null, then versions were skipped because
     *                    of PHP dependency
     * @return array|PEAR_Error
     * @access private
     */
    function _returnDownloadURL($base, $package, $release, $info, $found, $phpversion = false, $channel = false)
    {
        if (!$found) {
            $release = $info['r'][0];
        }
        $pinfo = $this->_rest->retrieveCacheFirst($base . 'p/' . strtolower($package) . '/' .
            'info.xml', false, false, $channel);
        if (PEAR::isError($pinfo)) {
            return PEAR::raiseError('Package "' . $package .
                '" does not have REST info xml available');
        }
        $releaseinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/' .
            $release['v'] . '.xml', false, false, $channel);
        if (PEAR::isError($releaseinfo)) {
            return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] .
                '" does not have REST xml available');
        }
        $packagexml = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/' .
            'deps.' . $release['v'] . '.txt', false, true, $channel);
        if (PEAR::isError($packagexml)) {
            return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] .
                '" does not have REST dependency information available');
        }
        $packagexml = unserialize($packagexml);
        if (!$packagexml) {
            $packagexml = array();
        }
        $allinfo = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
            '/allreleases.xml', false, false, $channel);
        if (!is_array($allinfo['r']) || !isset($allinfo['r'][0])) {
            $allinfo['r'] = array($allinfo['r']);
        }
        $compatible = false;
        foreach ($allinfo['r'] as $release) {
            if ($release['v'] != $releaseinfo['v']) {
                continue;
            }
            if (!isset($release['co'])) {
                break;
            }
            $compatible = array();
            if (!is_array($release['co']) || !isset($release['co'][0])) {
                $release['co'] = array($release['co']);
            }
            foreach ($release['co'] as $entry) {
                $comp = array();
                $comp['name'] = $entry['p'];
                $comp['channel'] = $entry['c'];
                $comp['min'] = $entry['min'];
                $comp['max'] = $entry['max'];
                if (isset($entry['x']) && !is_array($entry['x'])) {
                    $comp['exclude'] = $entry['x'];
                }
                $compatible[] = $comp;
            }
            if (count($compatible) == 1) {
                $compatible = $compatible[0];
            }
            break;
        }
        if (isset($pinfo['dc']) && isset($pinfo['dp'])) {
            if (is_array($pinfo['dp'])) {
                $deprecated = array('channel' => (string) $pinfo['dc'],
                                    'package' => trim($pinfo['dp']['_content']));
            } else {
                $deprecated = array('channel' => (string) $pinfo['dc'],
                                    'package' => trim($pinfo['dp']));
            }
        } else {
            $deprecated = false;
        }
        if ($found) {
            return
                array('version' => $releaseinfo['v'],
                      'info' => $packagexml,
                      'package' => $releaseinfo['p']['_content'],
                      'stability' => $releaseinfo['st'],
                      'url' => $releaseinfo['g'],
                      'compatible' => $compatible,
                      'deprecated' => $deprecated,
                );
        } else {
            return
                array('version' => $releaseinfo['v'],
                      'package' => $releaseinfo['p']['_content'],
                      'stability' => $releaseinfo['st'],
                      'info' => $packagexml,
                      'compatible' => $compatible,
                      'deprecated' => $deprecated,
                      'php' => $phpversion
                );
        }
    }

    function listPackages($base, $channel = false)
    {
        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
        if (PEAR::isError($packagelist)) {
            return $packagelist;
        }
        if (!is_array($packagelist) || !isset($packagelist['p'])) {
            return array();
        }
        if (!is_array($packagelist['p'])) {
            $packagelist['p'] = array($packagelist['p']);
        }
        return $packagelist['p'];
    }

    /**
     * List all categories of a REST server
     *
     * @param string $base base URL of the server
     * @return array of categorynames
     */
    function listCategories($base, $channel = false)
    {
        $categories = array();

        // c/categories.xml does not exist;
        // check for every package its category manually
        // This is SLOOOWWWW : ///
        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
        if (PEAR::isError($packagelist)) {
            return $packagelist;
        }
        if (!is_array($packagelist) || !isset($packagelist['p'])) {
            $ret = array();
            return $ret;
        }
        if (!is_array($packagelist['p'])) {
            $packagelist['p'] = array($packagelist['p']);
        }

        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        foreach ($packagelist['p'] as $package) {
                $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
                if (PEAR::isError($inf)) {
                    PEAR::popErrorHandling();
                    return $inf;
                }
                $cat = $inf['ca']['_content'];
                if (!isset($categories[$cat])) {
                    $categories[$cat] = $inf['ca'];
                }
        }
        return array_values($categories);
    }

    /**
     * List a category of a REST server
     *
     * @param string $base base URL of the server
     * @param string $category name of the category
     * @param boolean $info also download full package info
     * @return array of packagenames
     */
    function listCategory($base, $category, $info = false, $channel = false)
    {
        // gives '404 Not Found' error when category doesn't exist
        $packagelist = $this->_rest->retrieveData($base.'c/'.urlencode($category).'/packages.xml', false, false, $channel);
        if (PEAR::isError($packagelist)) {
            return $packagelist;
        }
        if (!is_array($packagelist) || !isset($packagelist['p'])) {
            return array();
        }
        if (!is_array($packagelist['p']) ||
            !isset($packagelist['p'][0])) { // only 1 pkg
            $packagelist = array($packagelist['p']);
        } else {
            $packagelist = $packagelist['p'];
        }

        if ($info == true) {
            // get individual package info
            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
            foreach ($packagelist as $i => $packageitem) {
                $url = sprintf('%s'.'r/%s/latest.txt',
                        $base,
                        strtolower($packageitem['_content']));
                $version = $this->_rest->retrieveData($url, false, false, $channel);
                if (PEAR::isError($version)) {
                    break; // skipit
                }
                $url = sprintf('%s'.'r/%s/%s.xml',
                        $base,
                        strtolower($packageitem['_content']),
                        $version);
                $info = $this->_rest->retrieveData($url, false, false, $channel);
                if (PEAR::isError($info)) {
                    break; // skipit
                }
                $packagelist[$i]['info'] = $info;
            }
            PEAR::popErrorHandling();
        }

        return $packagelist;
    }


    function listAll($base, $dostable, $basic = true, $searchpackage = false, $searchsummary = false, $channel = false)
    {
        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
        if (PEAR::isError($packagelist)) {
            return $packagelist;
        }
        if ($this->_rest->config->get('verbose') > 0) {
            $ui = &PEAR_Frontend::singleton();
            $ui->log('Retrieving data...0%', false);
        }
        $ret = array();
        if (!is_array($packagelist) || !isset($packagelist['p'])) {
            return $ret;
        }
        if (!is_array($packagelist['p'])) {
            $packagelist['p'] = array($packagelist['p']);
        }

        // only search-packagename = quicksearch !
        if ($searchpackage && (!$searchsummary || empty($searchpackage))) {
            $newpackagelist = array();
            foreach ($packagelist['p'] as $package) {
                if (!empty($searchpackage) && stristr($package, $searchpackage) !== false) {
                    $newpackagelist[] = $package;
                }
            }
            $packagelist['p'] = $newpackagelist;
        }
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        $next = .1;
        foreach ($packagelist['p'] as $progress => $package) {
            if ($this->_rest->config->get('verbose') > 0) {
                if ($progress / count($packagelist['p']) >= $next) {
                    if ($next == .5) {
                        $ui->log('50%', false);
                    } else {
                        $ui->log('.', false);
                    }
                    $next += .1;
                }
            }
            if ($basic) { // remote-list command
                if ($dostable) {
                    $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
                        '/stable.txt', false, false, $channel);
                } else {
                    $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
                        '/latest.txt', false, false, $channel);
                }
                if (PEAR::isError($latest)) {
                    $latest = false;
                }
                $info = array('stable' => $latest);
            } else { // list-all command
                $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
                if (PEAR::isError($inf)) {
                    PEAR::popErrorHandling();
                    return $inf;
                }
                if ($searchpackage) {
                    $found = (!empty($searchpackage) && stristr($package, $searchpackage) !== false);
                    if (!$found && !(isset($searchsummary) && !empty($searchsummary)
                        && (stristr($inf['s'], $searchsummary) !== false
                            || stristr($inf['d'], $searchsummary) !== false)))
                    {
                        continue;
                    };
                }
                $releases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
                    '/allreleases.xml', false, false, $channel);
                if (PEAR::isError($releases)) {
                    continue;
                }
                if (!isset($releases['r'][0])) {
                    $releases['r'] = array($releases['r']);
                }
                unset($latest);
                unset($unstable);
                unset($stable);
                unset($state);
                foreach ($releases['r'] as $release) {
                    if (!isset($latest)) {
                        if ($dostable && $release['s'] == 'stable') {
                            $latest = $release['v'];
                            $state = 'stable';
                        }
                        if (!$dostable) {
                            $latest = $release['v'];
                            $state = $release['s'];
                        }
                    }
                    if (!isset($stable) && $release['s'] == 'stable') {
                        $stable = $release['v'];
                        if (!isset($unstable)) {
                            $unstable = $stable;
                        }
                    }
                    if (!isset($unstable) && $release['s'] != 'stable') {
                        $latest = $unstable = $release['v'];
                        $state = $release['s'];
                    }
                    if (isset($latest) && !isset($state)) {
                        $state = $release['s'];
                    }
                    if (isset($latest) && isset($stable) && isset($unstable)) {
                        break;
                    }
                }
                $deps = array();
                if (!isset($unstable)) {
                    $unstable = false;
                    $state = 'stable';
                    if (isset($stable)) {
                        $latest = $unstable = $stable;
                    }
                } else {
                    $latest = $unstable;
                }
                if (!isset($latest)) {
                    $latest = false;
                }
                if ($latest) {
                    $d = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' .
                        $latest . '.txt', false, false, $channel);
                    if (!PEAR::isError($d)) {
                        $d = unserialize($d);
                        if ($d) {
                            if (isset($d['required'])) {
                                if (!class_exists('PEAR_PackageFile_v2')) {
                                    require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2.php';
                                }
                                if (!isset($pf)) {
                                    $pf = new PEAR_PackageFile_v2;
                                }
                                $pf->setDeps($d);
                                $tdeps = $pf->getDeps();
                            } else {
                                $tdeps = $d;
                            }
                            foreach ($tdeps as $dep) {
                                if ($dep['type'] !== 'pkg') {
                                    continue;
                                }
                                $deps[] = $dep;
                            }
                        }
                    }
                }
                if (!isset($stable)) {
                    $stable = '-n/a-';
                }
                if (!$searchpackage) {
                    $info = array('stable' => $latest, 'summary' => $inf['s'], 'description' =>
                        $inf['d'], 'deps' => $deps, 'category' => $inf['ca']['_content'],
                        'unstable' => $unstable, 'state' => $state);
                } else {
                    $info = array('stable' => $stable, 'summary' => $inf['s'], 'description' =>
                        $inf['d'], 'deps' => $deps, 'category' => $inf['ca']['_content'],
                        'unstable' => $unstable, 'state' => $state);
                }
            }
            $ret[$package] = $info;
        }
        PEAR::popErrorHandling();
        return $ret;
    }

    function listLatestUpgrades($base, $pref_state, $installed, $channel, &$reg)
    {
        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
        if (PEAR::isError($packagelist)) {
            return $packagelist;
        }
        $ret = array();
        if (!is_array($packagelist) || !isset($packagelist['p'])) {
            return $ret;
        }
        if (!is_array($packagelist['p'])) {
            $packagelist['p'] = array($packagelist['p']);
        }
        foreach ($packagelist['p'] as $package) {
            if (!isset($installed[strtolower($package)])) {
                continue;
            }
            $inst_version = $reg->packageInfo($package, 'version', $channel);
            $inst_state = $reg->packageInfo($package, 'release_state', $channel);
            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
            $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
                '/allreleases.xml', false, false, $channel);
            PEAR::popErrorHandling();
            if (PEAR::isError($info)) {
                continue; // no remote releases
            }
            if (!isset($info['r'])) {
                continue;
            }
            $found = false;
            $release = false;
            if (!is_array($info['r']) || !isset($info['r'][0])) {
                $info['r'] = array($info['r']);
            }
            // $info['r'] is sorted by version number
            foreach ($info['r'] as $release) {
                if ($inst_version && version_compare($release['v'], $inst_version, '<=')) {
                    // not newer than the one installed
                    break;
                }

                // new version > installed version
                if (!$pref_state) {
                    // every state is a good state
                    $found = true;
                    break;
                } else {
                    $new_state = $release['s'];
                    // if new state >= installed state: go
                    if (in_array($new_state, $this->betterStates($inst_state, true))) {
                        $found = true;
                        break;
                    } else {
                        // only allow to lower the state of package,
                        // if new state >= preferred state: go
                        if (in_array($new_state, $this->betterStates($pref_state, true))) {
                            $found = true;
                            break;
                        }
                    }
                }
            }
            if (!$found) {
                continue;
            }
            $relinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/' .
                $release['v'] . '.xml', false, false, $channel);
            if (PEAR::isError($relinfo)) {
                return $relinfo;
            }
            $ret[$package] = array(
                    'version' => $release['v'],
                    'state' => $release['s'],
                    'filesize' => $relinfo['f'],
                );
        }
        return $ret;
    }

    function packageInfo($base, $package, $channel = false)
    {
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        $pinfo = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
        if (PEAR::isError($pinfo)) {
            PEAR::popErrorHandling();
            return PEAR::raiseError('Unknown package: "' . $package . '" (Debug: ' .
                $pinfo->getMessage() . ')');
        }
        $releases = array();
        $allreleases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
            '/allreleases.xml', false, false, $channel);
        if (!PEAR::isError($allreleases)) {
            if (!class_exists('PEAR_PackageFile_v2')) {
                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2.php';
            }
            if (!is_array($allreleases['r']) || !isset($allreleases['r'][0])) {
                $allreleases['r'] = array($allreleases['r']);
            }
            $pf = new PEAR_PackageFile_v2;
            foreach ($allreleases['r'] as $release) {
                $ds = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' .
                    $release['v'] . '.txt', false, false, $channel);
                if (PEAR::isError($ds)) {
                    continue;
                }
                if (!isset($latest)) {
                    $latest = $release['v'];
                }
                $pf->setDeps(unserialize($ds));
                $ds = $pf->getDeps();
                $info = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package)
                    . '/' . $release['v'] . '.xml', false, false, $channel);
                if (PEAR::isError($info)) {
                    continue;
                }
                $releases[$release['v']] = array(
                    'doneby' => $info['m'],
                    'license' => $info['l'],
                    'summary' => $info['s'],
                    'description' => $info['d'],
                    'releasedate' => $info['da'],
                    'releasenotes' => $info['n'],
                    'state' => $release['s'],
                    'deps' => $ds ? $ds : array(),
                );
            }
        } else {
            $latest = '';
        }
        PEAR::popErrorHandling();
        if (isset($pinfo['dc']) && isset($pinfo['dp'])) {
            if (is_array($pinfo['dp'])) {
                $deprecated = array('channel' => (string) $pinfo['dc'],
                                    'package' => trim($pinfo['dp']['_content']));
            } else {
                $deprecated = array('channel' => (string) $pinfo['dc'],
                                    'package' => trim($pinfo['dp']));
            }
        } else {
            $deprecated = false;
        }
        return array(
            'name' => $pinfo['n'],
            'channel' => $pinfo['c'],
            'category' => $pinfo['ca']['_content'],
            'stable' => $latest,
            'license' => $pinfo['l'],
            'summary' => $pinfo['s'],
            'description' => $pinfo['d'],
            'releases' => $releases,
            'deprecated' => $deprecated,
            );
    }

    /**
     * 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);
    }
}
?><?php
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Config.php';
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Command.php';
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Common.php';
class PEAR_Start extends PEAR
{
    var $bin_dir;
    var $data_dir;
    var $cfg_dir;
    var $www_dir;
    var $install_pfc;
    var $corePackages =
        array(
            'Archive_Tar',
            'Console_Getopt',
            'PEAR',
            'Structures_Graph',
        );
    var $local_dir = array();
    var $origpwd;
    var $pfc_packages = array(
            'DB',
            'Net_Socket',
            'Net_SMTP',
            'Mail',
            'XML_Parser',
            'XML_RPC',
            'PHPUnit'
        );
    var $php_dir;
    var $php_bin;
    var $pear_conf;
    var $validPHPBin = false;
    var $test_dir;
    var $download_dir;
    var $temp_dir;
    var $config =
        array(
            'prefix',
            'bin_dir',
            'php_dir',
            'doc_dir',
            'data_dir',
            'cfg_dir',
            'www_dir',
            'test_dir',
            'temp_dir',
            'download_dir',
            'pear_conf',
        );
    var $prefix;
    var $progress = 0;
    var $configPrompt =
        array(
            'prefix' => 'Installation base ($prefix)',
            'temp_dir' => 'Temporary directory for processing',
            'download_dir' => 'Temporary directory for downloads',
            'bin_dir' => 'Binaries directory',
            'php_dir' => 'PHP code directory ($php_dir)',
            'doc_dir' => 'Documentation directory',
            'data_dir' => 'Data directory',
            'cfg_dir' => 'User-modifiable configuration files directory',
            'www_dir' => 'Public Web Files directory',
            'test_dir' => 'Tests directory',
            'pear_conf' => 'Name of configuration file',
        );
    var $localInstall;
    var $PEARConfig;
    var $tarball = array();
    function PEAR_Start()
    {
        parent::PEAR();
        if (OS_WINDOWS) {
            $this->configPrompt['php_bin'] = 'Path to CLI php.exe';
            $this->config[] = 'php_bin';
            $this->prefix = getcwd();

            if (!@is_dir($this->prefix)) {
                if (@is_dir('c:\php5')) {
                    $this->prefix = 'c:\php5';
                } elseif (@is_dir('c:\php4')) {
                    $this->prefix = 'c:\php4';
                } elseif (@is_dir('c:\php')) {
                    $this->prefix = 'c:\php';
                }
            }

            $this->localInstall = false;
            $this->bin_dir   = '$prefix';
            $this->temp_dir   = '$prefix\tmp';
            $this->download_dir   = '$prefix\tmp';
            $this->php_dir   = '$prefix\pear';
            $this->doc_dir   = '$prefix\docs';
            $this->data_dir  = '$prefix\data';
            $this->test_dir  = '$prefix\tests';
            $this->www_dir  = '$prefix\www';
            $this->cfg_dir  = '$prefix\cfg';
            $this->pear_conf = PEAR_CONFIG_SYSCONFDIR . '\\pear.ini';
            /*
             * Detects php.exe
             */
            $this->validPHPBin = true;
            if ($t = $this->safeGetenv('PHP_PEAR_PHP_BIN')) {
                $this->php_bin   = dirname($t);
            } elseif ($t = $this->safeGetenv('PHP_BIN')) {
                $this->php_bin   = dirname($t);
            } elseif ($t = System::which('php')) {
                $this->php_bin = dirname($t);
            } elseif (is_file($this->prefix . '\cli\php.exe')) {
                $this->php_bin = $this->prefix . '\cli';
            } elseif (is_file($this->prefix . '\php.exe')) {
                $this->php_bin = $this->prefix;
            }
            $phpexe = OS_WINDOWS ? '\\php.exe' : '/php';
            if ($this->php_bin && !is_file($this->php_bin . $phpexe)) {
                $this->php_bin = '';
            } else {
                if (!ereg(":", $this->php_bin)) {
                    $this->php_bin = getcwd() . DIRECTORY_SEPARATOR . $this->php_bin;
                }
            }
            if (!is_file($this->php_bin . $phpexe)) {
                if (is_file('c:/php/cli/php.exe')) {
                    $this->php_bin = 'c"\\php\\cli';
                } elseif (is_file('c:/php5/php.exe')) {
                    $this->php_bin = 'c:\\php5';
                } elseif (is_file('c:/php4/cli/php.exe')) {
                    $this->php_bin = 'c:\\php4\\cli';
                } else {
                    $this->validPHPBin = false;
                }
            }
        } else {
            $this->prefix = dirname(PHP_BINDIR);
            $this->pear_conf = PEAR_CONFIG_SYSCONFDIR . '/pear.conf';
            if (get_current_user() != 'root') {
                $this->prefix = $this->safeGetenv('HOME') . '/pear';
                $this->pear_conf = $this->safeGetenv('HOME') . '.pearrc';
            }
            $this->bin_dir   = '$prefix/bin';
            $this->php_dir   = '$prefix/share/pear';
            $this->temp_dir  = '/tmp/pear/install';
            $this->download_dir  = '/tmp/pear/install';
            $this->doc_dir   = '$prefix/docs';
            $this->www_dir   = '$prefix/www';
            $this->cfg_dir   = '$prefix/cfg';
            $this->data_dir  = '$prefix/data';
            $this->test_dir  = '$prefix/tests';
            // check if the user has installed PHP with PHP or GNU layout
            if (@is_dir("$this->prefix/lib/php/.registry")) {
                $this->php_dir = '$prefix/lib/php';
            } elseif (@is_dir("$this->prefix/share/pear/lib/.registry")) {
                $this->php_dir = '$prefix/share/pear/lib';
                $this->doc_dir   = '$prefix/share/pear/docs';
                $this->data_dir  = '$prefix/share/pear/data';
                $this->test_dir  = '$prefix/share/pear/tests';
            } elseif (@is_dir("$this->prefix/share/php/.registry")) {
                $this->php_dir = '$prefix/share/php';
            }
        }
    }

    function safeGetenv($var)
    {
        if (is_array($_ENV) && isset($_ENV[$var])) {
            return $_ENV[$var];
        }
        return getenv($var);
    }

    function show($stuff)
    {
        print $stuff;
    }

    function locatePackagesToInstall()
    {
        $dp = @opendir(dirname(__FILE__) . '/go-pear-tarballs');
        if (empty($dp)) {
            return PEAR::raiseError("while locating packages to install: opendir('" .
                dirname(__FILE__) . "/go-pear-tarballs') failed");
        }
        $potentials = array();
        while (false !== ($entry = readdir($dp))) {
            if ($entry{0} == '.' || !in_array(substr($entry, -4), array('.tar', '.tgz'))) {
                continue;
            }
            $potentials[] = $entry;
        }
        closedir($dp);
        $notfound = array();
        foreach ($this->corePackages as $package) {
            foreach ($potentials as $i => $candidate) {
                if (preg_match('/^' . $package . '-' . _PEAR_COMMON_PACKAGE_VERSION_PREG
                      . '\.(tar|tgz)\\z/', $candidate)) {
                    $this->tarball[$package] = dirname(__FILE__) . '/go-pear-tarballs/' . $candidate;
                    unset($potentials[$i]);
                    continue 2;
                }
            }
            $notfound[] = $package;
        }
        if (count($notfound)) {
            return PEAR::raiseError("No tarballs found for core packages: " .
                    implode(', ', $notfound));
        }
        $this->tarball = array_merge($this->tarball, $potentials);
    }

    function setupTempStuff()
    {
        if (!($this->ptmp = System::mktemp(array('-d')))) {
            $this->show("System's Tempdir failed, trying to use \$prefix/tmp ...");
            $res = System::mkDir(array($this->prefix . '/tmp'));
            if (!$res) {
                return PEAR::raiseError('mkdir ' . $this->prefix . '/tmp ... failed');
            }
            $_temp = tempnam($this->prefix . '/tmp', 'gope');

            System::rm(array('-rf', $_temp));
            System::mkdir(array('-p','-m', '0700', $_temp));
            $this->ptmp = $this->prefix . '/tmp';
            $ok = @chdir($this->ptmp);

            if (!$ok) { // This should not happen, really ;)
                $this->bail('chdir ' . $this->ptmp . ' ... failed');
            }
    
            print "ok\n";
    
            // Adjust TEMPDIR envvars
            if (!isset($_ENV)) {
                $_ENV = array();
            };
            $_ENV['TMPDIR'] = $_ENV['TEMP'] = $this->prefix . '/tmp';
        }
        return @chdir($this->ptmp);
    }

    /**
     * Try to detect the kind of SAPI used by the
     * the given php.exe.
     * @author Pierrre-Alain Joye
     */
    function win32DetectPHPSAPI()
    {
        if ($this->php_bin != '') {
            if (OS_WINDOWS) {
                exec('"' . $this->php_bin . '\\php.exe" -v', $res);
            } else {
                exec('"' . $this->php_bin . '/php" -v', $res);
            }
            if (is_array($res)) {
                if (isset($res[0]) && strpos($res[0],"(cli)")) {
                    return 'cli';
                }
                if (isset($res[0]) && strpos($res[0],"cgi")) {
                    return 'cgi';
                }
                if (isset($res[0]) && strpos($res[0],"cgi-fcgi")) {
                    return 'cgi';
                } else {
                    return 'unknown';
                }
            }
        }
        return 'unknown';
    }

    function doInstall()
    {
        print "Beginning install...\n";
        // finish php_bin config
        if (OS_WINDOWS) {
            $this->php_bin .= '\\php.exe';
        } else {
            $this->php_bin .= '/php';
        }
        $this->PEARConfig = &PEAR_Config::singleton($this->pear_conf, $this->pear_conf);
        $this->PEARConfig->set('preferred_state', 'stable');
        foreach ($this->config as $var) {
            if ($var == 'pear_conf' || $var == 'prefix') {
                continue;
            }
            $this->PEARConfig->set($var, $this->$var);
        }
        
        $this->PEARConfig->store();
//       $this->PEARConfig->set('verbose', 6);
        print "Configuration written to $this->pear_conf...\n";
        $this->registry = &$this->PEARConfig->getRegistry();
        print "Initialized registry...\n";
        $install = &PEAR_Command::factory('install', $this->PEARConfig);
        print "Preparing to install...\n";
        $options = array(
            'nodeps' => true,
            'force' => true,
            'upgrade' => true,
            );
        foreach ($this->tarball as $pkg => $src) {
            print "installing $src...\n";
        }
        $install->run('install', $options, array_values($this->tarball));
    }

    function postProcessConfigVars()
    {
        foreach ($this->config as $n => $var) {
            for ($m = 1; $m <= count($this->config); $m++) {
                $var2 = $this->config[$m];
                $this->$var = str_replace('$'.$var2, $this->$var2, $this->$var);
            }
        }

        foreach ($this->config as $var) {
            $dir = $this->$var;
        
            if (!preg_match('/_dir\\z/', $var)) {
                continue;
            }
        
            if (!@is_dir($dir)) {
                if (!System::mkDir(array('-p', $dir))) {
                    $root = OS_WINDOWS ? 'administrator' : 'root';
                    return PEAR::raiseError("Unable to create {$this->configPrompt[$var]} $dir.
Run this script as $root or pick another location.\n");
                }
            }
        }
    }

    /**
     * Get the php.ini file used with the current
     * process or with the given php.exe
     *
     * Horrible hack, but well ;)
     *
     * Not used yet, will add the support later
     * @author Pierre-Alain Joye <paj@pearfr.org>
     */
    function getPhpiniPath()
    {
        $pathIni = get_cfg_var('cfg_file_path');
        if ($pathIni && is_file($pathIni)) {
            return $pathIni;
        }
    
        // Oh well, we can keep this too :)
        // I dunno if get_cfg_var() is safe on every OS
        if (OS_WINDOWS) {
            // on Windows, we can be pretty sure that there is a php.ini
            // file somewhere
            do {
                $php_ini = PHP_CONFIG_FILE_PATH . DIRECTORY_SEPARATOR . 'php.ini';
                if (@file_exists($php_ini)) {
                    break;
                }
                $php_ini = 'c:\winnt\php.ini';
                if (@file_exists($php_ini)) {
                    break;
                }
                $php_ini = 'c:\windows\php.ini';
            } while (false);
        } else {
            $php_ini = PHP_CONFIG_FILE_PATH . DIRECTORY_SEPARATOR . 'php.ini';
        }
    
        if (@is_file($php_ini)) {
            return $php_ini;
        }
    
        // We re running in hackz&troubles :)
        ob_implicit_flush(false);
        ob_start();
        phpinfo(INFO_GENERAL);
        $strInfo = ob_get_contents();
        ob_end_clean();
        ob_implicit_flush(true);
    
        if (php_sapi_name() != 'cli') {
            $strInfo = strip_tags($strInfo,'<td>');
            $arrayInfo = explode("</td>", $strInfo );
            $cli = false;
        } else {
            $arrayInfo = explode("\n", $strInfo);
            $cli = true;
        }
    
        foreach ($arrayInfo as $val) {
            if (strpos($val,"php.ini")) {
                if ($cli) {
                    list(,$pathIni) = explode('=>', $val);
                } else {
                    $pathIni = strip_tags(trim($val));
                }
                $pathIni = trim($pathIni);
                if (is_file($pathIni)) {
                    return $pathIni;
                }
            }
        }

        return false;
    }
}
?>
<?php
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Start.php';
class PEAR_Start_CLI extends PEAR_Start
{

    var $descLength;
    var $descFormat;
    var $first;
    var $last;
    var $origpwd;
    var $tty;

    function PEAR_Start_CLI()
    {
        parent::PEAR_Start();
        ini_set('html_errors', 0);
        define('WIN32GUI', OS_WINDOWS && php_sapi_name() == 'cli' && System::which('cscript'));
        $this->tty = OS_WINDOWS ? @fopen('\con', 'r') : @fopen('/dev/tty', 'r');

        if (!$this->tty) {
            $this->tty = fopen('php://stdin', 'r');
        }
        $this->origpwd = getcwd();
        $this->config = array_keys($this->configPrompt);
        
        // make indices run from 1...
        array_unshift($this->config, "");
        unset($this->config[0]);
        reset($this->config);
        $this->descLength = max(array_map('strlen', $this->configPrompt));
        $this->descFormat = "%-{$this->descLength}s";
        $this->first = key($this->config);
        end($this->config);
        $this->last = key($this->config);
        PEAR_Command::setFrontendType('CLI');
    }

    function _PEAR_Start_CLI()
    {
        if ($this->tty) {
            @fclose($this->tty);
        }
    }

    function run()
    {
        if (PEAR::isError($err = $this->locatePackagesToInstall())) {
            return $err;
        }
        $this->startupQuestion();
        $this->setupTempStuff();
        $this->getInstallLocations();
        $this->displayPreamble();
        if (PEAR::isError($err = $this->postProcessConfigVars())) {
            return $err;
        }
        $this->doInstall();
        $this->finishInstall();
    }

    function startupQuestion()
    {
        if (OS_WINDOWS) {
            print "
Are you installing a system-wide PEAR or a local copy?
(system|local) [system] : ";
            $tmp = trim(fgets($this->tty, 1024));
            if (!empty($tmp)) {
                if (strtolower($tmp) !== 'system') {
                    print "Please confirm local copy by typing 'yes' : ";
                    $tmp = trim(fgets($this->tty, 1024));
                    if (strtolower($tmp) == 'yes') {
                        $this->localInstall = true;
                        $this->pear_conf = '$prefix\\pear.ini';
                    }
                }
            }
        } else {
            if (get_current_user() == 'root') {
                return;
            }
            $this->pear_conf = $this->safeGetenv('HOME') . '/.pearrc';
        }
    }

    function getInstallLocations()
    {
        while (true) {
            print "
Below is a suggested file layout for your new PEAR installation.  To
change individual locations, type the number in front of the
directory.  Type 'all' to change all of them or simply press Enter to
accept these locations.

";

            foreach ($this->config as $n => $var) {
                $fullvar = $this->$var;
                foreach ($this->config as $blah => $unused) {
                    foreach ($this->config as $m => $var2) {
                        $fullvar = str_replace('$'.$var2, $this->$var2, $fullvar);
                    }
                }
                printf("%2d. $this->descFormat : %s\n", $n, $this->configPrompt[$var], $fullvar);
            }
    
            print "\n$this->first-$this->last, 'all' or Enter to continue: ";
            $tmp = trim(fgets($this->tty, 1024));
            if (empty($tmp)) {
                if (OS_WINDOWS && !$this->validPHPBin) {
                    echo "**ERROR**
Please, enter the php.exe path.

";
                } else {
                    break;
                }
            }
            if (isset($this->config[(int)$tmp])) {
                $var = $this->config[(int)$tmp];
                $desc = $this->configPrompt[$var];
                $current = $this->$var;
                if (WIN32GUI && $var != 'pear_conf'){
                    $tmp = $this->win32BrowseForFolder("Choose a Folder for $desc [$current] :");
                } else {
                    print "(Use \$prefix as a shortcut for '$this->prefix', etc.)
$desc [$current] : ";
                    $tmp = trim(fgets($this->tty, 1024));
                }
                $old = $this->$var;
                $this->$var = $$var = $tmp;
                if (OS_WINDOWS && $var=='php_bin') {
                    if ($this->validatePhpExecutable($tmp)) {
                        $this->php_bin = $tmp;
                    } else {
                        $this->php_bin = $old;
                    }
                }
            } elseif ($tmp == 'all') {
                foreach ($this->config as $n => $var) {
                    $desc = $this->configPrompt[$var];
                    $current = $this->$var;
                    print "$desc [$current] : ";
                    $tmp = trim(fgets($this->tty, 1024));
                    if (!empty($tmp)) {
                        $this->$var = $tmp;
                    }
                }
            }
        }
    }

    function validatePhpExecutable($tmp)
    {
        if (OS_WINDOWS) {
            if (strpos($tmp, 'php.exe')) {
                $tmp = str_replace('php.exe', '', $tmp);
            }
            if (file_exists($tmp . DIRECTORY_SEPARATOR . 'php.exe')) {
                $tmp = $tmp . DIRECTORY_SEPARATOR . 'php.exe';
                $this->php_bin_sapi = $this->win32DetectPHPSAPI();
                if ($this->php_bin_sapi=='cgi'){
                    print "
******************************************************************************
NOTICE! We found php.exe under $this->php_bin, it uses a $this->php_bin_sapi SAPI.
PEAR commandline tool works well with it.
If you have a CLI php.exe available, we recommend using it.

Press Enter to continue...";
                    $tmp = trim(fgets($this->tty, 1024));
                } elseif ($this->php_bin_sapi=='unknown') {
                    print "
******************************************************************************
WARNING! We found php.exe under $this->php_bin, it uses an $this->php_bin_sapi SAPI.
PEAR commandline tool has NOT been tested with it.
If you have a CLI (or CGI) php.exe available, we strongly recommend using it.

Press Enter to continue...";
                    $tmp = trim(fgets($this->tty, 1024));
                }
                echo "php.exe (sapi: $this->php_bin_sapi) found.\n\n";
                return $this->validPHPBin = true;
            } else {
                echo "**ERROR**: not a folder, or no php.exe found in this folder.
Press Enter to continue...";
                $tmp = trim(fgets($this->tty, 1024));
                return $this->validPHPBin = false;
            }
        }
    }

    /**
     * Create a vbs script to browse the getfolder dialog, called
     * by cscript, if it's available.
     * $label is the label text in the header of the dialog box
     *
     * TODO:
     * - Do not show Control panel
     * - Replace WSH with calls to w32 as soon as callbacks work
     * @author Pierrre-Alain Joye
     */
    function win32BrowseForFolder($label)
    {
        static $wshSaved=false;
        static $cscript='';
    $wsh_browserfolder = 'Option Explicit
Dim ArgObj, var1, var2, sa, sFld
Set ArgObj = WScript.Arguments
Const BIF_EDITBOX = &H10
Const BIF_NEWDIALOGSTYLE = &H40
Const BIF_RETURNONLYFSDIRS   = &H0001
Const BIF_DONTGOBELOWDOMAIN  = &H0002
Const BIF_STATUSTEXT         = &H0004
Const BIF_RETURNFSANCESTORS  = &H0008
Const BIF_VALIDATE           = &H0020
Const BIF_BROWSEFORCOMPUTER  = &H1000
Const BIF_BROWSEFORPRINTER   = &H2000
Const BIF_BROWSEINCLUDEFILES = &H4000
Const OFN_LONGNAMES = &H200000
Const OFN_NOLONGNAMES = &H40000
Const ssfDRIVES = &H11
Const ssfNETWORK = &H12
Set sa = CreateObject("Shell.Application")
var1=ArgObj(0)
Set sFld = sa.BrowseForFolder(0, var1, BIF_EDITBOX + BIF_VALIDATE + BIF_BROWSEINCLUDEFILES + BIF_RETURNFSANCESTORS+BIF_NEWDIALOGSTYLE , ssfDRIVES )
if not sFld is nothing Then
    if not left(sFld.items.item.path,1)=":" Then
        WScript.Echo sFld.items.item.path
    Else
        WScript.Echo "invalid"
    End If
Else
    WScript.Echo "cancel"
End If
';
        if( !$wshSaved){
            $cscript = $this->ptmp . DIRECTORY_SEPARATOR . "bf.vbs";
            $fh = fopen($cscript, "wb+");
            fwrite($fh, $wsh_browserfolder, strlen($wsh_browserfolder));
            fclose($fh);
            $wshSaved  = true;
        }
        exec('cscript ' . $cscript . ' "' . $label . '" //noLogo', $arPath);
        if (!count($arPath) || $arPath[0]=='' || $arPath[0]=='cancel') {
            return '';
        } elseif ($arPath[0]=='invalid') {
            echo "Invalid Path.\n";
            return '';
        }
        return $arPath[0];
    }

    function displayPreamble()
    {
        if (OS_WINDOWS) {
            /*
             * Checks PHP SAPI version under windows/CLI
             */
            if ($this->php_bin == '') {
                print "
We do not find any php.exe, please select the php.exe folder (CLI is
recommended, usually in c:\php\cli\php.exe)
";
                $this->validPHPBin = false;
            } elseif (strlen($this->php_bin)) {
                $this->php_bin_sapi = $this->win32DetectPHPSAPI();
                $this->validPHPBin = true;
                switch ($this->php_bin_sapi) {
                    case 'cli':
                    break;
                    case 'cgi':
                    case 'cgi-fcgi':
                        print "
*NOTICE*
We found php.exe under $this->php_bin, it uses a $this->php_bin_sapi SAPI. PEAR commandline
tool works well with it, if you have a CLI php.exe available, we
recommend using it.
";
                    break;
                    default:
                        print "
*WARNING*
We found php.exe under $this->php_bin, it uses an unknown SAPI. PEAR commandline
tool has not been tested with it, if you have a CLI (or CGI) php.exe available,
we strongly recommend using it.

";
                    break;
                }
            }
        }
    }

    function finishInstall()
    {
        $sep = OS_WINDOWS ? ';' : ':';
        $include_path = explode($sep, ini_get('include_path'));
        if (OS_WINDOWS) {
            $found = false;
            $t = strtolower($this->php_dir);
            foreach ($include_path as $path) {
                if ($t == strtolower($path)) {
                    $found = true;
                    break;
                }
            }
        } else {
            $found = in_array($this->php_dir, $include_path);
        }
        if (!$found) {
            print "
******************************************************************************
WARNING!  The include_path defined in the currently used php.ini does not
contain the PEAR PHP directory you just specified:
<$this->php_dir>
If the specified directory is also not in the include_path used by
your scripts, you will have problems getting any PEAR packages working.
";
    
            if ($php_ini = $this->getPhpiniPath()) {
                print "\n\nWould you like to alter php.ini <$php_ini>? [Y/n] : ";
                $alter_phpini = !stristr(fgets($this->tty, 1024), "n");
                if ($alter_phpini) {
                    $this->alterPhpIni($php_ini);
                } else {
                    if (OS_WINDOWS) {
                        print "
Please look over your php.ini file to make sure
$this->php_dir is in your include_path.";
                    } else {
                        print "
I will add a workaround for this in the 'pear' command to make sure
the installer works, but please look over your php.ini or Apache
configuration to make sure $this->php_dir is in your include_path.
";
                    }
                }
            }
    
        print "
Current include path           : ".ini_get('include_path')."
Configured directory           : $this->php_dir
Currently used php.ini (guess) : $php_ini
";
    
            print "Press Enter to continue: ";
            fgets($this->tty, 1024);
        }
    
        $pear_cmd = $this->bin_dir . DIRECTORY_SEPARATOR . 'pear';
        $pear_cmd = OS_WINDOWS ? strtolower($pear_cmd).'.bat' : $pear_cmd;
    
        // check that the installed pear and the one in the path are the same (if any)
        $pear_old = System::which(OS_WINDOWS ? 'pear.bat' : 'pear', $this->bin_dir);
        if ($pear_old && ($pear_old != $pear_cmd)) {
            // check if it is a link or symlink
            $islink = OS_WINDOWS ? false : is_link($pear_old) ;
            if ($islink && readlink($pear_old) != $pear_cmd) {
                print "\n** WARNING! The link $pear_old does not point to the " .
                      "installed $pear_cmd\n";
            } elseif (!$this->localInstall && is_writable($pear_old) && !is_dir($pear_old)) {
                rename($pear_old, "{$pear_old}_old");
                print "\n** WARNING! Backed up old pear to {$pear_old}_old\n";
            } else {
                print "\n** WARNING! Old version found at $pear_old, please remove it or ".
                      "be sure to use the new $pear_cmd command\n";
            }
        }
    
        print "\nThe 'pear' command is now at your service at $pear_cmd\n";
    
        // Alert the user if the pear cmd is not in PATH
        $old_dir = $pear_old ? dirname($pear_old) : false;
        if (!$this->which('pear', $old_dir)) {
            print "
** The 'pear' command is not currently in your PATH, so you need to
** use '$pear_cmd' until you have added
** '$this->bin_dir' to your PATH environment variable.

";
    
        print "Run it without parameters to see the available actions, try 'pear list'
to see what packages are installed, or 'pear help' for help.

For more information about PEAR, see:

  http://pear.php.net/faq.php
  http://pear.php.net/manual/

Thanks for using go-pear!

";
        }
    
        if (OS_WINDOWS && !$this->localInstall) {
            $this->win32CreateRegEnv();
        }
    }

    /**
     * System::which() does not allow path exclusion
     */
    function which($program, $dont_search_in = false)
    {
        if (OS_WINDOWS) {
            if ($_path = $this->safeGetEnv('Path')) {
                $dirs = explode(';', $_path);
            } else {
                $dirs = explode(';', $this->safeGetEnv('PATH'));
            }
            foreach ($dirs as $i => $dir) {
                $dirs[$i] = strtolower(realpath($dir));
            }
            if ($dont_search_in) {
                $dont_search_in = strtolower(realpath($dont_search_in));
            }
            if ($dont_search_in &&
                ($key = array_search($dont_search_in, $dirs)) !== false)
            {
                unset($dirs[$key]);
            }
    
            foreach ($dirs as $dir) {
                $dir = str_replace('\\\\', '\\', $dir);
                if (!strlen($dir)) {
                    continue;
                }
                if ($dir{strlen($dir) - 1} != '\\') {
                    $dir .= '\\';
                }
                $tmp = $dir . $program;
                $info = pathinfo($tmp);
                if (isset($info['extension']) && in_array(strtolower($info['extension']),
                      array('exe', 'com', 'bat', 'cmd'))) {
                    if (file_exists($tmp)) {
                        return strtolower($tmp);
                    }
                } elseif (file_exists($ret = $tmp . '.exe') ||
                    file_exists($ret = $tmp . '.com') ||
                    file_exists($ret = $tmp . '.bat') ||
                    file_exists($ret = $tmp . '.cmd')) {
                    return strtolower($ret);
                }
            }
        } else {
            $dirs = explode(':', $this->safeGetEnv('PATH'));
            if ($dont_search_in &&
                ($key = array_search($dont_search_in, $dirs)) !== false)
            {
                unset($dirs[$key]);
            }
            foreach ($dirs as $dir) {
                if (is_executable("$dir/$program")) {
                    return "$dir/$program";
                }
            }
        }
        return false;
    }

    /**
     * Not optimized, but seems to work, if some nice
     * peardev will test it? :)
     *
     * @author Pierre-Alain Joye <paj@pearfr.org>
     */
    function alterPhpIni($pathIni='')
    {
        $foundAt = array();
        $iniSep = OS_WINDOWS ? ';' : ':';

        if ($pathIni=='') {
            $pathIni =  $this->getPhpiniPath();
        }

        $arrayIni = file($pathIni);
        $i=0;
        $found=0;

        // Looks for each active include_path directives
        foreach ($arrayIni as $iniLine) {
            $iniLine = trim($iniLine);
            $iniLine = str_replace(array("\n", "\r"), array('', ''), $iniLine);
            if (preg_match("/^\s*include_path/", $iniLine)) {
                $foundAt[] = $i;
                $found++;
            }
            $i++;
        }

        if ($found) {
            $includeLine = $arrayIni[$foundAt[0]];
            list(, $currentPath) = explode('=', $includeLine);

            $currentPath = trim($currentPath);
            if (substr($currentPath,0,1) == '"') {
                $currentPath = substr($currentPath, 1, strlen($currentPath) - 2);
            }

            $arrayPath = explode($iniSep, $currentPath);
            $newPath = array();
            if ($arrayPath[0]=='.') {
                $newPath[0] = '.';
                $newPath[1] = $this->php_dir;
                array_shift($arrayPath);
            } else {
                $newPath[0] = $this->php_dir;
            }
    
            foreach ($arrayPath as $path) {
                $newPath[]= $path;
            }
        } else {
            $newPath = array();
            $newPath[0] = '.';
            $newPath[1] = $this->php_dir;
            $foundAt[] = count($arrayIni); // add a new line if none is present
        }
        $nl = OS_WINDOWS ? "\r\n" : "\n";
        $includepath = 'include_path="' . implode($iniSep,$newPath) . '"';
        $newInclude = "$nl$nl;***** Added by go-pear$nl" .
                       $includepath .
                       $nl . ";*****" .
                       $nl . $nl;

        $arrayIni[$foundAt[0]] = $newInclude;

        for ($i=1; $i<$found; $i++) {
            $arrayIni[$foundAt[$i]]=';' . trim($arrayIni[$foundAt[$i]]);
        }

        $newIni = implode("", $arrayIni);
        if (!($fh = @fopen($pathIni, "wb+"))) {
            $prefixIni = $this->prefix . DIRECTORY_SEPARATOR . "php.ini-gopear";
            $fh = fopen($prefixIni, "wb+");
            if (!$fh) {
                echo "
******************************************************************************
WARNING: Cannot write to $pathIni nor in $this->prefix/php.ini-gopear. Please
modify manually your php.ini by adding:

$includepath

";
                return false;
            } else {
                fwrite($fh, $newIni, strlen($newIni));
                fclose($fh);
                echo "
******************************************************************************
WARNING: Cannot write to $pathIni, but php.ini was successfully created
at <$this->prefix/php.ini-gopear>. Please replace the file <$pathIni> with
<$prefixIni> or modify your php.ini by adding:

$includepath

";

            }
        } else {
            fwrite($fh, $newIni, strlen($newIni));
            fclose($fh);
            echo "
php.ini <$pathIni> include_path updated.
";
        }
        return true;
    }

    /**
     * Generates a registry addOn for Win32 platform
     * This addon set PEAR environment variables
     * @author Pierrre-Alain Joye
     */
    function win32CreateRegEnv()
    {
        $nl = "\r\n";
        $reg ='REGEDIT4'.$nl.
                '[HKEY_CURRENT_USER\Environment]'. $nl .
                '"PHP_PEAR_SYSCONF_DIR"="' . addslashes($this->prefix) . '"' . $nl .
                '"PHP_PEAR_INSTALL_DIR"="' . addslashes($this->php_dir) . '"' . $nl .
                '"PHP_PEAR_DOC_DIR"="' . addslashes($this->doc_dir) . '"' . $nl .
                '"PHP_PEAR_BIN_DIR"="' . addslashes($this->bin_dir) . '"' . $nl .
                '"PHP_PEAR_DATA_DIR"="' . addslashes($this->data_dir) . '"' . $nl .
                '"PHP_PEAR_PHP_BIN"="' . addslashes($this->php_bin) . '"' . $nl .
                '"PHP_PEAR_TEST_DIR"="' . addslashes($this->test_dir) . '"' . $nl;
    
        $fh = fopen($this->prefix . DIRECTORY_SEPARATOR . 'PEAR_ENV.reg', 'wb');
        if($fh){
            fwrite($fh, $reg, strlen($reg));
            fclose($fh);
            echo "

* WINDOWS ENVIRONMENT VARIABLES *
For convenience, a REG file is available under $this->prefix\\PEAR_ENV.reg .
This file creates ENV variables for the current user.

Double-click this file to add it to the current user registry.

";
        }
    }

    function displayHTMLProgress()
    {
    }
}
?><?php
/**
 * PEAR_Task_Common, base class for installer tasks
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Common.php,v 1.18 2008/05/13 21:28:20 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**#@+
 * Error codes for task validation routines
 */
define('PEAR_TASK_ERROR_NOATTRIBS', 1);
define('PEAR_TASK_ERROR_MISSING_ATTRIB', 2);
define('PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE', 3);
define('PEAR_TASK_ERROR_INVALID', 4);
/**#@-*/
define('PEAR_TASK_PACKAGE', 1);
define('PEAR_TASK_INSTALL', 2);
define('PEAR_TASK_PACKAGEANDINSTALL', 3);
/**
 * A task is an operation that manipulates the contents of a file.
 *
 * Simple tasks operate on 1 file.  Multiple tasks are executed after all files have been
 * processed and installed, and are designed to operate on all files containing the task.
 * The Post-install script task simply takes advantage of the fact that it will be run
 * after installation, replace is a simple task.
 *
 * Combining tasks is possible, but ordering is significant.
 *
 * <file name="test.php" role="php">
 *  <tasks:replace from="@data-dir@" to="data_dir" type="pear-config"/>
 *  <tasks:postinstallscript/>
 * </file>
 *
 * This will first replace any instance of @data-dir@ in the test.php file
 * with the path to the current data directory.  Then, it will include the
 * test.php file and run the script it contains to configure the package post-installation.
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 * @abstract
 */
class PEAR_Task_Common
{
    /**
     * Valid types for this version are 'simple' and 'multiple'
     *
     * - simple tasks operate on the contents of a file and write out changes to disk
     * - multiple tasks operate on the contents of many files and write out the
     *   changes directly to disk
     *
     * Child task classes must override this property.
     * @access protected
     */
    var $type = 'simple';
    /**
     * Determines which install phase this task is executed under
     */
    var $phase = PEAR_TASK_INSTALL;
    /**
     * @access protected
     */
    var $config;
    /**
     * @access protected
     */
    var $registry;
    /**
     * @access protected
     */
    var $logger;
    /**
     * @access protected
     */
    var $installphase;
    /**
     * @param PEAR_Config
     * @param PEAR_Common
     */
    function PEAR_Task_Common(&$config, &$logger, $phase)
    {
        $this->config = &$config;
        $this->registry = &$config->getRegistry();
        $this->logger = &$logger;
        $this->installphase = $phase;
        if ($this->type == 'multiple') {
            $GLOBALS['_PEAR_TASK_POSTINSTANCES'][get_class($this)][] = &$this;
        }
    }

    /**
     * Validate the basic contents of a task tag.
     * @param PEAR_PackageFile_v2
     * @param array
     * @param PEAR_Config
     * @param array the entire parsed <file> tag
     * @return true|array On error, return an array in format:
     *    array(PEAR_TASK_ERROR_???[, param1][, param2][, ...])
     *
     *    For PEAR_TASK_ERROR_MISSING_ATTRIB, pass the attribute name in
     *    For PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, pass the attribute name and an array
     *    of legal values in
     * @static
     * @abstract
     */
    function validateXml($pkg, $xml, $config, $fileXml)
    {
    }

    /**
     * Initialize a task instance with the parameters
     * @param array raw, parsed xml
     * @param array attributes from the <file> tag containing this task
     * @param string|null last installed version of this package
     * @abstract
     */
    function init($xml, $fileAttributes, $lastVersion)
    {
    }

    /**
     * Begin a task processing session.  All multiple tasks will be processed after each file
     * has been successfully installed, all simple tasks should perform their task here and
     * return any errors using the custom throwError() method to allow forward compatibility
     *
     * This method MUST NOT write out any changes to disk
     * @param PEAR_PackageFile_v2
     * @param string file contents
     * @param string the eventual final file location (informational only)
     * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
     *         (use $this->throwError), otherwise return the new contents
     * @abstract
     */
    function startSession($pkg, $contents, $dest)
    {
    }

    /**
     * This method is used to process each of the tasks for a particular multiple class
     * type.  Simple tasks need not implement this method.
     * @param array an array of tasks
     * @access protected
     * @static
     * @abstract
     */
    function run($tasks)
    {
    }

    /**
     * @static
     * @final
     */
    function hasPostinstallTasks()
    {
        return isset($GLOBALS['_PEAR_TASK_POSTINSTANCES']);
    }

    /**
     * @static
     * @final
     */
     function runPostinstallTasks()
     {
         foreach ($GLOBALS['_PEAR_TASK_POSTINSTANCES'] as $class => $tasks) {
             $err = call_user_func(array($class, 'run'),
                  $GLOBALS['_PEAR_TASK_POSTINSTANCES'][$class]);
             if ($err) {
                 return PEAR_Task_Common::throwError($err);
             }
         }
         unset($GLOBALS['_PEAR_TASK_POSTINSTANCES']);
    }

    /**
     * Determines whether a role is a script
     * @return bool
     */
    function isScript()
    {
        return $this->type == 'script';
    }

    function throwError($msg, $code = -1)
    {
        include_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
        return PEAR::raiseError($msg, $code);
    }
}
?><?php
/**
 * <tasks:postinstallscript>
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Postinstallscript.php,v 1.20 2008/05/13 21:28:20 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * Base class
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Common.php';
/**
 * Implements the postinstallscript file task.
 *
 * Note that post-install scripts are handled separately from installation, by the
 * "pear run-scripts" command
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Task_Postinstallscript extends PEAR_Task_Common
{
    var $type = 'script';
    var $_class;
    var $_params;
    var $_obj;
    /**
     *
     * @var PEAR_PackageFile_v2
     */
    var $_pkg;
    var $_contents;
    var $phase = PEAR_TASK_INSTALL;

    /**
     * Validate the raw xml at parsing-time.
     *
     * This also attempts to validate the script to make sure it meets the criteria
     * for a post-install script
     * @param PEAR_PackageFile_v2
     * @param array The XML contents of the <postinstallscript> tag
     * @param PEAR_Config
     * @param array the entire parsed <file> tag
     * @static
     */
    function validateXml($pkg, $xml, $config, $fileXml)
    {
        if ($fileXml['role'] != 'php') {
            return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
            $fileXml['name'] . '" must be role="php"');
        }
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        $file = $pkg->getFileContents($fileXml['name']);
        if (PEAR::isError($file)) {
            PEAR::popErrorHandling();
            return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                $fileXml['name'] . '" is not valid: ' .
                $file->getMessage());
        } elseif ($file === null) {
            return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                $fileXml['name'] . '" could not be retrieved for processing!');
        } else {
            $analysis = $pkg->analyzeSourceCode($file, true);
            if (!$analysis) {
                PEAR::popErrorHandling();
                $warnings = '';
                foreach ($pkg->getValidationWarnings() as $warn) {
                    $warnings .= $warn['message'] . "\n";
                }
                return array(PEAR_TASK_ERROR_INVALID, 'Analysis of post-install script "' .
                    $fileXml['name'] . '" failed: ' . $warnings);
            }
            if (count($analysis['declared_classes']) != 1) {
                PEAR::popErrorHandling();
                return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                    $fileXml['name'] . '" must declare exactly 1 class');
            }
            $class = $analysis['declared_classes'][0];
            if ($class != str_replace(array('/', '.php'), array('_', ''),
                  $fileXml['name']) . '_postinstall') {
                PEAR::popErrorHandling();
                return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                    $fileXml['name'] . '" class "' . $class . '" must be named "' .
                    str_replace(array('/', '.php'), array('_', ''),
                    $fileXml['name']) . '_postinstall"');
            }
            if (!isset($analysis['declared_methods'][$class])) {
                PEAR::popErrorHandling();
                return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                    $fileXml['name'] . '" must declare methods init() and run()');
            }
            $methods = array('init' => 0, 'run' => 1);
            foreach ($analysis['declared_methods'][$class] as $method) {
                if (isset($methods[$method])) {
                    unset($methods[$method]);
                }
            }
            if (count($methods)) {
                PEAR::popErrorHandling();
                return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                    $fileXml['name'] . '" must declare methods init() and run()');
            }
        }
        PEAR::popErrorHandling();
        $definedparams = array();
        $tasksNamespace = $pkg->getTasksNs() . ':';
        if (!isset($xml[$tasksNamespace . 'paramgroup']) && isset($xml['paramgroup'])) {
            // in order to support the older betas, which did not expect internal tags
            // to also use the namespace
            $tasksNamespace = '';
        }
        if (isset($xml[$tasksNamespace . 'paramgroup'])) {
            $params = $xml[$tasksNamespace . 'paramgroup'];
            if (!is_array($params) || !isset($params[0])) {
                $params = array($params);
            }
            foreach ($params as $param) {
                if (!isset($param[$tasksNamespace . 'id'])) {
                    return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                        $fileXml['name'] . '" <paramgroup> must have ' .
                        'an ' . $tasksNamespace . 'id> tag');
                }
                if (isset($param[$tasksNamespace . 'name'])) {
                    if (!in_array($param[$tasksNamespace . 'name'], $definedparams)) {
                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                            $fileXml['name'] . '" ' . $tasksNamespace .
                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
                            '" parameter "' . $param[$tasksNamespace . 'name'] .
                            '" has not been previously defined');
                    }
                    if (!isset($param[$tasksNamespace . 'conditiontype'])) {
                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                            $fileXml['name'] . '" ' . $tasksNamespace .
                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
                            '" must have a ' . $tasksNamespace . 
                            'conditiontype> tag containing either "=", ' .
                            '"!=", or "preg_match"');
                    }
                    if (!in_array($param[$tasksNamespace . 'conditiontype'],
                          array('=', '!=', 'preg_match'))) {
                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                            $fileXml['name'] . '" ' . $tasksNamespace .
                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
                            '" must have a ' . $tasksNamespace .
                            'conditiontype> tag containing either "=", ' .
                            '"!=", or "preg_match"');
                    }
                    if (!isset($param[$tasksNamespace . 'value'])) {
                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                            $fileXml['name'] . '" ' . $tasksNamespace .
                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
                            '" must have a ' . $tasksNamespace .
                            'value> tag containing expected parameter value');
                    }
                }
                if (isset($param[$tasksNamespace . 'instructions'])) {
                    if (!is_string($param[$tasksNamespace . 'instructions'])) {
                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                            $fileXml['name'] . '" ' . $tasksNamespace .
                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
                            '" ' . $tasksNamespace . 'instructions> must be simple text');
                    }
                }
                if (!isset($param[$tasksNamespace . 'param'])) {
                    continue; // <param> is no longer required
                }
                $subparams = $param[$tasksNamespace . 'param'];
                if (!is_array($subparams) || !isset($subparams[0])) {
                    $subparams = array($subparams);
                }
                foreach ($subparams as $subparam) {
                    if (!isset($subparam[$tasksNamespace . 'name'])) {
                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                            $fileXml['name'] . '" parameter for ' .
                            $tasksNamespace . 'paramgroup> id "' .
                            $param[$tasksNamespace . 'id'] . '" must have ' .
                            'a ' . $tasksNamespace . 'name> tag');
                    }
                    if (!preg_match('/[a-zA-Z0-9]+/',
                          $subparam[$tasksNamespace . 'name'])) {
                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                            $fileXml['name'] . '" parameter "' .
                            $subparam[$tasksNamespace . 'name'] .
                            '" for ' . $tasksNamespace . 'paramgroup> id "' .
                            $param[$tasksNamespace . 'id'] .
                            '" is not a valid name.  Must contain only alphanumeric characters');
                    }
                    if (!isset($subparam[$tasksNamespace . 'prompt'])) {
                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                            $fileXml['name'] . '" parameter "' .
                            $subparam[$tasksNamespace . 'name'] .
                            '" for ' . $tasksNamespace . 'paramgroup> id "' .
                            $param[$tasksNamespace . 'id'] .
                            '" must have a ' . $tasksNamespace . 'prompt> tag');
                    }
                    if (!isset($subparam[$tasksNamespace . 'type'])) {
                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
                            $fileXml['name'] . '" parameter "' .
                            $subparam[$tasksNamespace . 'name'] .
                            '" for ' . $tasksNamespace . 'paramgroup> id "' .
                            $param[$tasksNamespace . 'id'] .
                            '" must have a ' . $tasksNamespace . 'type> tag');
                    }
                    $definedparams[] = $param[$tasksNamespace . 'id'] . '::' .
                    $subparam[$tasksNamespace . 'name'];
                }
            }
        }
        return true;
    }

    /**
     * Initialize a task instance with the parameters
     * @param array raw, parsed xml
     * @param array attributes from the <file> tag containing this task
     * @param string|null last installed version of this package, if any (useful for upgrades)
     */
    function init($xml, $fileattribs, $lastversion)
    {
        $this->_class = str_replace('/', '_', $fileattribs['name']);
        $this->_filename = $fileattribs['name'];
        $this->_class = str_replace ('.php', '', $this->_class) . '_postinstall';
        $this->_params = $xml;
        $this->_lastversion = $lastversion;
    }

    /**
     * Strip the tasks: namespace from internal params
     *
     * @access private
     */
    function _stripNamespace($params = null)
    {
        if ($params === null) {
            $params = array();
            if (!is_array($this->_params)) {
                return;
            }
            foreach ($this->_params as $i => $param) {
                if (is_array($param)) {
                    $param = $this->_stripNamespace($param);
                }
                $params[str_replace($this->_pkg->getTasksNs() . ':', '', $i)] = $param;
            }
            $this->_params = $params;
        } else {
            $newparams = array();
            foreach ($params as $i => $param) {
                if (is_array($param)) {
                    $param = $this->_stripNamespace($param);
                }
                $newparams[str_replace($this->_pkg->getTasksNs() . ':', '', $i)] = $param;
            }
            return $newparams;
        }
    }

    /**
     * Unlike other tasks, the installed file name is passed in instead of the file contents,
     * because this task is handled post-installation
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @param string file name
     * @return bool|PEAR_Error false to skip this file, PEAR_Error to fail
     *         (use $this->throwError)
     */
    function startSession($pkg, $contents)
    {
        if ($this->installphase != PEAR_TASK_INSTALL) {
            return false;
        }
        // remove the tasks: namespace if present
        $this->_pkg = $pkg;
        $this->_stripNamespace();
        $this->logger->log(0, 'Including external post-installation script "' .
            $contents . '" - any errors are in this script');
        include_once 'phar://install-pear-nozlib.phar/' . $contents;
        if (class_exists($this->_class)) {
            $this->logger->log(0, 'Inclusion succeeded');
        } else {
            return $this->throwError('init of post-install script class "' . $this->_class
                . '" failed');
        }
        $this->_obj = new $this->_class;
        $this->logger->log(1, 'running post-install script "' . $this->_class . '->init()"');
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        $res = $this->_obj->init($this->config, $pkg, $this->_lastversion);
        PEAR::popErrorHandling();
        if ($res) {
            $this->logger->log(0, 'init succeeded');
        } else {
            return $this->throwError('init of post-install script "' . $this->_class .
                '->init()" failed');
        }
        $this->_contents = $contents;
        return true;
    }

    /**
     * No longer used
     * @see PEAR_PackageFile_v2::runPostinstallScripts()
     * @param array an array of tasks
     * @param string install or upgrade
     * @access protected
     * @static
     */
    function run()
    {
    }
}
?><?php
/**
 * <tasks:postinstallscript> - read/write version
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: rw.php,v 1.12 2008/01/03 20:26:37 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a10
 */
/**
 * Base class
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Postinstallscript.php';
/**
 * Abstracts the postinstallscript file task xml.
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a10
 */
class PEAR_Task_Postinstallscript_rw extends PEAR_Task_Postinstallscript
{
    /**
     * parent package file object
     *
     * @var PEAR_PackageFile_v2_rw
     */
    var $_pkg;
    /**
     * Enter description here...
     *
     * @param PEAR_PackageFile_v2_rw $pkg
     * @param PEAR_Config $config
     * @param PEAR_Frontend $logger
     * @param array $fileXml
     * @return PEAR_Task_Postinstallscript_rw
     */
    function PEAR_Task_Postinstallscript_rw(&$pkg, &$config, &$logger, $fileXml)
    {
        parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE);
        $this->_contents = $fileXml;
        $this->_pkg = &$pkg;
        $this->_params = array();
    }

    function validate()
    {
        return $this->validateXml($this->_pkg, $this->_params, $this->config, $this->_contents);
    }

    function getName()
    {
        return 'postinstallscript';
    }

    /**
     * add a simple <paramgroup> to the post-install script
     *
     * Order is significant, so call this method in the same
     * sequence the users should see the paramgroups.  The $params
     * parameter should either be the result of a call to {@link getParam()}
     * or an array of calls to getParam().
     * 
     * Use {@link addConditionTypeGroup()} to add a <paramgroup> containing
     * a <conditiontype> tag
     * @param string $id <paramgroup> id as seen by the script
     * @param array|false $params array of getParam() calls, or false for no params
     * @param string|false $instructions
     */
    function addParamGroup($id, $params = false, $instructions = false)
    {
        if ($params && isset($params[0]) && !isset($params[1])) {
            $params = $params[0];
        }
        $stuff =
            array(
                $this->_pkg->getTasksNs() . ':id' => $id,
            );
        if ($instructions) {
            $stuff[$this->_pkg->getTasksNs() . ':instructions'] = $instructions;
        }
        if ($params) {
            $stuff[$this->_pkg->getTasksNs() . ':param'] = $params;
        }
        $this->_params[$this->_pkg->getTasksNs() . ':paramgroup'][] = $stuff;
    }

    /**
     * add a complex <paramgroup> to the post-install script with conditions
     *
     * This inserts a <paramgroup> with
     *
     * Order is significant, so call this method in the same
     * sequence the users should see the paramgroups.  The $params
     * parameter should either be the result of a call to {@link getParam()}
     * or an array of calls to getParam().
     * 
     * Use {@link addParamGroup()} to add a simple <paramgroup>
     *
     * @param string $id <paramgroup> id as seen by the script
     * @param string $oldgroup <paramgroup> id of the section referenced by
     *                         <conditiontype>
     * @param string $param name of the <param> from the older section referenced
     *                      by <contitiontype>
     * @param string $value value to match of the parameter
     * @param string $conditiontype one of '=', '!=', 'preg_match'
     * @param array|false $params array of getParam() calls, or false for no params
     * @param string|false $instructions
     */
    function addConditionTypeGroup($id, $oldgroup, $param, $value, $conditiontype = '=',
                                   $params = false, $instructions = false)
    {
        if ($params && isset($params[0]) && !isset($params[1])) {
            $params = $params[0];
        }
        $stuff =
            array(
                $this->_pkg->getTasksNs() . ':id' => $id,
            );
        if ($instructions) {
            $stuff[$this->_pkg->getTasksNs() . ':instructions'] = $instructions;
        }
        $stuff[$this->_pkg->getTasksNs() . ':name'] = $oldgroup . '::' . $param;
        $stuff[$this->_pkg->getTasksNs() . ':conditiontype'] = $conditiontype;
        $stuff[$this->_pkg->getTasksNs() . ':value'] = $value;
        if ($params) {
            $stuff[$this->_pkg->getTasksNs() . ':param'] = $params;
        }
        $this->_params[$this->_pkg->getTasksNs() . ':paramgroup'][] = $stuff;
    }

    function getXml()
    {
        return $this->_params;
    }

    /**
     * Use to set up a param tag for use in creating a paramgroup
     * @static
     */
    function getParam($name, $prompt, $type = 'string', $default = null)
    {
        if ($default !== null) {
            return 
            array(
                $this->_pkg->getTasksNs() . ':name' => $name,
                $this->_pkg->getTasksNs() . ':prompt' => $prompt,
                $this->_pkg->getTasksNs() . ':type' => $type,
                $this->_pkg->getTasksNs() . ':default' => $default
            );
        }
        return
            array(
                $this->_pkg->getTasksNs() . ':name' => $name,
                $this->_pkg->getTasksNs() . ':prompt' => $prompt,
                $this->_pkg->getTasksNs() . ':type' => $type,
            );
    }
}
?><?php
/**
 * <tasks:replace>
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Replace.php,v 1.17 2008/05/13 21:28:20 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * Base class
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Common.php';
/**
 * Implements the replace file task.
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Task_Replace extends PEAR_Task_Common
{
    var $type = 'simple';
    var $phase = PEAR_TASK_PACKAGEANDINSTALL;
    var $_replacements;

    /**
     * Validate the raw xml at parsing-time.
     * @param PEAR_PackageFile_v2
     * @param array raw, parsed xml
     * @param PEAR_Config
     * @static
     */
    function validateXml($pkg, $xml, $config, $fileXml)
    {
        if (!isset($xml['attribs'])) {
            return array(PEAR_TASK_ERROR_NOATTRIBS);
        }
        if (!isset($xml['attribs']['type'])) {
            return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'type');
        }
        if (!isset($xml['attribs']['to'])) {
            return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'to');
        }
        if (!isset($xml['attribs']['from'])) {
            return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'from');
        }
        if ($xml['attribs']['type'] == 'pear-config') {
            if (!in_array($xml['attribs']['to'], $config->getKeys())) {
                return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'],
                    $config->getKeys());
            }
        } elseif ($xml['attribs']['type'] == 'php-const') {
            if (defined($xml['attribs']['to'])) {
                return true;
            } else {
                return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'],
                    array('valid PHP constant'));
            }
        } elseif ($xml['attribs']['type'] == 'package-info') {
            if (in_array($xml['attribs']['to'],
                array('name', 'summary', 'channel', 'notes', 'extends', 'description',
                    'release_notes', 'license', 'release-license', 'license-uri',
                    'version', 'api-version', 'state', 'api-state', 'release_date',
                    'date', 'time'))) {
                return true;
            } else {
                return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'],
                    array('name', 'summary', 'channel', 'notes', 'extends', 'description',
                    'release_notes', 'license', 'release-license', 'license-uri',
                    'version', 'api-version', 'state', 'api-state', 'release_date',
                    'date', 'time'));
            }
        } else {
            return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'type', $xml['attribs']['type'],
                array('pear-config', 'package-info', 'php-const'));
        }
        return true;
    }

    /**
     * Initialize a task instance with the parameters
     * @param array raw, parsed xml
     * @param unused
     */
    function init($xml, $attribs)
    {
        $this->_replacements = isset($xml['attribs']) ? array($xml) : $xml;
    }

    /**
     * Do a package.xml 1.0 replacement, with additional package-info fields available
     *
     * See validateXml() source for the complete list of allowed fields
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @param string file contents
     * @param string the eventual final file location (informational only)
     * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
     *         (use $this->throwError), otherwise return the new contents
     */
    function startSession($pkg, $contents, $dest)
    {
        $subst_from = $subst_to = array();
        foreach ($this->_replacements as $a) {
            $a = $a['attribs'];
            $to = '';
            if ($a['type'] == 'pear-config') {
                if ($this->installphase == PEAR_TASK_PACKAGE) {
                    return false;
                }
                if ($a['to'] == 'master_server') {
                    $chan = $this->registry->getChannel($pkg->getChannel());
                    if (!PEAR::isError($chan)) {
                        $to = $chan->getServer();
                    } else {
                        $this->logger->log(0, "$dest: invalid pear-config replacement: $a[to]");
                        return false;
                    }
                } else {
                    if ($this->config->isDefinedLayer('ftp')) {
                        // try the remote config file first
                        $to = $this->config->get($a['to'], 'ftp', $pkg->getChannel());
                        if (is_null($to)) {
                            // then default to local
                            $to = $this->config->get($a['to'], null, $pkg->getChannel());
                        }
                    } else {
                        $to = $this->config->get($a['to'], null, $pkg->getChannel());
                    }
                }
                if (is_null($to)) {
                    $this->logger->log(0, "$dest: invalid pear-config replacement: $a[to]");
                    return false;
                }
            } elseif ($a['type'] == 'php-const') {
                if ($this->installphase == PEAR_TASK_PACKAGE) {
                    return false;
                }
                if (defined($a['to'])) {
                    $to = constant($a['to']);
                } else {
                    $this->logger->log(0, "$dest: invalid php-const replacement: $a[to]");
                    return false;
                }
            } else {
                if ($t = $pkg->packageInfo($a['to'])) {
                    $to = $t;
                } else {
                    $this->logger->log(0, "$dest: invalid package-info replacement: $a[to]");
                    return false;
                }
            }
            if (!is_null($to)) {
                $subst_from[] = $a['from'];
                $subst_to[] = $to;
            }
        }
        $this->logger->log(3, "doing " . sizeof($subst_from) .
            " substitution(s) for $dest");
        if (sizeof($subst_from)) {
            $contents = str_replace($subst_from, $subst_to, $contents);
        }
        return $contents;
    }
}
?><?php
/**
 * <tasks:replace> - read/write version
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: rw.php,v 1.4 2008/01/03 20:26:37 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a10
 */
/**
 * Base class
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Replace.php';
/**
 * Abstracts the replace task xml.
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a10
 */
class PEAR_Task_Replace_rw extends PEAR_Task_Replace
{
    function PEAR_Task_Replace_rw(&$pkg, &$config, &$logger, $fileXml)
    {
        parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE);
        $this->_contents = $fileXml;
        $this->_pkg = &$pkg;
        $this->_params = array();
    }

    function validate()
    {
        return $this->validateXml($this->_pkg, $this->_params, $this->config, $this->_contents);
    }

    function setInfo($from, $to, $type)
    {
        $this->_params = array('attribs' => array('from' => $from, 'to' => $to, 'type' => $type));
    }

    function getName()
    {
        return 'replace';
    }

    function getXml()
    {
        return $this->_params;
    }
}
?><?php
/**
 * <tasks:unixeol>
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Unixeol.php,v 1.10 2008/05/13 21:28:20 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * Base class
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Common.php';
/**
 * Implements the unix line endings file task.
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Task_Unixeol extends PEAR_Task_Common
{
    var $type = 'simple';
    var $phase = PEAR_TASK_PACKAGE;
    var $_replacements;

    /**
     * Validate the raw xml at parsing-time.
     * @param PEAR_PackageFile_v2
     * @param array raw, parsed xml
     * @param PEAR_Config
     * @static
     */
    function validateXml($pkg, $xml, $config, $fileXml)
    {
        if ($xml != '') {
            return array(PEAR_TASK_ERROR_INVALID, 'no attributes allowed');
        }
        return true;
    }

    /**
     * Initialize a task instance with the parameters
     * @param array raw, parsed xml
     * @param unused
     */
    function init($xml, $attribs)
    {
    }

    /**
     * Replace all line endings with line endings customized for the current OS
     *
     * See validateXml() source for the complete list of allowed fields
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @param string file contents
     * @param string the eventual final file location (informational only)
     * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
     *         (use $this->throwError), otherwise return the new contents
     */
    function startSession($pkg, $contents, $dest)
    {
        $this->logger->log(3, "replacing all line endings with \\n in $dest");
        return preg_replace("/\r\n|\n\r|\r|\n/", "\n", $contents);
    }
}
?><?php
/**
 * <tasks:unixeol> - read/write version
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: rw.php,v 1.5 2008/01/03 20:26:37 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a10
 */
/**
 * Base class
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Unixeol.php';
/**
 * Abstracts the unixeol task xml.
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a10
 */
class PEAR_Task_Unixeol_rw extends PEAR_Task_Unixeol
{
    function PEAR_Task_Unixeol_rw(&$pkg, &$config, &$logger, $fileXml)
    {
        parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE);
        $this->_contents = $fileXml;
        $this->_pkg = &$pkg;
        $this->_params = array();
    }

    function validate()
    {
        return true;
    }

    function getName()
    {
        return 'unixeol';
    }

    function getXml()
    {
        return '';
    }
}
?><?php
/**
 * <tasks:windowseol>
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Windowseol.php,v 1.9 2008/05/13 21:28:20 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**
 * Base class
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Common.php';
/**
 * Implements the windows line endsings file task.
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Task_Windowseol extends PEAR_Task_Common
{
    var $type = 'simple';
    var $phase = PEAR_TASK_PACKAGE;
    var $_replacements;

    /**
     * Validate the raw xml at parsing-time.
     * @param PEAR_PackageFile_v2
     * @param array raw, parsed xml
     * @param PEAR_Config
     * @static
     */
    function validateXml($pkg, $xml, $config, $fileXml)
    {
        if ($xml != '') {
            return array(PEAR_TASK_ERROR_INVALID, 'no attributes allowed');
        }
        return true;
    }

    /**
     * Initialize a task instance with the parameters
     * @param array raw, parsed xml
     * @param unused
     */
    function init($xml, $attribs)
    {
    }

    /**
     * Replace all line endings with windows line endings
     *
     * See validateXml() source for the complete list of allowed fields
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @param string file contents
     * @param string the eventual final file location (informational only)
     * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
     *         (use $this->throwError), otherwise return the new contents
     */
    function startSession($pkg, $contents, $dest)
    {
        $this->logger->log(3, "replacing all line endings with \\r\\n in $dest");
        return preg_replace("/\r\n|\n\r|\r|\n/", "\r\n", $contents);
    }
}
?><?php
/**
 * <tasks:windowseol> - read/write version
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: rw.php,v 1.5 2008/01/03 20:26:37 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a10
 */
/**
 * Base class
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Windowseol.php';
/**
 * Abstracts the windowseol task xml.
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a10
 */
class PEAR_Task_Windowseol_rw extends PEAR_Task_Windowseol
{
    function PEAR_Task_Windowseol_rw(&$pkg, &$config, &$logger, $fileXml)
    {
        parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE);
        $this->_contents = $fileXml;
        $this->_pkg = &$pkg;
        $this->_params = array();
    }

    function validate()
    {
        return true;
    }

    function getName()
    {
        return 'windowseol';
    }

    function getXml()
    {
        return '';
    }
}
?><?php
/**
 * PEAR_Validate
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Validate.php,v 1.52 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */
/**#@+
 * Constants for install stage
 */
define('PEAR_VALIDATE_INSTALLING', 1);
define('PEAR_VALIDATE_UNINSTALLING', 2); // this is not bit-mapped like the others
define('PEAR_VALIDATE_NORMAL', 3);
define('PEAR_VALIDATE_DOWNLOADING', 4); // this is not bit-mapped like the others
define('PEAR_VALIDATE_PACKAGING', 7);
/**#@-*/
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Common.php';
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Validator/PECL.php';

/**
 * Validation class for package.xml - channel-level advanced validation
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_Validate
{
    var $packageregex = _PEAR_COMMON_PACKAGE_NAME_PREG;
    /**
     * @var PEAR_PackageFile_v1|PEAR_PackageFile_v2
     */
    var $_packagexml;
    /**
     * @var int one of the PEAR_VALIDATE_* constants
     */
    var $_state = PEAR_VALIDATE_NORMAL;
    /**
     * Format: ('error' => array('field' => name, 'reason' => reason), 'warning' => same)
     * @var array
     * @access private
     */
    var $_failures = array('error' => array(), 'warning' => array());

    /**
     * Override this method to handle validation of normal package names
     * @param string
     * @return bool
     * @access protected
     */
    function _validPackageName($name)
    {
        return (bool) preg_match('/^' . $this->packageregex . '\\z/', $name);
    }

    /**
     * @param string package name to validate
     * @param string name of channel-specific validation package
     * @final
     */
    function validPackageName($name, $validatepackagename = false)
    {
        if ($validatepackagename) {
            if (strtolower($name) == strtolower($validatepackagename)) {
                return (bool) preg_match('/^[a-zA-Z0-9_]+(?:\.[a-zA-Z0-9_]+)*\\z/', $name);
            }
        }
        return $this->_validPackageName($name);
    }

    /**
     * This validates a bundle name, and bundle names must conform
     * to the PEAR naming convention, so the method is final and static.
     * @param string
     * @final
     * @static
     */
    function validGroupName($name)
    {
        return (bool) preg_match('/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '\\z/', $name);
    }

    /**
     * Determine whether $state represents a valid stability level
     * @param string
     * @return bool
     * @static
     * @final
     */
    function validState($state)
    {
        return in_array($state, array('snapshot', 'devel', 'alpha', 'beta', 'stable'));
    }

    /**
     * Get a list of valid stability levels
     * @return array
     * @static
     * @final
     */
    function getValidStates()
    {
        return array('snapshot', 'devel', 'alpha', 'beta', 'stable');
    }

    /**
     * Determine whether a version is a properly formatted version number that can be used
     * by version_compare
     * @param string
     * @return bool
     * @static
     * @final
     */
    function validVersion($ver)
    {
        return (bool) preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver);
    }

    /**
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     */
    function setPackageFile(&$pf)
    {
        $this->_packagexml = &$pf;
    }

    /**
     * @access private
     */
    function _addFailure($field, $reason)
    {
        $this->_failures['errors'][] = array('field' => $field, 'reason' => $reason);
    }

    /**
     * @access private
     */
    function _addWarning($field, $reason)
    {
        $this->_failures['warnings'][] = array('field' => $field, 'reason' => $reason);
    }

    function getFailures()
    {
        $failures = $this->_failures;
        $this->_failures = array('warnings' => array(), 'errors' => array());
        return $failures;
    }

    /**
     * @param int one of the PEAR_VALIDATE_* constants
     */
    function validate($state = null)
    {
        if (!isset($this->_packagexml)) {
            return false;
        }
        if ($state !== null) {
            $this->_state = $state;
        }
        $this->_failures = array('warnings' => array(), 'errors' => array());
        $this->validatePackageName();
        $this->validateVersion();
        $this->validateMaintainers();
        $this->validateDate();
        $this->validateSummary();
        $this->validateDescription();
        $this->validateLicense();
        $this->validateNotes();
        if ($this->_packagexml->getPackagexmlVersion() == '1.0') {
            $this->validateState();
            $this->validateFilelist();
        } elseif ($this->_packagexml->getPackagexmlVersion() == '2.0' ||
                  $this->_packagexml->getPackagexmlVersion() == '2.1') {
            $this->validateTime();
            $this->validateStability();
            $this->validateDeps();
            $this->validateMainFilelist();
            $this->validateReleaseFilelist();
            //$this->validateGlobalTasks();
            $this->validateChangelog();
        }
        return !((bool) count($this->_failures['errors']));
    }

    /**
     * @access protected
     */
    function validatePackageName()
    {
        if ($this->_state == PEAR_VALIDATE_PACKAGING ||
              $this->_state == PEAR_VALIDATE_NORMAL) {
            if (($this->_packagexml->getPackagexmlVersion() == '2.0' ||
                 $this->_packagexml->getPackagexmlVersion() == '2.1') &&
                  $this->_packagexml->getExtends()) {
                $version = $this->_packagexml->getVersion() . '';
                $name = $this->_packagexml->getPackage();
                $test = array_shift($a = explode('.', $version));
                if ($test == '0') {
                    return true;
                }
                $vlen = strlen($test);
                $majver = substr($name, strlen($name) - $vlen);
                while ($majver && !is_numeric($majver{0})) {
                    $majver = substr($majver, 1);
                }
                if ($majver != $test) {
                    $this->_addWarning('package', "package $name extends package " .
                        $this->_packagexml->getExtends() . ' and so the name should ' .
                        'have a postfix equal to the major version like "' .
                        $this->_packagexml->getExtends() . $test . '"');
                    return true;
                } elseif (substr($name, 0, strlen($name) - $vlen) !=
                            $this->_packagexml->getExtends()) {
                    $this->_addWarning('package', "package $name extends package " .
                        $this->_packagexml->getExtends() . ' and so the name must ' .
                        'be an extension like "' . $this->_packagexml->getExtends() .
                        $test . '"');
                    return true;
                }
            }
        }
        if (!$this->validPackageName($this->_packagexml->getPackage())) {
            $this->_addFailure('name', 'package name "' .
                $this->_packagexml->getPackage() . '" is invalid');
            return false;
        }
    }

    /**
     * @access protected
     */
    function validateVersion()
    {
        if ($this->_state != PEAR_VALIDATE_PACKAGING) {
            if (!$this->validVersion($this->_packagexml->getVersion())) {
                $this->_addFailure('version',
                    'Invalid version number "' . $this->_packagexml->getVersion() . '"');
            }
            return false;
        }
        $version = $this->_packagexml->getVersion();
        $versioncomponents = explode('.', $version);
        if (count($versioncomponents) != 3) {
            $this->_addWarning('version',
                'A version number should have 3 decimals (x.y.z)');
            return true;
        }
        $name = $this->_packagexml->getPackage();
        // version must be based upon state
        switch ($this->_packagexml->getState()) {
            case 'snapshot' :
                return true;
            case 'devel' :
                if ($versioncomponents[0] . 'a' == '0a') {
                    return true;
                }
                if ($versioncomponents[0] == 0) {
                    $versioncomponents[0] = '0';
                    $this->_addWarning('version',
                        'version "' . $version . '" should be "' .
                        implode('.' ,$versioncomponents) . '"');
                } else {
                    $this->_addWarning('version',
                        'packages with devel stability must be < version 1.0.0');
                }
                return true;
            break;
            case 'alpha' :
            case 'beta' :
                // check for a package that extends a package,
                // like Foo and Foo2
                if ($this->_state == PEAR_VALIDATE_PACKAGING) {
                    if (substr($versioncomponents[2], 1, 2) == 'rc') {
                        $this->_addFailure('version', 'Release Candidate versions ' .
                            'must have capital RC, not lower-case rc');
                        return false;
                    }
                }
                if (!$this->_packagexml->getExtends()) {
                    if ($versioncomponents[0] == '1') {
                        if ($versioncomponents[2]{0} == '0') {
                            if ($versioncomponents[2] == '0') {
                                // version 1.*.0000
                                $this->_addWarning('version',
                                    'version 1.' . $versioncomponents[1] .
                                        '.0 probably should not be alpha or beta');
                                return true;
                            } elseif (strlen($versioncomponents[2]) > 1) {
                                // version 1.*.0RC1 or 1.*.0beta24 etc.
                                return true;
                            } else {
                                // version 1.*.0
                                $this->_addWarning('version',
                                    'version 1.' . $versioncomponents[1] .
                                        '.0 probably should not be alpha or beta');
                                return true;
                            }
                        } else {
                            $this->_addWarning('version',
                                'bugfix versions (1.3.x where x > 0) probably should ' .
                                'not be alpha or beta');
                            return true;
                        }
                    } elseif ($versioncomponents[0] != '0') {
                        $this->_addWarning('version',
                            'major versions greater than 1 are not allowed for packages ' .
                            'without an <extends> tag or an identical postfix (foo2 v2.0.0)');
                        return true;
                    }
                    if ($versioncomponents[0] . 'a' == '0a') {
                        return true;
                    }
                    if ($versioncomponents[0] == 0) {
                        $versioncomponents[0] = '0';
                        $this->_addWarning('version',
                            'version "' . $version . '" should be "' .
                            implode('.' ,$versioncomponents) . '"');
                    }
                } else {
                    $vlen = strlen($versioncomponents[0] . '');
                    $majver = substr($name, strlen($name) - $vlen);
                    while ($majver && !is_numeric($majver{0})) {
                        $majver = substr($majver, 1);
                    }
                    if (($versioncomponents[0] != 0) && $majver != $versioncomponents[0]) {
                        $this->_addWarning('version', 'first version number "' .
                            $versioncomponents[0] . '" must match the postfix of ' .
                            'package name "' . $name . '" (' .
                            $majver . ')');
                        return true;
                    }
                    if ($versioncomponents[0] == $majver) {
                        if ($versioncomponents[2]{0} == '0') {
                            if ($versioncomponents[2] == '0') {
                                // version 2.*.0000
                                $this->_addWarning('version',
                                    "version $majver." . $versioncomponents[1] .
                                        '.0 probably should not be alpha or beta');
                                return false;
                            } elseif (strlen($versioncomponents[2]) > 1) {
                                // version 2.*.0RC1 or 2.*.0beta24 etc.
                                return true;
                            } else {
                                // version 2.*.0
                                $this->_addWarning('version',
                                    "version $majver." . $versioncomponents[1] .
                                        '.0 cannot be alpha or beta');
                                return true;
                            }
                        } else {
                            $this->_addWarning('version',
                                "bugfix versions ($majver.x.y where y > 0) should " .
                                'not be alpha or beta');
                            return true;
                        }
                    } elseif ($versioncomponents[0] != '0') {
                        $this->_addWarning('version',
                            "only versions 0.x.y and $majver.x.y are allowed for alpha/beta releases");
                        return true;
                    }
                    if ($versioncomponents[0] . 'a' == '0a') {
                        return true;
                    }
                    if ($versioncomponents[0] == 0) {
                        $versioncomponents[0] = '0';
                        $this->_addWarning('version',
                            'version "' . $version . '" should be "' .
                            implode('.' ,$versioncomponents) . '"');
                    }
                }
                return true;
            break;
            case 'stable' :
                if ($versioncomponents[0] == '0') {
                    $this->_addWarning('version', 'versions less than 1.0.0 cannot ' .
                    'be stable');
                    return true;
                }
                if (!is_numeric($versioncomponents[2])) {
                    if (preg_match('/\d+(rc|a|alpha|b|beta)\d*/i',
                          $versioncomponents[2])) {
                        $this->_addWarning('version', 'version "' . $version . '" or any ' .
                            'RC/beta/alpha version cannot be stable');
                        return true;
                    }
                }
                // check for a package that extends a package,
                // like Foo and Foo2
                if ($this->_packagexml->getExtends()) {
                    $vlen = strlen($versioncomponents[0] . '');
                    $majver = substr($name, strlen($name) - $vlen);
                    while ($majver && !is_numeric($majver{0})) {
                        $majver = substr($majver, 1);
                    }
                    if (($versioncomponents[0] != 0) && $majver != $versioncomponents[0]) {
                        $this->_addWarning('version', 'first version number "' .
                            $versioncomponents[0] . '" must match the postfix of ' .
                            'package name "' . $name . '" (' .
                            $majver . ')');
                        return true;
                    }
                } elseif ($versioncomponents[0] > 1) {
                    $this->_addWarning('version', 'major version x in x.y.z may not be greater than ' .
                        '1 for any package that does not have an <extends> tag');
                }
                return true;
            break;
            default :
                return false;
            break;
        }
    }

    /**
     * @access protected
     */
    function validateMaintainers()
    {
        // maintainers can only be truly validated server-side for most channels
        // but allow this customization for those who wish it
        return true;
    }

    /**
     * @access protected
     */
    function validateDate()
    {
        if ($this->_state == PEAR_VALIDATE_NORMAL ||
              $this->_state == PEAR_VALIDATE_PACKAGING) {

            if (!preg_match('/(\d\d\d\d)\-(\d\d)\-(\d\d)/',
                  $this->_packagexml->getDate(), $res) ||
                  count($res) < 4
                  || !checkdate($res[2], $res[3], $res[1])
                ) {
                $this->_addFailure('date', 'invalid release date "' .
                    $this->_packagexml->getDate() . '"');
                return false;
            }


            if ($this->_state == PEAR_VALIDATE_PACKAGING &&
                  $this->_packagexml->getDate() != date('Y-m-d')) {
                $this->_addWarning('date', 'Release Date "' .
                    $this->_packagexml->getDate() . '" is not today');
            }
        }
        return true;
    }

    /**
     * @access protected
     */
    function validateTime()
    {
        if (!$this->_packagexml->getTime()) {
            // default of no time value set
            return true;
        }
        // packager automatically sets time, so only validate if
        // pear validate is called
        if ($this->_state = PEAR_VALIDATE_NORMAL) {
            if (!preg_match('/\d\d:\d\d:\d\d/',
                  $this->_packagexml->getTime())) {
                $this->_addFailure('time', 'invalid release time "' .
                    $this->_packagexml->getTime() . '"');
                return false;
            }
            if (strtotime($this->_packagexml->getTime()) == -1) {
                $this->_addFailure('time', 'invalid release time "' .
                    $this->_packagexml->getTime() . '"');
                return false;
            }
        }
        return true;
    }

    /**
     * @access protected
     */
    function validateState()
    {
        // this is the closest to "final" php4 can get
        if (!PEAR_Validate::validState($this->_packagexml->getState())) {
            if (strtolower($this->_packagexml->getState() == 'rc')) {
                $this->_addFailure('state', 'RC is not a state, it is a version ' .
                    'postfix, use ' . $this->_packagexml->getVersion() . 'RC1, state beta');
            }
            $this->_addFailure('state', 'invalid release state "' .
                $this->_packagexml->getState() . '", must be one of: ' .
                implode(', ', PEAR_Validate::getValidStates()));
            return false;
        }
        return true;
    }

    /**
     * @access protected
     */
    function validateStability()
    {
        $ret = true;
        $packagestability = $this->_packagexml->getState();
        $apistability = $this->_packagexml->getState('api');
        if (!PEAR_Validate::validState($packagestability)) {
            $this->_addFailure('state', 'invalid release stability "' .
                $this->_packagexml->getState() . '", must be one of: ' .
                implode(', ', PEAR_Validate::getValidStates()));
            $ret = false;
        }
        $apistates = PEAR_Validate::getValidStates();
        array_shift($apistates); // snapshot is not allowed
        if (!in_array($apistability, $apistates)) {
            $this->_addFailure('state', 'invalid API stability "' .
                $this->_packagexml->getState('api') . '", must be one of: ' .
                implode(', ', $apistates));
            $ret = false;
        }
        return $ret;
    }

    /**
     * @access protected
     */
    function validateSummary()
    {
        return true;
    }

    /**
     * @access protected
     */
    function validateDescription()
    {
        return true;
    }

    /**
     * @access protected
     */
    function validateLicense()
    {
        return true;
    }

    /**
     * @access protected
     */
    function validateNotes()
    {
        return true;
    }

    /**
     * for package.xml 2.0 only - channels can't use package.xml 1.0
     * @access protected
     */
    function validateDependencies()
    {
        return true;
    }

    /**
     * for package.xml 1.0 only
     * @access private
     */
    function _validateFilelist()
    {
        return true; // placeholder for now
    }

    /**
     * for package.xml 2.0 only
     * @access protected
     */
    function validateMainFilelist()
    {
        return true; // placeholder for now
    }

    /**
     * for package.xml 2.0 only
     * @access protected
     */
    function validateReleaseFilelist()
    {
        return true; // placeholder for now
    }

    /**
     * @access protected
     */
    function validateChangelog()
    {
        return true;
    }

    /**
     * @access protected
     */
    function validateFilelist()
    {
        return true;
    }

    /**
     * @access protected
     */
    function validateDeps()
    {
        return true;
    }
}
?>
<?php
/**
 * Channel Validator for the pecl.php.net channel
 *
 * PHP 4 and PHP 5
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2006 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: PECL.php,v 1.9 2008/01/03 20:26:37 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a5
 */
/**
 * This is the parent class for all validators
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Validate.php';
/**
 * Channel Validator for the pecl.php.net channel
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a5
 */
class PEAR_Validator_PECL extends PEAR_Validate
{
    function validateVersion()
    {
        if ($this->_state == PEAR_VALIDATE_PACKAGING) {
            $version = $this->_packagexml->getVersion();
            $versioncomponents = explode('.', $version);
            $last = array_pop($versioncomponents);
            if (substr($last, 1, 2) == 'rc') {
                $this->_addFailure('version', 'Release Candidate versions must have ' .
                'upper-case RC, not lower-case rc');
                return false;
            }
        }
        return true;
    }

    function validatePackageName()
    {
        $ret = parent::validatePackageName();
        if ($this->_packagexml->getPackageType() == 'extsrc' ||
              $this->_packagexml->getPackageType() == 'zendextsrc') {
            if (strtolower($this->_packagexml->getPackage()) !=
                  strtolower($this->_packagexml->getProvidesExtension())) {
                $this->_addWarning('providesextension', 'package name "' .
                    $this->_packagexml->getPackage() . '" is different from extension name "' .
                    $this->_packagexml->getProvidesExtension() . '"');
            }
        }
        return $ret;
    }
}
?><?php
/**
 * PEAR_XMLParser
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @author     Stephan Schmidt (original XML_Unserializer code)
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: XMLParser.php,v 1.13 2008/01/03 20:26:36 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a1
 */

/**
 * Parser for any xml file
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @author     Stephan Schmidt (original XML_Unserializer code)
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: 1.7.2
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 */
class PEAR_XMLParser
{
    /**
     * unserilialized data
     * @var string $_serializedData
     */
    var $_unserializedData = null;

    /**
     * name of the root tag
     * @var string $_root
     */
    var $_root = null;

    /**
     * stack for all data that is found
     * @var array    $_dataStack
     */
    var $_dataStack  =   array();

    /**
     * stack for all values that are generated
     * @var array    $_valStack
     */
    var $_valStack  =   array();

    /**
     * current tag depth
     * @var int    $_depth
     */
    var $_depth = 0;

    /**
     * @return array
     */
    function getData()
    {
        return $this->_unserializedData;
    }

    /**
     * @param string xml content
     * @return true|PEAR_Error
     */
    function parse($data)
    {
        if (!extension_loaded('xml')) {
            include_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
            return PEAR::raiseError("XML Extension not found", 1);
        }
        $this->_valStack = array();
        $this->_dataStack = array();
        $this->_depth = 0;

        if (version_compare(phpversion(), '5.0.0', 'lt')) {
            if (strpos($data, 'encoding="UTF-8"')) {
                $data = utf8_decode($data);
            }
            $xp = xml_parser_create('ISO-8859-1');
        } else {
            if (strpos($data, 'encoding="UTF-8"')) {
                $xp = xml_parser_create('UTF-8');
            } else {
                $xp = xml_parser_create('ISO-8859-1');
            }
        }
        xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, 0);
        xml_set_object($xp, $this);
        xml_set_element_handler($xp, 'startHandler', 'endHandler');
        xml_set_character_data_handler($xp, 'cdataHandler');
        if (!xml_parse($xp, $data)) {
            $msg = xml_error_string(xml_get_error_code($xp));
            $line = xml_get_current_line_number($xp);
            xml_parser_free($xp);
            include_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
            return PEAR::raiseError("XML Error: '$msg' on line '$line'", 2);
        }
        xml_parser_free($xp);
        return true;
    }

    /**
     * Start element handler for XML parser
     *
     * @access private
     * @param  object $parser  XML parser object
     * @param  string $element XML element
     * @param  array  $attribs attributes of XML tag
     * @return void
     */
    function startHandler($parser, $element, $attribs)
    {
        $type = 'string';

        $this->_depth++;
        $this->_dataStack[$this->_depth] = null;

        $val = array(
                     'name'         => $element,
                     'value'        => null,
                     'type'         => $type,
                     'childrenKeys' => array(),
                     'aggregKeys'   => array()
                    );

        if (count($attribs) > 0) {
            $val['children'] = array();
            $val['type'] = 'array';

            $val['children']['attribs'] = $attribs;

        }

        array_push($this->_valStack, $val);
    }

    /**
     * post-process data
     *
     * @param string $data
     * @param string $element element name
     */
    function postProcess($data, $element)
    {
        return trim($data);
    }

    /**
     * End element handler for XML parser
     *
     * @access private
     * @param  object XML parser object
     * @param  string
     * @return void
     */
    function endHandler($parser, $element)
    {
        $value = array_pop($this->_valStack);
        $data  = $this->postProcess($this->_dataStack[$this->_depth], $element);

        // adjust type of the value
        switch(strtolower($value['type'])) {

            /*
             * unserialize an array
             */
            case 'array':
                if ($data !== '') {
                    $value['children']['_content'] = $data;
                }
                if (isset($value['children'])) {
                    $value['value'] = $value['children'];
                } else {
                    $value['value'] = array();
                }
                break;

            /*
             * unserialize a null value
             */
            case 'null':
                $data = null;
                break;

            /*
             * unserialize any scalar value
             */
            default:
                settype($data, $value['type']);
                $value['value'] = $data;
                break;
        }
        $parent = array_pop($this->_valStack);
        if ($parent === null) {
            $this->_unserializedData = &$value['value'];
            $this->_root = &$value['name'];
            return true;
        } else {
            // parent has to be an array
            if (!isset($parent['children']) || !is_array($parent['children'])) {
                $parent['children'] = array();
                if ($parent['type'] != 'array') {
                    $parent['type'] = 'array';
                }
            }

            if (!empty($value['name'])) {
                // there already has been a tag with this name
                if (in_array($value['name'], $parent['childrenKeys'])) {
                    // no aggregate has been created for this tag
                    if (!in_array($value['name'], $parent['aggregKeys'])) {
                        if (isset($parent['children'][$value['name']])) {
                            $parent['children'][$value['name']] = array($parent['children'][$value['name']]);
                        } else {
                            $parent['children'][$value['name']] = array();
                        }
                        array_push($parent['aggregKeys'], $value['name']);
                    }
                    array_push($parent['children'][$value['name']], $value['value']);
                } else {
                    $parent['children'][$value['name']] = &$value['value'];
                    array_push($parent['childrenKeys'], $value['name']);
                }
            } else {
                array_push($parent['children'],$value['value']);
            }
            array_push($this->_valStack, $parent);
        }

        $this->_depth--;
    }

    /**
     * Handler for character data
     *
     * @access private
     * @param  object XML parser object
     * @param  string CDATA
     * @return void
     */
    function cdataHandler($parser, $cdata)
    {
        $this->_dataStack[$this->_depth] .= $cdata;
    }
}
?><?php
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
// +-----------------------------------------------------------------------------+
// | Copyright (c) 2003 Srgio Gonalves Carvalho                                |
// +-----------------------------------------------------------------------------+
// | This file is part of Structures_Graph.                                      |
// |                                                                             |
// | Structures_Graph is free software; you can redistribute it and/or modify    |
// | it under the terms of the GNU Lesser General Public License as published by |
// | the Free Software Foundation; either version 2.1 of the License, or         |
// | (at your option) any later version.                                         |
// |                                                                             |
// | Structures_Graph is distributed in the hope that it will be useful,         |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
// | GNU Lesser General Public License for more details.                         |
// |                                                                             |
// | You should have received a copy of the GNU Lesser General Public License    |
// | along with Structures_Graph; if not, write to the Free Software             |
// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA                    |
// | 02111-1307 USA                                                              |
// +-----------------------------------------------------------------------------+
// | Author: Srgio Carvalho <sergio.carvalho@portugalmail.com>                  |
// +-----------------------------------------------------------------------------+
//
/**
 * The Graph.php file contains the definition of the Structures_Graph class 
 *
 * @see Structures_Graph
 * @package Structures_Graph
 */

/* dependencies {{{ */
/** PEAR base classes */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
/** Graph Node */
require_once 'phar://install-pear-nozlib.phar/' . 'Structures/Graph/Node.php';
/* }}} */

define('STRUCTURES_GRAPH_ERROR_GENERIC', 100);

/* class Structures_Graph {{{ */
/**
 * The Structures_Graph class represents a graph data structure. 
 *
 * A Graph is a data structure composed by a set of nodes, connected by arcs.
 * Graphs may either be directed or undirected. In a directed graph, arcs are 
 * directional, and can be traveled only one way. In an undirected graph, arcs
 * are bidirectional, and can be traveled both ways.
 *
 * @author		Srgio Carvalho <sergio.carvalho@portugalmail.com> 
 * @copyright	(c) 2004 by Srgio Carvalho
 * @package Structures_Graph
 */
/* }}} */
class Structures_Graph {
    /* fields {{{ */
    /**
     * @access private
     */
    var $_nodes = array();
    /**
     * @access private
     */
    var $_directed = false;
    /* }}} */

    /* Constructor {{{ */
    /**
    *
    * Constructor
    *
    * @param    boolean    Set to true if the graph is directed. Set to false if it is not directed. (Optional, defaults to true)
    * @access	public
    */
    function Structures_Graph($directed = true) {
        $this->_directed = $directed;
    }
    /* }}} */

    /* isDirected {{{ */
    /**
    *
    * Return true if a graph is directed
    *
    * @return	boolean	 true if the graph is directed
    * @access	public
    */
    function isDirected() {
        return (boolean) $this->_directed;
    }
    /* }}} */

    /* addNode {{{ */
    /**
    *
    * Add a Node to the Graph
    *
    * @param    Structures_Graph_Node   The node to be added.
    * @access	public
    */
    function addNode(&$newNode) {
        // We only add nodes
        if (!is_a($newNode, 'Structures_Graph_Node')) return Pear::raiseError('Structures_Graph::addNode received an object that is not a Structures_Graph_Node', STRUCTURES_GRAPH_ERROR_GENERIC);
        // Graphs are node *sets*, so duplicates are forbidden. We allow nodes that are exactly equal, but disallow equal references.
        foreach($this->_nodes as $key => $node) {
            /*
             ZE1 equality operators choke on the recursive cycle introduced by the _graph field in the Node object.
             So, we'll check references the hard way (change $this->_nodes[$key] and check if the change reflects in 
             $node)
            */
            $savedData = $this->_nodes[$key];
            $referenceIsEqualFlag = false;
            $this->_nodes[$key] = true;
            if ($node === true) {
                $this->_nodes[$key] = false;
                if ($node === false) $referenceIsEqualFlag = true;
            }
            $this->_nodes[$key] = $savedData;
            if ($referenceIsEqualFlag) return Pear::raiseError('Structures_Graph::addNode received an object that is a duplicate for this dataset', STRUCTURES_GRAPH_ERROR_GENERIC);
        }
        $this->_nodes[] =& $newNode;
        $newNode->setGraph($this);
    }
    /* }}} */

    /* removeNode (unimplemented) {{{ */
    /**
    *
    * Remove a Node from the Graph
    *
    * @todo     This is unimplemented
    * @param    Structures_Graph_Node   The node to be removed from the graph
    * @access	public
    */
    function removeNode(&$node) {
    }
    /* }}} */

    /* getNodes {{{ */
    /**
    *
    * Return the node set, in no particular order. For ordered node sets, use a Graph Manipulator insted.
    *
    * @access   public
    * @see      Structures_Graph_Manipulator_TopologicalSorter
    * @return   array The set of nodes in this graph
    */
    function &getNodes() {
        return $this->_nodes;
    }
    /* }}} */
}
?>
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
// +-----------------------------------------------------------------------------+
// | Copyright (c) 2003 Srgio Gonalves Carvalho                                |
// +-----------------------------------------------------------------------------+
// | This file is part of Structures_Graph.                                      |
// |                                                                             |
// | Structures_Graph is free software; you can redistribute it and/or modify    |
// | it under the terms of the GNU Lesser General Public License as published by |
// | the Free Software Foundation; either version 2.1 of the License, or         |
// | (at your option) any later version.                                         |
// |                                                                             |
// | Structures_Graph is distributed in the hope that it will be useful,         |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
// | GNU Lesser General Public License for more details.                         |
// |                                                                             |
// | You should have received a copy of the GNU Lesser General Public License    |
// | along with Structures_Graph; if not, write to the Free Software             |
// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA                    |
// | 02111-1307 USA                                                              |
// +-----------------------------------------------------------------------------+
// | Author: Srgio Carvalho <sergio.carvalho@portugalmail.com>                  |
// +-----------------------------------------------------------------------------+
//
/**
 * This file contains the definition of the Structures_Graph_Manipulator_AcyclicTest graph manipulator.
 * 
 * @see Structures_Graph_Manipulator_AcyclicTest
 * @package Structures_Graph
 */

/* dependencies {{{ */
/** */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
/** */
require_once 'phar://install-pear-nozlib.phar/' . 'Structures/Graph.php';
/** */
require_once 'phar://install-pear-nozlib.phar/' . 'Structures/Graph/Node.php';
/* }}} */

/* class Structures_Graph_Manipulator_AcyclicTest {{{ */
/**
 * The Structures_Graph_Manipulator_AcyclicTest is a graph manipulator
 * which tests whether a graph contains a cycle. 
 * 
 * The definition of an acyclic graph used in this manipulator is that of a 
 * DAG. The graph must be directed, or else it is considered cyclic, even when 
 * there are no arcs.
 *
 * @author		Srgio Carvalho <sergio.carvalho@portugalmail.com> 
 * @copyright	(c) 2004 by Srgio Carvalho
 * @package Structures_Graph
 */
class Structures_Graph_Manipulator_AcyclicTest {
    /* _nonVisitedInDegree {{{ */
    /**
    *
    * This is a variant of Structures_Graph::inDegree which does 
    * not count nodes marked as visited.
    *
    * @access   private
    * @return	integer	 Number of non-visited nodes that link to this one
    */
    function _nonVisitedInDegree(&$node) {
        $result = 0;
        $graphNodes =& $node->_graph->getNodes();
        foreach (array_keys($graphNodes) as $key) {
            if ((!$graphNodes[$key]->getMetadata('acyclic-test-visited')) && $graphNodes[$key]->connectsTo($node)) $result++;
        }
        return $result;
        
    }
    /* }}} */

    /* _isAcyclic {{{ */
    /**
    * @access   private
    */
    function _isAcyclic(&$graph) {
        // Mark every node as not visited
        $nodes =& $graph->getNodes();
        $nodeKeys = array_keys($nodes);
        $refGenerator = array();
        foreach($nodeKeys as $key) {
            $refGenerator[] = false;
            $nodes[$key]->setMetadata('acyclic-test-visited', $refGenerator[sizeof($refGenerator) - 1]);
        }

        // Iteratively peel off leaf nodes
        do {
            // Find out which nodes are leafs (excluding visited nodes)
            $leafNodes = array();
            foreach($nodeKeys as $key) {
                if ((!$nodes[$key]->getMetadata('acyclic-test-visited')) && Structures_Graph_Manipulator_AcyclicTest::_nonVisitedInDegree($nodes[$key]) == 0) {
                    $leafNodes[] =& $nodes[$key];
                }
            }
            // Mark leafs as visited
            for ($i=sizeof($leafNodes) - 1; $i>=0; $i--) {
                $visited =& $leafNodes[$i]->getMetadata('acyclic-test-visited');
                $visited = true;
                $leafNodes[$i]->setMetadata('acyclic-test-visited', $visited);
            }
        } while (sizeof($leafNodes) > 0);

        // If graph is a DAG, there should be no non-visited nodes. Let's try to prove otherwise
        $result = true;
        foreach($nodeKeys as $key) if (!$nodes[$key]->getMetadata('acyclic-test-visited')) $result = false;
        
        // Cleanup visited marks
        foreach($nodeKeys as $key) $nodes[$key]->unsetMetadata('acyclic-test-visited');

        return $result;
    }
    /* }}} */

    /* isAcyclic {{{ */
    /**
    *
    * isAcyclic returns true if a graph contains no cycles, false otherwise.
    *
    * @return	boolean	 true iff graph is acyclic
    * @access	public
    */
    function isAcyclic(&$graph) {
        // We only test graphs
        if (!is_a($graph, 'Structures_Graph')) return Pear::raiseError('Structures_Graph_Manipulator_AcyclicTest::isAcyclic received an object that is not a Structures_Graph', STRUCTURES_GRAPH_ERROR_GENERIC);
        if (!$graph->isDirected()) return false; // Only directed graphs may be acyclic

        return Structures_Graph_Manipulator_AcyclicTest::_isAcyclic($graph);
    }
    /* }}} */
}
/* }}} */
?>
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
// +-----------------------------------------------------------------------------+
// | Copyright (c) 2003 Srgio Gonalves Carvalho                                |
// +-----------------------------------------------------------------------------+
// | This file is part of Structures_Graph.                                      |
// |                                                                             |
// | Structures_Graph is free software; you can redistribute it and/or modify    |
// | it under the terms of the GNU Lesser General Public License as published by |
// | the Free Software Foundation; either version 2.1 of the License, or         |
// | (at your option) any later version.                                         |
// |                                                                             |
// | Structures_Graph is distributed in the hope that it will be useful,         |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
// | GNU Lesser General Public License for more details.                         |
// |                                                                             |
// | You should have received a copy of the GNU Lesser General Public License    |
// | along with Structures_Graph; if not, write to the Free Software             |
// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA                    |
// | 02111-1307 USA                                                              |
// +-----------------------------------------------------------------------------+
// | Author: Srgio Carvalho <sergio.carvalho@portugalmail.com>                  |
// +-----------------------------------------------------------------------------+
//
/**
 * This file contains the definition of the Structures_Graph_Manipulator_TopologicalSorter class.
 * 
 * @see Structures_Graph_Manipulator_TopologicalSorter
 * @package Structures_Graph
 */

/* dependencies {{{ */
/** */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
/** */
require_once 'phar://install-pear-nozlib.phar/' . 'Structures/Graph.php';
/** */
require_once 'phar://install-pear-nozlib.phar/' . 'Structures/Graph/Node.php';
/** */
require_once 'phar://install-pear-nozlib.phar/' . 'Structures/Graph/Manipulator/AcyclicTest.php';
/* }}} */

/* class Structures_Graph_Manipulator_TopologicalSorter {{{ */
/**
 * The Structures_Graph_Manipulator_TopologicalSorter is a manipulator 
 * which is able to return the set of nodes in a graph, sorted by topological 
 * order.
 *
 * A graph may only be sorted topologically iff it's a DAG. You can test it
 * with the Structures_Graph_Manipulator_AcyclicTest.
 * 
 * @author		Srgio Carvalho <sergio.carvalho@portugalmail.com> 
 * @copyright	(c) 2004 by Srgio Carvalho
 * @see     Structures_Graph_Manipulator_AcyclicTest
 * @package Structures_Graph
 */
class Structures_Graph_Manipulator_TopologicalSorter {
    /* _nonVisitedInDegree {{{ */
    /**
    *
    * This is a variant of Structures_Graph::inDegree which does 
    * not count nodes marked as visited.
    *
    * @access   private
    * @return	integer	 Number of non-visited nodes that link to this one
    */
    function _nonVisitedInDegree(&$node) {
        $result = 0;
        $graphNodes =& $node->_graph->getNodes();
        foreach (array_keys($graphNodes) as $key) {
            if ((!$graphNodes[$key]->getMetadata('topological-sort-visited')) && $graphNodes[$key]->connectsTo($node)) $result++;
        }
        return $result;
        
    }
    /* }}} */

    /* _sort {{{ */
    /**
    * @access   private
    */
    function _sort(&$graph) {
        // Mark every node as not visited
        $nodes =& $graph->getNodes();
        $nodeKeys = array_keys($nodes);
        $refGenerator = array();
        foreach($nodeKeys as $key) {
            $refGenerator[] = false;
            $nodes[$key]->setMetadata('topological-sort-visited', $refGenerator[sizeof($refGenerator) - 1]);
        }

        // Iteratively peel off leaf nodes
        $topologicalLevel = 0;
        do {
            // Find out which nodes are leafs (excluding visited nodes)
            $leafNodes = array();
            foreach($nodeKeys as $key) {
                if ((!$nodes[$key]->getMetadata('topological-sort-visited')) && Structures_Graph_Manipulator_TopologicalSorter::_nonVisitedInDegree($nodes[$key]) == 0) {
                    $leafNodes[] =& $nodes[$key];
                }
            }
            // Mark leafs as visited
            $refGenerator[] = $topologicalLevel;
            for ($i=sizeof($leafNodes) - 1; $i>=0; $i--) {
                $visited =& $leafNodes[$i]->getMetadata('topological-sort-visited');
                $visited = true;
                $leafNodes[$i]->setMetadata('topological-sort-visited', $visited);
                $leafNodes[$i]->setMetadata('topological-sort-level', $refGenerator[sizeof($refGenerator) - 1]);
            }
            $topologicalLevel++;
        } while (sizeof($leafNodes) > 0);

        // Cleanup visited marks
        foreach($nodeKeys as $key) $nodes[$key]->unsetMetadata('topological-sort-visited');
    }
    /* }}} */

    /* sort {{{ */
    /**
    *
    * sort returns the graph's nodes, sorted by topological order. 
    * 
    * The result is an array with 
    * as many entries as topological levels. Each entry in this array is an array of nodes within
    * the given topological level.
    *
    * @return	array	 The graph's nodes, sorted by topological order.
    * @access	public
    */
    function sort(&$graph) {
        // We only sort graphs
        if (!is_a($graph, 'Structures_Graph')) return Pear::raiseError('Structures_Graph_Manipulator_TopologicalSorter::sort received an object that is not a Structures_Graph', STRUCTURES_GRAPH_ERROR_GENERIC);
        if (!Structures_Graph_Manipulator_AcyclicTest::isAcyclic($graph)) return Pear::raiseError('Structures_Graph_Manipulator_TopologicalSorter::sort received an graph that has cycles', STRUCTURES_GRAPH_ERROR_GENERIC);

        Structures_Graph_Manipulator_TopologicalSorter::_sort($graph);
        $result = array();

        // Fill out result array
        $nodes =& $graph->getNodes();
        $nodeKeys = array_keys($nodes);
        foreach($nodeKeys as $key) {
            if (!array_key_exists($nodes[$key]->getMetadata('topological-sort-level'), $result)) $result[$nodes[$key]->getMetadata('topological-sort-level')] = array();
            $result[$nodes[$key]->getMetadata('topological-sort-level')][] =& $nodes[$key];
            $nodes[$key]->unsetMetadata('topological-sort-level');
        }

        return $result;
    }
    /* }}} */
}
/* }}} */
?>
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
// +-----------------------------------------------------------------------------+
// | Copyright (c) 2003 Srgio Gonalves Carvalho                                |
// +-----------------------------------------------------------------------------+
// | This file is part of Structures_Graph.                                      |
// |                                                                             |
// | Structures_Graph is free software; you can redistribute it and/or modify    |
// | it under the terms of the GNU Lesser General Public License as published by |
// | the Free Software Foundation; either version 2.1 of the License, or         |
// | (at your option) any later version.                                         |
// |                                                                             |
// | Structures_Graph is distributed in the hope that it will be useful,         |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
// | GNU Lesser General Public License for more details.                         |
// |                                                                             |
// | You should have received a copy of the GNU Lesser General Public License    |
// | along with Structures_Graph; if not, write to the Free Software             |
// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA                    |
// | 02111-1307 USA                                                              |
// +-----------------------------------------------------------------------------+
// | Author: Srgio Carvalho <sergio.carvalho@portugalmail.com>                  |
// +-----------------------------------------------------------------------------+
//
/**
 * This file contains the definition of the Structures_Graph_Node class
 * 
 * @see Structures_Graph_Node
 * @package Structures_Graph
 */

/* dependencies {{{ */
/** */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
/** */
require_once 'phar://install-pear-nozlib.phar/' . 'Structures/Graph.php';
/* }}} */

/* class Structures_Graph_Node {{{ */
/**
 * The Structures_Graph_Node class represents a Node that can be member of a 
 * graph node set.
 *
 * A graph node can contain data. Under this API, the node contains default data, 
 * and key index data. It behaves, thus, both as a regular data node, and as a 
 * dictionary (or associative array) node.
 * 
 * Regular data is accessed via getData and setData. Key indexed data is accessed
 * via getMetadata and setMetadata.
 *
 * @author		Srgio Carvalho <sergio.carvalho@portugalmail.com> 
 * @copyright	(c) 2004 by Srgio Carvalho
 * @package Structures_Graph
 */
/* }}} */
class Structures_Graph_Node {
    /* fields {{{ */
    /** 
     * @access private 
     */
    var $_data = null;
    /** @access private */
    var $_metadata = array();
    /** @access private */
    var $_arcs = array();
    /** @access private */
    var $_graph = null;
    /* }}} */

    /* Constructor {{{ */
    /**
    *
    * Constructor
    *
    * @access	public
    */
    function Structures_Graph_Node() {
    }
    /* }}} */

    /* getGraph {{{ */
    /**
    *
    * Node graph getter
    *
    * @return	Structures_Graph	Graph where node is stored
    * @access	public
    */
    function &getGraph() {
        return $this->_graph;
    }
    /* }}} */

    /* setGraph {{{ */
    /**
    *
    * Node graph setter. This method should not be called directly. Use Graph::addNode instead.
    *
    * @param    Structures_Graph   Set the graph for this node. 
    * @see      Structures_Graph::addNode()
    * @access	public
    */
    function setGraph(&$graph) {
        $this->_graph =& $graph;
    }
    /* }}} */

    /* getData {{{ */
    /**
    *
    * Node data getter.
    * 
    * Each graph node can contain a reference to one variable. This is the getter for that reference.
    *
    * @return	mixed	Data stored in node
    * @access	public
    */
    function &getData() {
        return $this->_data;
    }
    /* }}} */

    /* setData {{{ */
    /**
    *
    * Node data setter
    *
    * Each graph node can contain a reference to one variable. This is the setter for that reference.
    *
    * @return	mixed	Data to store in node
    * @access	public
    */
    function setData($data) {
        $this->_data =& $data;
    }
    /* }}} */

    /* metadataKeyExists {{{ */
    /**
    *
    * Test for existence of metadata under a given key.
    *
    * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an 
    * associative array or in a dictionary. This method tests whether a given metadata key exists for this node.
    *
    * @param    string    Key to test
    * @return	boolean	 
    * @access	public
    */
    function metadataKeyExists($key) {
        return array_key_exists($key, $this->_metadata);
    }
    /* }}} */

    /* getMetadata {{{ */
    /**
    *
    * Node metadata getter
    *
    * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an 
    * associative array or in a dictionary. This method gets the data under the given key. If the key does
    * not exist, an error will be thrown, so testing using metadataKeyExists might be needed.
    *
    * @param    string  Key
    * @param    boolean nullIfNonexistent (defaults to false).
    * @return	mixed	Metadata Data stored in node under given key
    * @see      metadataKeyExists
    * @access	public
    */
    function &getMetadata($key, $nullIfNonexistent = false) {
        if (array_key_exists($key, $this->_metadata)) {
            return $this->_metadata[$key];
        } else {
            if ($nullIfNonexistent) {
                $a = null;
                return $a;
            } else {
                $a = Pear::raiseError('Structures_Graph_Node::getMetadata: Requested key does not exist', STRUCTURES_GRAPH_ERROR_GENERIC);
                return $a;
            }
        }
    }
    /* }}} */

    /* unsetMetadata {{{ */
    /**
    *
    * Delete metadata by key
    *
    * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an 
    * associative array or in a dictionary. This method removes any data that might be stored under the provided key.
    * If the key does not exist, no error is thrown, so it is safe using this method without testing for key existence.
    *
    * @param    string  Key
    * @access	public
    */
    function unsetMetadata($key) {
        if (array_key_exists($key, $this->_metadata)) unset($this->_metadata[$key]);
    }
    /* }}} */

    /* setMetadata {{{ */
    /**
    *
    * Node metadata setter
    *
    * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an 
    * associative array or in a dictionary. This method stores data under the given key. If the key already exists,
    * previously stored data is discarded.
    *
    * @param    string  Key
    * @param    mixed   Data 
    * @access	public
    */
    function setMetadata($key, $data) {
        $this->_metadata[$key] =& $data;
    }
    /* }}} */

    /* _connectTo {{{ */
    /** @access private */
    function _connectTo(&$destinationNode) {
        $this->_arcs[] =& $destinationNode;
    }
    /* }}} */

    /* connectTo {{{ */
    /**
    *
    * Connect this node to another one.
    * 
    * If the graph is not directed, the reverse arc, connecting $destinationNode to $this is also created.
    *
    * @param    Structures_Graph Node to connect to
    * @access	public
    */
    function connectTo(&$destinationNode) {
        // We only connect to nodes
        if (!is_a($destinationNode, 'Structures_Graph_Node')) return Pear::raiseError('Structures_Graph_Node::connectTo received an object that is not a Structures_Graph_Node', STRUCTURES_GRAPH_ERROR_GENERIC);
        // Nodes must already be in graphs to be connected
        if ($this->_graph == null) return Pear::raiseError('Structures_Graph_Node::connectTo Tried to connect a node that is not in a graph', STRUCTURES_GRAPH_ERROR_GENERIC);
        if ($destinationNode->getGraph() == null) return Pear::raiseError('Structures_Graph_Node::connectTo Tried to connect to a node that is not in a graph', STRUCTURES_GRAPH_ERROR_GENERIC);
        // Connect here
        $this->_connectTo($destinationNode);
        // If graph is undirected, connect back
        if (!$this->_graph->isDirected()) {
            $destinationNode->_connectTo($this);
        }
    }
    /* }}} */

    /* getNeighbours {{{ */
    /**
    *
    * Return nodes connected to this one.
    * 
    * @return   array   Array of nodes
    * @access	public
    */
    function getNeighbours() {
        return $this->_arcs;
    }
    /* }}} */

    /* connectsTo {{{ */
    /**
    *
    * Test wether this node has an arc to the target node
    *
    * @return	boolean   True if the two nodes are connected
    * @access	public
    */
    function connectsTo(&$target) {
        $copy = $target;
        $arcKeys = array_keys($this->_arcs);
        foreach($arcKeys as $key) {
            /* ZE1 chokes on this expression:
                if ($target === $arc) return true;
              so, we'll use more convoluted stuff
            */
            $arc =& $this->_arcs[$key];
            $target = true;
            if ($arc === true) {
                $target = false;
                if ($arc === false) {
                    $target = $copy;
                    return true;
                }
            }
        }
        $target = $copy;
        return false;
    }
    /* }}} */

    /* inDegree {{{ */
    /**
    *
    * Calculate the in degree of the node.
    * 
    * The indegree for a node is the number of arcs entering the node. For non directed graphs, 
    * the indegree is equal to the outdegree.
    *
    * @return	integer	 In degree of the node
    * @access	public
    */
    function inDegree() {
        if ($this->_graph == null) return 0;
        if (!$this->_graph->isDirected()) return $this->outDegree();
        $result = 0;
        $graphNodes =& $this->_graph->getNodes();
        foreach (array_keys($graphNodes) as $key) {
            if ($graphNodes[$key]->connectsTo($this)) $result++;
        }
        return $result;
        
    }
    /* }}} */

    /* outDegree {{{ */
    /**
    *
    * Calculate the out degree of the node.
    *
    * The outdegree for a node is the number of arcs exiting the node. For non directed graphs,
    * the outdegree is always equal to the indegree.
    * 
    * @return	integer	 Out degree of the node
    * @access	public
    */
    function outDegree() {
        if ($this->_graph == null) return 0;
        return sizeof($this->_arcs);
    }
    /* }}} */
}
?>
package2.xml100644   1750   1750       14156 10560475634   6540 <?xml version="1.0" encoding="UTF-8"?>
<package packagerversion="1.5.0" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0     http://pear.php.net/dtd/tasks-1.0.xsd     http://pear.php.net/dtd/package-2.0     http://pear.php.net/dtd/package-2.0.xsd">
 <name>Structures_Graph</name>
 <channel>pear.php.net</channel>
 <summary>Graph datastructure manipulation library</summary>
 <description>Structures_Graph is a package for creating and manipulating graph datastructures. It allows building of directed
and undirected graphs, with data and metadata stored in nodes. The library provides functions for graph traversing
as well as for characteristic extraction from the graph topology.</description>
 <lead>
  <name>Sérgio Carvalho</name>
  <user>sergiosgc</user>
  <email>sergio.carvalho@portugalmail.com</email>
  <active>yes</active>
 </lead>
 <date>2007-02-01</date>
 <time>17:45:32</time>
 <version>
  <release>1.0.2</release>
  <api>1.0.0</api>
 </version>
 <stability>
  <release>stable</release>
  <api>stable</api>
 </stability>
 <license uri="http://opensource.org/licenses/lgpl-license.php">LGPL</license>
 <notes>- Bug #9682 only variables can be returned by reference
- fix Bug #9661 notice in Structures_Graph_Manipulator_Topological::sort()</notes>
 <contents>
  <dir baseinstalldir="" name="/">
   <file baseinstalldir="" md5sum="039cb8781dae7b5f5661dc2892fb53b5" name="docs/html/media/banner.css" role="doc" />
   <file baseinstalldir="" md5sum="8ff124484e8a009f94a8d0b4d9b8b1cd" name="docs/html/media/stylesheet.css" role="doc" />
   <file baseinstalldir="" md5sum="a17210dd27171316f5cb446a5bef59ab" name="docs/html/Structures_Graph/Structures_Graph.html" role="doc" />
   <file baseinstalldir="" md5sum="1ed0ac55530b7504c00df35f335e7ef3" name="docs/html/Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html" role="doc" />
   <file baseinstalldir="" md5sum="fefb65755d57a87d2736e058a1095b03" name="docs/html/Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.html" role="doc" />
   <file baseinstalldir="" md5sum="afb0e3afb91beafd1bc16a07ac31c826" name="docs/html/Structures_Graph/Structures_Graph_Node.html" role="doc" />
   <file baseinstalldir="" md5sum="3fa477998b281061361c4d98fb78c3a8" name="docs/html/Structures_Graph/tutorial_Structures_Graph.pkg.html" role="doc" />
   <file baseinstalldir="" md5sum="c46e6566be5249811667ddc7decf405f" name="docs/html/Structures_Graph/_Structures_Graph_Manipulator_AcyclicTest_php.html" role="doc" />
   <file baseinstalldir="" md5sum="c45890d45dd42dc56adb71988c3a45c1" name="docs/html/Structures_Graph/_Structures_Graph_Manipulator_TopologicalSorter_php.html" role="doc" />
   <file baseinstalldir="" md5sum="09cb6d17f96553bfb66b99c502676844" name="docs/html/Structures_Graph/_Structures_Graph_Node_php.html" role="doc" />
   <file baseinstalldir="" md5sum="78e1e7ec43a6e754cf4d5dace48fe18c" name="docs/html/Structures_Graph/_Structures_Graph_php.html" role="doc" />
   <file baseinstalldir="" md5sum="d0ada99c3b7e95b23461cc3e713f0c3d" name="docs/html/classtrees_Structures_Graph.html" role="doc" />
   <file baseinstalldir="" md5sum="898dd26ccc1c1572d36e9879afcb5d39" name="docs/html/elementindex.html" role="doc" />
   <file baseinstalldir="" md5sum="19dd287d44163d2abde1a1d8d52521f8" name="docs/html/elementindex_Structures_Graph.html" role="doc" />
   <file baseinstalldir="" md5sum="ae809f6ebbf632351648c340d2d6acd4" name="docs/html/errors.html" role="doc" />
   <file baseinstalldir="" md5sum="db3206e7d176ae78fd04f22acead5ffa" name="docs/html/index.html" role="doc" />
   <file baseinstalldir="" md5sum="4ec087627323546610b64d69a6aafca6" name="docs/html/li_Structures_Graph.html" role="doc" />
   <file baseinstalldir="" md5sum="005081a4c5d60afcbabc1f4909ebfcad" name="docs/html/packages.html" role="doc" />
   <file baseinstalldir="" md5sum="a7a1b748bbda6dfa1632bf6078fc8bb6" name="docs/html/todolist.html" role="doc" />
   <file baseinstalldir="" md5sum="628eb6532a8047bf5962fe24c1c245df" name="docs/tutorials/Structures_Graph/Structures_Graph.pkg" role="doc" />
   <file baseinstalldir="" md5sum="ce2da39dbb75e21074eb5e96231a3379" name="docs/generate.sh" role="doc" />
   <file baseinstalldir="" md5sum="f0aff5a1efd188d63b4b8b9e9e840b97" name="Structures/Graph/Manipulator/AcyclicTest.php" role="php" />
   <file baseinstalldir="" md5sum="0492e677436d29228df93dca23629e06" name="Structures/Graph/Manipulator/TopologicalSorter.php" role="php" />
   <file baseinstalldir="" md5sum="7e28e01ebfa273e5df2eb1ef7a356635" name="Structures/Graph/Node.php" role="php" />
   <file baseinstalldir="" md5sum="4f25a6275af156f6f8e7b4309cb9f40d" name="Structures/Graph.php" role="php" />
   <file baseinstalldir="" md5sum="63dfc6f0f14e18c13d8a8415c3fe049d" name="tests/testCase/BasicGraph.php" role="test" />
   <file baseinstalldir="" md5sum="1b9e35896d229b84601e6754cd44e339" name="tests/all-tests.php" role="test" />
   <file baseinstalldir="" md5sum="d41d8cd98f00b204e9800998ecf8427e" name="tests/README" role="test" />
   <file baseinstalldir="" md5sum="ab23406ec7bfbb2029ca536378ff57db" name="genpackage.xml.pl" role="data" />
   <file baseinstalldir="" md5sum="7fbc338309ac38fefcd64b04bb903e34" name="LICENSE" role="data" />
   <file baseinstalldir="" md5sum="6e782f281c7d0ce27e54b574247073cb" name="package.sh" role="data" />
   <file baseinstalldir="" md5sum="b81f67f7b5600231735243be73625e54" name="publish.sh" role="data" />
  </dir>
 </contents>
 <dependencies>
  <required>
   <php>
    <min>4.2.0</min>
   </php>
   <pearinstaller>
    <min>1.4.3</min>
   </pearinstaller>
  </required>
 </dependencies>
 <phprelease />
 <changelog>
  <release>
   <version>
    <release>1.0.2</release>
    <api>1.0.0</api>
   </version>
   <stability>
    <release>stable</release>
    <api>stable</api>
   </stability>
   <date>2007-01-07</date>
   <license uri="http://opensource.org/licenses/lgpl-license.php">LGPL</license>
   <notes>- Bug #9682 only variables can be returned by reference
- fix Bug #9661 notice in Structures_Graph_Manipulator_Topological::sort()</notes>
  </release>
 </changelog>
</package>
Structures_Graph-1.0.2/docs/html/media/banner.css100644   1750   1750         651 10560475634  15166 body 
{ 
	background-color: #CCCCFF; 
	margin: 0px; 
	padding: 0px;
}

/* Banner (top bar) classes */

.banner {  }

.banner-menu 
{ 
	clear: both;
	padding: .5em;
	border-top: 2px solid #6666AA;	
}

.banner-title 
{ 
	text-align: right; 
	font-size: 20pt; 
	font-weight: bold; 
	margin: .2em;
}

.package-selector 
{ 
	background-color: #AAAADD; 
	border: 1px solid black; 
	color: yellow;
}
Structures_Graph-1.0.2/docs/html/media/stylesheet.css100644   1750   1750       12012 10560475634  16144 a { color: #336699; text-decoration: none; }
a:hover { color: #6699CC; text-decoration: underline; }
a:active { color: #6699CC; text-decoration: underline; }

body { background : #FFFFFF; }
body, table { font-family: Georgia, Times New Roman, Times, serif; font-size: 10pt }
p, li { line-height: 140% }
a img { border: 0px; }
dd { margin-left: 0px; padding-left: 1em; }

/* Page layout/boxes */

.info-box {}
.info-box-title { margin: 1em 0em 0em 0em; padding: .25em; font-weight: normal; font-size: 14pt; border: 2px solid #999999; background-color: #CCCCFF }
.info-box-body { border: 1px solid #999999; padding: .5em; }
.nav-bar { font-size: 8pt; white-space: nowrap; text-align: right; padding: .2em; margin: 0em 0em 1em 0em; }

.oddrow { background-color: #F8F8F8; border: 1px solid #AAAAAA; padding: .5em; margin-bottom: 1em}
.evenrow { border: 1px solid #AAAAAA; padding: .5em; margin-bottom: 1em}

.page-body { max-width: 800px; margin: auto; }
.tree dl { margin: 0px }

/* Index formatting classes */

.index-item-body { margin-top: .5em; margin-bottom: .5em}
.index-item-description { margin-top: .25em }
.index-item-details { font-weight: normal; font-style: italic; font-size: 8pt }
.index-letter-section { background-color: #EEEEEE; border: 1px dotted #999999; padding: .5em; margin-bottom: 1em}
.index-letter-title { font-size: 12pt; font-weight: bold }
.index-letter-menu { text-align: center; margin: 1em }
.index-letter { font-size: 12pt }

/* Docbook classes */

.description {}
.short-description { font-weight: bold; color: #666666; }
.tags {	padding-left: 0em; margin-left: 3em; color: #666666; list-style-type: square; }
.parameters {	padding-left: 0em; margin-left: 3em; font-style: italic; list-style-type: square; }
.redefinitions { font-size: 8pt; padding-left: 0em; margin-left: 2em; }
.package {  }
.package-title { font-weight: bold; font-size: 14pt; border-bottom: 1px solid black }
.package-details { font-size: 85%; }
.sub-package { font-weight: bold; font-size: 120% }
.tutorial { border-width: thin; border-color: #0066ff }
.tutorial-nav-box { width: 100%; border: 1px solid #999999; background-color: #F8F8F8; }
.nav-button-disabled { color: #999999; }
.nav-button:active, 
.nav-button:focus, 
.nav-button:hover { background-color: #DDDDDD; outline: 1px solid #999999; text-decoration: none }
.folder-title { font-style: italic }

/* Generic formatting */

.field { font-weight: bold; }
.detail { font-size: 8pt; }
.notes { font-style: italic; font-size: 8pt; }
.separator { background-color: #999999; height: 2px; }
.warning {  color: #FF6600; }
.disabled { font-style: italic; color: #999999; }

/* Code elements */

.line-number {  }

.class-table { width: 100%; }
.class-table-header { border-bottom: 1px dotted #666666; text-align: left}
.class-name { color: #000000; font-weight: bold; }

.method-summary { padding-left: 1em; font-size: 8pt }
.method-header { }
.method-definition { margin-bottom: .3em }
.method-title { font-weight: bold; }
.method-name { font-weight: bold; }
.method-signature { font-size: 85%; color: #666666; margin: .5em 0em }
.method-result { font-style: italic; }

.var-summary { padding-left: 1em; font-size: 8pt; }
.var-header { }
.var-title { margin-bottom: .3em }
.var-type { font-style: italic; }
.var-name { font-weight: bold; }
.var-default {}
.var-description { font-weight: normal; color: #000000; }

.include-title {  }
.include-type { font-style: italic; }
.include-name { font-weight: bold; }

.const-title {  }
.const-name { font-weight: bold; }

/* Syntax highlighting */

.src-code {  border: 1px solid #336699; padding: 1em; background-color: #EEEEEE; }

.src-comm { color: green; }
.src-id {  }
.src-inc { color: #0000FF; }
.src-key { color: #0000FF; }
.src-num { color: #CC0000; }
.src-str { color: #66cccc; }
.src-sym { font-weight: bold; }
.src-var { }

.src-php { font-weight: bold; }

.src-doc { color: #009999 }
.src-doc-close-template { color: #0000FF }
.src-doc-coretag { color: #0099FF; font-weight: bold }
.src-doc-inlinetag { color: #0099FF }
.src-doc-internal { color: #6699cc }
.src-doc-tag { color: #0080CC }
.src-doc-template { color: #0000FF }
.src-doc-type { font-style: italic }
.src-doc-var { font-style: italic }

/* tutorial */

.authors {  }
.author { font-style: italic; font-weight: bold }
.author-blurb { margin: .5em 0em .5em 2em; font-size: 85%; font-weight: normal; font-style: normal }
.example { border: 1px dashed #999999; background-color: #EEEEEE; padding: .5em }
.listing { border: 1px dashed #999999; background-color: #EEEEEE; padding: .5em; white-space: nowrap }
.release-info { font-size: 85%; font-style: italic; margin: 1em 0em }
.ref-title-box {  }
.ref-title {  }
.ref-purpose { font-style: italic; color: #666666 }
.ref-synopsis {  }
.title { font-weight: bold; margin: 1em 0em 0em 0em; padding: .25em; border: 2px solid #999999; background-color: #CCCCFF  }
.cmd-synopsis { margin: 1em 0em }
.cmd-title { font-weight: bold }
.toc { margin-left: 2em; padding-left: 0em }

Structures_Graph-1.0.2/docs/html/Structures_Graph/Structures_Graph.html100644   1750   1750       21655 10560475634  21675 <?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
		<head>
			<!-- template designed by Marco Von Ballmoos -->
			<title>Docs For Class Structures_Graph</title>
			<link rel="stylesheet" href="../media/stylesheet.css" />
			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
		</head>
		<body>
			<div class="page-body">			
<h2 class="class-name">Class Structures_Graph</h2>

<a name="sec-description"></a>
<div class="info-box">
	<div class="info-box-title">Description</div>
	<div class="nav-bar">
					<span class="disabled">Description</span> |
															<a href="#sec-method-summary">Methods</a> (<a href="#sec-methods">details</a>)
						
			</div>
	<div class="info-box-body">
		<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">The Structures_Graph class represents a graph data structure.</p>
<p class="description"><p>A Graph is a data structure composed by a set of nodes, connected by arcs.  Graphs may either be directed or undirected. In a directed graph, arcs are  directional, and can be traveled only one way. In an undirected graph, arcs  are bidirectional, and can be traveled both ways.</p></p>
	<ul class="tags">
				<li><span class="field">copyright:</span> (c) 2004 by Srgio Carvalho</li>
				<li><span class="field">author:</span> Srgio Carvalho &lt;<a href="mailto:sergio.carvalho@portugalmail.com">mailto:sergio.carvalho@portugalmail.com</a>&gt;</li>
			</ul>
		<p class="notes">
			Located in <a class="field" href="_Structures_Graph_php.html">/Structures/Graph.php</a> (line <span class="field">56</span>)
		</p>
		
				
		<pre></pre>
	
			</div>
</div>



	<a name="sec-method-summary"></a>
	<div class="info-box">
		<div class="info-box-title">Method Summary</span></div>
		<div class="nav-bar">
			<a href="#sec-description">Description</a> |
									<span class="disabled">Methods</span> (<a href="#sec-methods">details</a>)
		</div>
		<div class="info-box-body">			
			<div class="method-summary">
								
				<div class="method-definition">
											<span class="method-result">Structures_Graph</span>
										<a href="#Structures_Graph" title="details" class="method-name">Structures_Graph</a>
											([<span class="var-type">boolean</span>&nbsp;<span class="var-name">$directed</span> = <span class="var-default">true</span>])
									</div>
								
				<div class="method-definition">
											<span class="method-result">void</span>
										<a href="#addNode" title="details" class="method-name">addNode</a>
											(<span class="var-type"><a href="../Structures_Graph/Structures_Graph_Node.html">Structures_Graph_Node</a></span>&nbsp;<span class="var-name">&$newNode</span>)
									</div>
								
				<div class="method-definition">
											<span class="method-result">array</span>
										<a href="#getNodes" title="details" class="method-name">&amp;getNodes</a>
										()
									</div>
								
				<div class="method-definition">
											<span class="method-result">boolean</span>
										<a href="#isDirected" title="details" class="method-name">isDirected</a>
										()
									</div>
								
				<div class="method-definition">
											<span class="method-result">void</span>
										<a href="#removeNode" title="details" class="method-name">removeNode</a>
											(<span class="var-type"><a href="../Structures_Graph/Structures_Graph_Node.html">Structures_Graph_Node</a></span>&nbsp;<span class="var-name">&$node</span>)
									</div>
							</div>
		</div>
	</div>		

	
	<a name="sec-methods"></a>
	<div class="info-box">
		<div class="info-box-title">Methods</div>
		<div class="nav-bar">
			<a href="#sec-description">Description</a> |
													<a href="#sec-method-summary">Methods</a> (<span class="disabled">details</span>)
						
		</div>
		<div class="info-box-body">
			<A NAME='method_detail'></A>
<a name="methodStructures_Graph" id="Structures_Graph"><!-- --></a>
<div class="evenrow">
	
	<div class="method-header">
		<span class="method-title">Constructor Structures_Graph</span> (line <span class="line-number">76</span>)
	</div> 
	
	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">Constructor</p>
	<ul class="tags">
				<li><span class="field">access:</span> public</li>
			</ul>
	
	<div class="method-signature">
		<span class="method-result">Structures_Graph</span>
		<span class="method-name">
			Structures_Graph
		</span>
					([<span class="var-type">boolean</span>&nbsp;<span class="var-name">$directed</span> = <span class="var-default">true</span>])
			</div>
	
			<ul class="parameters">
					<li>
				<span class="var-type">boolean</span>
				<span class="var-name">$directed</span><span class="var-description">: Set to true if the graph is directed. Set to false if it is not directed. (Optional, defaults to true)</span>			</li>
				</ul>
		
		
	</div>
<a name="methodaddNode" id="addNode"><!-- --></a>
<div class="oddrow">
	
	<div class="method-header">
		<span class="method-title">addNode</span> (line <span class="line-number">102</span>)
	</div> 
	
	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">Add a Node to the Graph</p>
	<ul class="tags">
				<li><span class="field">access:</span> public</li>
			</ul>
	
	<div class="method-signature">
		<span class="method-result">void</span>
		<span class="method-name">
			addNode
		</span>
					(<span class="var-type"><a href="../Structures_Graph/Structures_Graph_Node.html">Structures_Graph_Node</a></span>&nbsp;<span class="var-name">&$newNode</span>)
			</div>
	
			<ul class="parameters">
					<li>
				<span class="var-type"><a href="../Structures_Graph/Structures_Graph_Node.html">Structures_Graph_Node</a></span>
				<span class="var-name">&$newNode</span><span class="var-description">: The node to be added.</span>			</li>
				</ul>
		
		
	</div>
<a name="methodgetNodes" id="getNodes"><!-- --></a>
<div class="evenrow">
	
	<div class="method-header">
		<span class="method-title">getNodes</span> (line <span class="line-number">151</span>)
	</div> 
	
	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">Return the node set, in no particular order. For ordered node sets, use a Graph Manipulator insted.</p>
	<ul class="tags">
				<li><span class="field">return:</span> The set of nodes in this graph</li>
				<li><span class="field">see:</span> <a href="../Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.html">Structures_Graph_Manipulator_TopologicalSorter</a></li>
				<li><span class="field">access:</span> public</li>
			</ul>
	
	<div class="method-signature">
		<span class="method-result">array</span>
		<span class="method-name">
			&amp;getNodes
		</span>
				()
			</div>
	
		
		
	</div>
<a name="methodisDirected" id="isDirected"><!-- --></a>
<div class="oddrow">
	
	<div class="method-header">
		<span class="method-title">isDirected</span> (line <span class="line-number">89</span>)
	</div> 
	
	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">Return true if a graph is directed</p>
	<ul class="tags">
				<li><span class="field">return:</span> true if the graph is directed</li>
				<li><span class="field">access:</span> public</li>
			</ul>
	
	<div class="method-signature">
		<span class="method-result">boolean</span>
		<span class="method-name">
			isDirected
		</span>
				()
			</div>
	
		
		
	</div>
<a name="methodremoveNode" id="removeNode"><!-- --></a>
<div class="evenrow">
	
	<div class="method-header">
		<span class="method-title">removeNode</span> (line <span class="line-number">138</span>)
	</div> 
	
	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">Remove a Node from the Graph</p>
	<ul class="tags">
				<li><span class="field">access:</span> public</li>
				<li><span class="field">todo:</span> This is unimplemented</li>
			</ul>
	
	<div class="method-signature">
		<span class="method-result">void</span>
		<span class="method-name">
			removeNode
		</span>
					(<span class="var-type"><a href="../Structures_Graph/Structures_Graph_Node.html">Structures_Graph_Node</a></span>&nbsp;<span class="var-name">&$node</span>)
			</div>
	
			<ul class="parameters">
					<li>
				<span class="var-type"><a href="../Structures_Graph/Structures_Graph_Node.html">Structures_Graph_Node</a></span>
				<span class="var-name">&$node</span><span class="var-description">: The node to be removed from the graph</span>			</li>
				</ul>
		
		
	</div>
						
		</div>
	</div>
	
	<p class="notes" id="credit">
		Documentation generated on Fri, 30 Jan 2004 16:37:28 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
	</p>
	</div></body>
</html>Structures_Graph-1.0.2/docs/html/Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html100644   1750   1750        7560 10560475634  26516 <?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
		<head>
			<!-- template designed by Marco Von Ballmoos -->
			<title>Docs For Class Structures_Graph_Manipulator_AcyclicTest</title>
			<link rel="stylesheet" href="../media/stylesheet.css" />
			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
		</head>
		<body>
			<div class="page-body">			
<h2 class="class-name">Class Structures_Graph_Manipulator_AcyclicTest</h2>

<a name="sec-description"></a>
<div class="info-box">
	<div class="info-box-title">Description</div>
	<div class="nav-bar">
					<span class="disabled">Description</span> |
															<a href="#sec-method-summary">Methods</a> (<a href="#sec-methods">details</a>)
						
			</div>
	<div class="info-box-body">
		<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">The Structures_Graph_Manipulator_AcyclicTest is a graph manipulator  which tests whether a graph contains a cycle.</p>
<p class="description"><p>The definition of an acyclic graph used in this manipulator is that of a  DAG. The graph must be directed, or else it is considered cyclic, even when  there are no arcs.</p></p>
	<ul class="tags">
				<li><span class="field">copyright:</span> (c) 2004 by Srgio Carvalho</li>
				<li><span class="field">author:</span> Srgio Carvalho &lt;<a href="mailto:sergio.carvalho@portugalmail.com">mailto:sergio.carvalho@portugalmail.com</a>&gt;</li>
			</ul>
		<p class="notes">
			Located in <a class="field" href="_Structures_Graph_Manipulator_AcyclicTest_php.html">/Structures/Graph/Manipulator/AcyclicTest.php</a> (line <span class="field">55</span>)
		</p>
		
				
		<pre></pre>
	
			</div>
</div>



	<a name="sec-method-summary"></a>
	<div class="info-box">
		<div class="info-box-title">Method Summary</span></div>
		<div class="nav-bar">
			<a href="#sec-description">Description</a> |
									<span class="disabled">Methods</span> (<a href="#sec-methods">details</a>)
		</div>
		<div class="info-box-body">			
			<div class="method-summary">
								
				<div class="method-definition">
											<span class="method-result">boolean</span>
										<a href="#isAcyclic" title="details" class="method-name">isAcyclic</a>
											(<span class="var-type">mixed</span>&nbsp;<span class="var-name">&$graph</span>)
									</div>
							</div>
		</div>
	</div>		

	
	<a name="sec-methods"></a>
	<div class="info-box">
		<div class="info-box-title">Methods</div>
		<div class="nav-bar">
			<a href="#sec-description">Description</a> |
													<a href="#sec-method-summary">Methods</a> (<span class="disabled">details</span>)
						
		</div>
		<div class="info-box-body">
			<A NAME='method_detail'></A>
<a name="methodisAcyclic" id="isAcyclic"><!-- --></a>
<div class="evenrow">
	
	<div class="method-header">
		<span class="method-title">isAcyclic</span> (line <span class="line-number">126</span>)
	</div> 
	
	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">isAcyclic returns true if a graph contains no cycles, false otherwise.</p>
	<ul class="tags">
				<li><span class="field">return:</span> true iff graph is acyclic</li>
				<li><span class="field">access:</span> public</li>
			</ul>
	
	<div class="method-signature">
		<span class="method-result">boolean</span>
		<span class="method-name">
			isAcyclic
		</span>
					(<span class="var-type">mixed</span>&nbsp;<span class="var-name">&$graph</span>)
			</div>
	
		
		
	</div>
						
		</div>
	</div>
	
	<p class="notes" id="credit">
		Documentation generated on Fri, 30 Jan 2004 16:37:28 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
	</p>
	</div></body>
</html>././@LongLink000        145 0             4476 LStructures_Graph-1.0.2/docs/html/Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.htmlStructures_Graph-1.0.2/docs/html/Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.htm100644   1750   1750       10313 10560475634  27614 <?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
		<head>
			<!-- template designed by Marco Von Ballmoos -->
			<title>Docs For Class Structures_Graph_Manipulator_TopologicalSorter</title>
			<link rel="stylesheet" href="../media/stylesheet.css" />
			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
		</head>
		<body>
			<div class="page-body">			
<h2 class="class-name">Class Structures_Graph_Manipulator_TopologicalSorter</h2>

<a name="sec-description"></a>
<div class="info-box">
	<div class="info-box-title">Description</div>
	<div class="nav-bar">
					<span class="disabled">Description</span> |
															<a href="#sec-method-summary">Methods</a> (<a href="#sec-methods">details</a>)
						
			</div>
	<div class="info-box-body">
		<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">The Structures_Graph_Manipulator_TopologicalSorter is a manipulator  which is able to return the set of nodes in a graph, sorted by topological  order.</p>
<p class="description"><p>A graph may only be sorted topologically iff it's a DAG. You can test it  with the Structures_Graph_Manipulator_AcyclicTest.</p></p>
	<ul class="tags">
				<li><span class="field">see:</span> <a href="../Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html">Structures_Graph_Manipulator_AcyclicTest</a></li>
				<li><span class="field">copyright:</span> (c) 2004 by Srgio Carvalho</li>
				<li><span class="field">author:</span> Srgio Carvalho &lt;<a href="mailto:sergio.carvalho@portugalmail.com">mailto:sergio.carvalho@portugalmail.com</a>&gt;</li>
			</ul>
		<p class="notes">
			Located in <a class="field" href="_Structures_Graph_Manipulator_TopologicalSorter_php.html">/Structures/Graph/Manipulator/TopologicalSorter.php</a> (line <span class="field">58</span>)
		</p>
		
				
		<pre></pre>
	
			</div>
</div>



	<a name="sec-method-summary"></a>
	<div class="info-box">
		<div class="info-box-title">Method Summary</span></div>
		<div class="nav-bar">
			<a href="#sec-description">Description</a> |
									<span class="disabled">Methods</span> (<a href="#sec-methods">details</a>)
		</div>
		<div class="info-box-body">			
			<div class="method-summary">
								
				<div class="method-definition">
											<span class="method-result">array</span>
										<a href="#sort" title="details" class="method-name">sort</a>
											(<span class="var-type">mixed</span>&nbsp;<span class="var-name">&$graph</span>)
									</div>
							</div>
		</div>
	</div>		

	
	<a name="sec-methods"></a>
	<div class="info-box">
		<div class="info-box-title">Methods</div>
		<div class="nav-bar">
			<a href="#sec-description">Description</a> |
													<a href="#sec-method-summary">Methods</a> (<span class="disabled">details</span>)
						
		</div>
		<div class="info-box-body">
			<A NAME='method_detail'></A>
<a name="methodsort" id="sort"><!-- --></a>
<div class="evenrow">
	
	<div class="method-header">
		<span class="method-title">sort</span> (line <span class="line-number">133</span>)
	</div> 
	
	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">sort returns the graph's nodes, sorted by topological order.</p>
<p class="description"><p>The result is an array with  as many entries as topological levels. Each entry in this array is an array of nodes within  the given topological level.</p></p>
	<ul class="tags">
				<li><span class="field">return:</span> The graph's nodes, sorted by topological order.</li>
				<li><span class="field">access:</span> public</li>
			</ul>
	
	<div class="method-signature">
		<span class="method-result">array</span>
		<span class="method-name">
			sort
		</span>
					(<span class="var-type">mixed</span>&nbsp;<span class="var-name">&$graph</span>)
			</div>
	
		
		
	</div>
						
		</div>
	</div>
	
	<p class="notes" id="credit">
		Documentation generated on Fri, 30 Jan 2004 16:37:29 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
	</p>
	</div></body>
</html>Structures_Graph-1.0.2/docs/html/Structures_Graph/Structures_Graph_Node.html100644   1750   1750       51135 10560475634  22636 <?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
		<head>
			<!-- template designed by Marco Von Ballmoos -->
			<title>Docs For Class Structures_Graph_Node</title>
			<link rel="stylesheet" href="../media/stylesheet.css" />
			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
		</head>
		<body>
			<div class="page-body">			
<h2 class="class-name">Class Structures_Graph_Node</h2>

<a name="sec-description"></a>
<div class="info-box">
	<div class="info-box-title">Description</div>
	<div class="nav-bar">
					<span class="disabled">Description</span> |
															<a href="#sec-method-summary">Methods</a> (<a href="#sec-methods">details</a>)
						
			</div>
	<div class="info-box-body">
		<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">The Structures_Graph_Node class represents a Node that can be member of a  graph node set.</p>
<p class="description"><p>A graph node can contain data. Under this API, the node contains default data,  and key index data. It behaves, thus, both as a regular data node, and as a  dictionary (or associative array) node.</p><p>Regular data is accessed via getData and setData. Key indexed data is accessed  via getMetadata and setMetadata.</p></p>
	<ul class="tags">
				<li><span class="field">copyright:</span> (c) 2004 by Srgio Carvalho</li>
				<li><span class="field">author:</span> Srgio Carvalho &lt;<a href="mailto:sergio.carvalho@portugalmail.com">mailto:sergio.carvalho@portugalmail.com</a>&gt;</li>
			</ul>
		<p class="notes">
			Located in <a class="field" href="_Structures_Graph_Node_php.html">/Structures/Graph/Node.php</a> (line <span class="field">57</span>)
		</p>
		
				
		<pre></pre>
	
			</div>
</div>



	<a name="sec-method-summary"></a>
	<div class="info-box">
		<div class="info-box-title">Method Summary</span></div>
		<div class="nav-bar">
			<a href="#sec-description">Description</a> |
									<span class="disabled">Methods</span> (<a href="#sec-methods">details</a>)
		</div>
		<div class="info-box-body">			
			<div class="method-summary">
								
				<div class="method-definition">
											<span class="method-result">Structures_Graph_Node</span>
										<a href="#Structures_Graph_Node" title="details" class="method-name">Structures_Graph_Node</a>
										()
									</div>
								
				<div class="method-definition">
											<span class="method-result">boolean</span>
										<a href="#connectsTo" title="details" class="method-name">connectsTo</a>
											(<span class="var-type">mixed</span>&nbsp;<span class="var-name">&$target</span>)
									</div>
								
				<div class="method-definition">
											<span class="method-result">void</span>
										<a href="#connectTo" title="details" class="method-name">connectTo</a>
											(<span class="var-type"><a href="../Structures_Graph/Structures_Graph.html">Structures_Graph</a></span>&nbsp;<span class="var-name">&$destinationNode</span>)
									</div>
								
				<div class="method-definition">
											<span class="method-result">mixed</span>
										<a href="#getData" title="details" class="method-name">&amp;getData</a>
										()
									</div>
								
				<div class="method-definition">
											<span class="method-result"><a href="../Structures_Graph/Structures_Graph.html">Structures_Graph</a></span>
										<a href="#getGraph" title="details" class="method-name">&amp;getGraph</a>
										()
									</div>
								
				<div class="method-definition">
											<span class="method-result">mixed</span>
										<a href="#getMetadata" title="details" class="method-name">&amp;getMetadata</a>
											(<span class="var-type">string</span>&nbsp;<span class="var-name">$key</span>, [<span class="var-type">boolean</span>&nbsp;<span class="var-name">$nullIfNonexistent</span> = <span class="var-default">false</span>])
									</div>
								
				<div class="method-definition">
											<span class="method-result">array</span>
										<a href="#getNeighbours" title="details" class="method-name">getNeighbours</a>
										()
									</div>
								
				<div class="method-definition">
											<span class="method-result">integer</span>
										<a href="#inDegree" title="details" class="method-name">inDegree</a>
										()
									</div>
								
				<div class="method-definition">
											<span class="method-result">boolean</span>
										<a href="#metadataKeyExists" title="details" class="method-name">metadataKeyExists</a>
											(<span class="var-type">string</span>&nbsp;<span class="var-name">$key</span>)
									</div>
								
				<div class="method-definition">
											<span class="method-result">integer</span>
										<a href="#outDegree" title="details" class="method-name">outDegree</a>
										()
									</div>
								
				<div class="method-definition">
											<span class="method-result">mixed</span>
										<a href="#setData" title="details" class="method-name">setData</a>
											(<span class="var-type">mixed</span>&nbsp;<span class="var-name">$data</span>)
									</div>
								
				<div class="method-definition">
											<span class="method-result">void</span>
										<a href="#setGraph" title="details" class="method-name">setGraph</a>
											(<span class="var-type"><a href="../Structures_Graph/Structures_Graph.html">Structures_Graph</a></span>&nbsp;<span class="var-name">&$graph</span>)
									</div>
								
				<div class="method-definition">
											<span class="method-result">void</span>
										<a href="#setMetadata" title="details" class="method-name">setMetadata</a>
											(<span class="var-type">string</span>&nbsp;<span class="var-name">$key</span>, <span class="var-type">mixed</span>&nbsp;<span class="var-name">$data</span>)
									</div>
								
				<div class="method-definition">
											<span class="method-result">void</span>
										<a href="#unsetMetadata" title="details" class="method-name">unsetMetadata</a>
											(<span class="var-type">string</span>&nbsp;<span class="var-name">$key</span>)
									</div>
							</div>
		</div>
	</div>		

	
	<a name="sec-methods"></a>
	<div class="info-box">
		<div class="info-box-title">Methods</div>
		<div class="nav-bar">
			<a href="#sec-description">Description</a> |
													<a href="#sec-method-summary">Methods</a> (<span class="disabled">details</span>)
						
		</div>
		<div class="info-box-body">
			<A NAME='method_detail'></A>
<a name="methodStructures_Graph_Node" id="Structures_Graph_Node"><!-- --></a>
<div class="evenrow">
	
	<div class="method-header">
		<span class="method-title">Constructor Structures_Graph_Node</span> (line <span class="line-number">78</span>)
	</div> 
	
	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">Constructor</p>
	<ul class="tags">
				<li><span class="field">access:</span> public</li>
			</ul>
	
	<div class="method-signature">
		<span class="method-result">Structures_Graph_Node</span>
		<span class="method-name">
			Structures_Graph_Node
		</span>
				()
			</div>
	
		
		
	</div>
<a name="methodconnectsTo" id="connectsTo"><!-- --></a>
<div class="oddrow">
	
	<div class="method-header">
		<span class="method-title">connectsTo</span> (line <span class="line-number">275</span>)
	</div> 
	
	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">Test wether this node has an arc to the target node</p>
	<ul class="tags">
				<li><span class="field">return:</span> True if the two nodes are connected</li>
				<li><span class="field">access:</span> public</li>
			</ul>
	
	<div class="method-signature">
		<span class="method-result">boolean</span>
		<span class="method-name">
			connectsTo
		</span>
					(<span class="var-type">mixed</span>&nbsp;<span class="var-name">&$target</span>)
			</div>
	
		
		
	</div>
<a name="methodconnectTo" id="connectTo"><!-- --></a>
<div class="evenrow">
	
	<div class="method-header">
		<span class="method-title">connectTo</span> (line <span class="line-number">236</span>)
	</div> 
	
	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">Connect this node to another one.</p>
<p class="description"><p>If the graph is not directed, the reverse arc, connecting $destinationNode to $this is also created.</p></p>
	<ul class="tags">
				<li><span class="field">access:</span> public</li>
			</ul>
	
	<div class="method-signature">
		<span class="method-result">void</span>
		<span class="method-name">
			connectTo
		</span>
					(<span class="var-type"><a href="../Structures_Graph/Structures_Graph.html">Structures_Graph</a></span>&nbsp;<span class="var-name">&$destinationNode</span>)
			</div>
	
			<ul class="parameters">
					<li>
				<span class="var-type"><a href="../Structures_Graph/Structures_Graph.html">Structures_Graph</a></span>
				<span class="var-name">&$destinationNode</span><span class="var-description">: Node to connect to</span>			</li>
				</ul>
		
		
	</div>
<a name="methodgetData" id="getData"><!-- --></a>
<div class="oddrow">
	
	<div class="method-header">
		<span class="method-title">getData</span> (line <span class="line-number">119</span>)
	</div> 
	
	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">Node data getter.</p>
<p class="description"><p>Each graph node can contain a reference to one variable. This is the getter for that reference.</p></p>
	<ul class="tags">
				<li><span class="field">return:</span> Data stored in node</li>
				<li><span class="field">access:</span> public</li>
			</ul>
	
	<div class="method-signature">
		<span class="method-result">mixed</span>
		<span class="method-name">
			&amp;getData
		</span>
				()
			</div>
	
		
		
	</div>
<a name="methodgetGraph" id="getGraph"><!-- --></a>
<div class="evenrow">
	
	<div class="method-header">
		<span class="method-title">getGraph</span> (line <span class="line-number">90</span>)
	</div> 
	
	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">Node graph getter</p>
	<ul class="tags">
				<li><span class="field">return:</span> Graph where node is stored</li>
				<li><span class="field">access:</span> public</li>
			</ul>
	
	<div class="method-signature">
		<span class="method-result"><a href="../Structures_Graph/Structures_Graph.html">Structures_Graph</a></span>
		<span class="method-name">
			&amp;getGraph
		</span>
				()
			</div>
	
		
		
	</div>
<a name="methodgetMetadata" id="getMetadata"><!-- --></a>
<div class="oddrow">
	
	<div class="method-header">
		<span class="method-title">getMetadata</span> (line <span class="line-number">171</span>)
	</div> 
	
	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">Node metadata getter</p>
<p class="description"><p>Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an  associative array or in a dictionary. This method gets the data under the given key. If the key does  not exist, an error will be thrown, so testing using metadataKeyExists might be needed.</p></p>
	<ul class="tags">
				<li><span class="field">return:</span> Metadata Data stored in node under given key</li>
				<li><span class="field">access:</span> public</li>
				<li><span class="field">see:</span> <a href="../Structures_Graph/Structures_Graph_Node.html#methodmetadataKeyExists">Structures_Graph_Node::metadataKeyExists()</a></li>
			</ul>
	
	<div class="method-signature">
		<span class="method-result">mixed</span>
		<span class="method-name">
			&amp;getMetadata
		</span>
					(<span class="var-type">string</span>&nbsp;<span class="var-name">$key</span>, [<span class="var-type">boolean</span>&nbsp;<span class="var-name">$nullIfNonexistent</span> = <span class="var-default">false</span>])
			</div>
	
			<ul class="parameters">
					<li>
				<span class="var-type">string</span>
				<span class="var-name">$key</span><span class="var-description">: Key</span>			</li>
					<li>
				<span class="var-type">boolean</span>
				<span class="var-name">$nullIfNonexistent</span><span class="var-description">: nullIfNonexistent (defaults to false).</span>			</li>
				</ul>
		
		
	</div>
<a name="methodgetNeighbours" id="getNeighbours"><!-- --></a>
<div class="evenrow">
	
	<div class="method-header">
		<span class="method-title">getNeighbours</span> (line <span class="line-number">262</span>)
	</div> 
	
	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">Return nodes connected to this one.</p>
	<ul class="tags">
				<li><span class="field">return:</span> Array of nodes</li>
				<li><span class="field">access:</span> public</li>
			</ul>
	
	<div class="method-signature">
		<span class="method-result">array</span>
		<span class="method-name">
			getNeighbours
		</span>
				()
			</div>
	
		
		
	</div>
<a name="methodinDegree" id="inDegree"><!-- --></a>
<div class="oddrow">
	
	<div class="method-header">
		<span class="method-title">inDegree</span> (line <span class="line-number">309</span>)
	</div> 
	
	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">Calculate the in degree of the node.</p>
<p class="description"><p>The indegree for a node is the number of arcs entering the node. For non directed graphs,  the indegree is equal to the outdegree.</p></p>
	<ul class="tags">
				<li><span class="field">return:</span> In degree of the node</li>
				<li><span class="field">access:</span> public</li>
			</ul>
	
	<div class="method-signature">
		<span class="method-result">integer</span>
		<span class="method-name">
			inDegree
		</span>
				()
			</div>
	
		
		
	</div>
<a name="methodmetadataKeyExists" id="metadataKeyExists"><!-- --></a>
<div class="evenrow">
	
	<div class="method-header">
		<span class="method-title">metadataKeyExists</span> (line <span class="line-number">151</span>)
	</div> 
	
	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">Test for existence of metadata under a given key.</p>
<p class="description"><p>Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an  associative array or in a dictionary. This method tests whether a given metadata key exists for this node.</p></p>
	<ul class="tags">
				<li><span class="field">access:</span> public</li>
			</ul>
	
	<div class="method-signature">
		<span class="method-result">boolean</span>
		<span class="method-name">
			metadataKeyExists
		</span>
					(<span class="var-type">string</span>&nbsp;<span class="var-name">$key</span>)
			</div>
	
			<ul class="parameters">
					<li>
				<span class="var-type">string</span>
				<span class="var-name">$key</span><span class="var-description">: Key to test</span>			</li>
				</ul>
		
		
	</div>
<a name="methodoutDegree" id="outDegree"><!-- --></a>
<div class="oddrow">
	
	<div class="method-header">
		<span class="method-title">outDegree</span> (line <span class="line-number">333</span>)
	</div> 
	
	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">Calculate the out degree of the node.</p>
<p class="description"><p>The outdegree for a node is the number of arcs exiting the node. For non directed graphs,  the outdegree is always equal to the indegree.</p></p>
	<ul class="tags">
				<li><span class="field">return:</span> Out degree of the node</li>
				<li><span class="field">access:</span> public</li>
			</ul>
	
	<div class="method-signature">
		<span class="method-result">integer</span>
		<span class="method-name">
			outDegree
		</span>
				()
			</div>
	
		
		
	</div>
<a name="methodsetData" id="setData"><!-- --></a>
<div class="evenrow">
	
	<div class="method-header">
		<span class="method-title">setData</span> (line <span class="line-number">134</span>)
	</div> 
	
	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">Node data setter</p>
<p class="description"><p>Each graph node can contain a reference to one variable. This is the setter for that reference.</p></p>
	<ul class="tags">
				<li><span class="field">return:</span> Data to store in node</li>
				<li><span class="field">access:</span> public</li>
			</ul>
	
	<div class="method-signature">
		<span class="method-result">mixed</span>
		<span class="method-name">
			setData
		</span>
					(<span class="var-type">mixed</span>&nbsp;<span class="var-name">$data</span>)
			</div>
	
		
		
	</div>
<a name="methodsetGraph" id="setGraph"><!-- --></a>
<div class="oddrow">
	
	<div class="method-header">
		<span class="method-title">setGraph</span> (line <span class="line-number">104</span>)
	</div> 
	
	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">Node graph setter. This method should not be called directly. Use Graph::addNode instead.</p>
	<ul class="tags">
				<li><span class="field">access:</span> public</li>
				<li><span class="field">see:</span> <a href="../Structures_Graph/Structures_Graph.html#methodaddNode">Structures_Graph::addNode()</a></li>
			</ul>
	
	<div class="method-signature">
		<span class="method-result">void</span>
		<span class="method-name">
			setGraph
		</span>
					(<span class="var-type"><a href="../Structures_Graph/Structures_Graph.html">Structures_Graph</a></span>&nbsp;<span class="var-name">&$graph</span>)
			</div>
	
			<ul class="parameters">
					<li>
				<span class="var-type"><a href="../Structures_Graph/Structures_Graph.html">Structures_Graph</a></span>
				<span class="var-name">&$graph</span><span class="var-description">: Set the graph for this node.</span>			</li>
				</ul>
		
		
	</div>
<a name="methodsetMetadata" id="setMetadata"><!-- --></a>
<div class="evenrow">
	
	<div class="method-header">
		<span class="method-title">setMetadata</span> (line <span class="line-number">214</span>)
	</div> 
	
	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">Node metadata setter</p>
<p class="description"><p>Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an  associative array or in a dictionary. This method stores data under the given key. If the key already exists,  previously stored data is discarded.</p></p>
	<ul class="tags">
				<li><span class="field">access:</span> public</li>
			</ul>
	
	<div class="method-signature">
		<span class="method-result">void</span>
		<span class="method-name">
			setMetadata
		</span>
					(<span class="var-type">string</span>&nbsp;<span class="var-name">$key</span>, <span class="var-type">mixed</span>&nbsp;<span class="var-name">$data</span>)
			</div>
	
			<ul class="parameters">
					<li>
				<span class="var-type">string</span>
				<span class="var-name">$key</span><span class="var-description">: Key</span>			</li>
					<li>
				<span class="var-type">mixed</span>
				<span class="var-name">$data</span><span class="var-description">: Data</span>			</li>
				</ul>
		
		
	</div>
<a name="methodunsetMetadata" id="unsetMetadata"><!-- --></a>
<div class="oddrow">
	
	<div class="method-header">
		<span class="method-title">unsetMetadata</span> (line <span class="line-number">196</span>)
	</div> 
	
	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">Delete metadata by key</p>
<p class="description"><p>Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an  associative array or in a dictionary. This method removes any data that might be stored under the provided key.  If the key does not exist, no error is thrown, so it is safe using this method without testing for key existence.</p></p>
	<ul class="tags">
				<li><span class="field">access:</span> public</li>
			</ul>
	
	<div class="method-signature">
		<span class="method-result">void</span>
		<span class="method-name">
			unsetMetadata
		</span>
					(<span class="var-type">string</span>&nbsp;<span class="var-name">$key</span>)
			</div>
	
			<ul class="parameters">
					<li>
				<span class="var-type">string</span>
				<span class="var-name">$key</span><span class="var-description">: Key</span>			</li>
				</ul>
		
		
	</div>
						
		</div>
	</div>
	
	<p class="notes" id="credit">
		Documentation generated on Fri, 30 Jan 2004 16:37:29 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
	</p>
	</div></body>
</html>Structures_Graph-1.0.2/docs/html/Structures_Graph/tutorial_Structures_Graph.pkg.html100644   1750   1750       11131 10560475634  24364 <?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
		<head>
			<!-- template designed by Marco Von Ballmoos -->
			<title>Structures_Graph Tutorial</title>
			<link rel="stylesheet" href="../media/stylesheet.css" />
			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
		</head>
		<body>
			<div class="page-body">			

<div><a name="package.database.structures_graph.tutorial"></a><div class="ref-title-box"><h1 class="ref-title">Structures_Graph Tutorial</h1>
  <h2 class="ref-purpose">A first tour of graph datastructure manipulation</h2></div>
 <span><a name="package.database.structures_graph.tutorial.intro"></a><h2 class="title">Introduction</h2><p>Structures_Graph is a package for creating and manipulating graph datastructures. A graph is a set of objects, called nodes, connected by arcs. When used as a datastructure, usually nodes contain data, and arcs represent relationships between nodes. When arcs have a direction, and can be travelled only one way, graphs are said to be directed. When arcs have no direction, and can always be travelled both ways, graphs are said to be non directed.</p>
  <p>Structures_Graph provides an object oriented API to create and directly query a graph, as well as a set of Manipulator classes to extract information from the graph.</p></span>
 <span><a name="package.database.structures_graph.tutorial.creation"></a><h2 class="title">Creating a Graph</h2><p>Creating a graph is done using the simple constructor:
   <pre class="listing"><pre>
require_once 'Structures/Graph.php';

$directedGraph =&amp; new Structures_Graph(true);
$nonDirectedGraph =&amp; new Structures_Graph(false);
    </pre></pre>
   and passing the constructor a flag telling it whether the graph should be directed. A directed graph will always be directed during its lifetime. It's a permanent characteristic.</p>
  <p>To fill out the graph, we'll need to create some nodes, and then call Graph::addNode.
   <pre class="listing"><pre>
require_once 'Structures/Graph/Node.php';

$nodeOne =&amp; new Structures_Graph_Node();
$nodeTwo =&amp; new Structures_Graph_Node();
$nodeThree =&amp; new Structures_Graph_Node();

$directedGraph-&gt;addNode(&amp;$nodeOne);
$directedGraph-&gt;addNode(&amp;$nodeTwo);
$directedGraph-&gt;addNode(&amp;$nodeThree);
    </pre></pre>
   and then setup the arcs:
   <pre class="listing"><pre>
$nodeOne-&gt;connectTo($nodeTwo);
$nodeOne-&gt;connectTo($nodeThree);
    </pre></pre>
   Note that arcs can only be created after the nodes have been inserted into the graph.</p></span>
 <span><a name="package.database.structures_graph.tutorial.nodesanddata"></a><h2 class="title">Associating Data</h2><p>Graphs are only useful as datastructures if they can hold data. Structure_Graph stores data in nodes. Each node contains a setter and a getter for its data.
   <pre class="listing"><pre>
$nodeOne-&gt;setData(&quot;Node One's Data is a String&quot;);
$nodeTwo-&gt;setData(1976);
$nodeThree-&gt;setData('Some other string');

print(&quot;NodeTwo's Data is an integer: &quot; . $nodeTwo-&gt;getData());
    </pre></pre></p>
  <p>Structure_Graph nodes can also store metadata, alongside with the main data. Metadata differs from regular data just because it is stored under a key, making it possible to store more than one data reference per node. The metadata getter and setter need the key to perform the operation:
   <pre class="listing"><pre>
$nodeOne-&gt;setMetadata('example key', &quot;Node One's Sample Metadata&quot;);
print(&quot;Metadata stored under key 'example key' in node one: &quot; . $nodeOne-&gt;getMetadata('example key'));
$nodeOne-&gt;unsetMetadata('example key');
    </pre></pre></p></span>
 <span><a name="package.database.structures_graph.tutorial.querying"></a><h2 class="title">Querying a Graph</h2><p>Structures_Graph provides for basic querying of the graph:
   <pre class="listing"><pre>
// Nodes are able to calculate their indegree and outdegree
print(&quot;NodeOne's inDegree: &quot; . $nodeOne-&gt;inDegree());
print(&quot;NodeOne's outDegree: &quot; . $nodeOne-&gt;outDegree());

// and naturally, nodes can report on their arcs
$arcs = $nodeOne-&gt;getNeighbours();
for ($i=0;$i&lt;sizeof($arcs);$i++) {
    print(&quot;NodeOne has an arc to &quot; . $arcs[$i]-&gt;getData());
}
    </pre></pre></p></span></div>


	<p class="notes" id="credit">
		Documentation generated on Fri, 30 Jan 2004 16:37:28 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
	</p>
	</div></body>
</html>././@LongLink000        144 0             4475 LStructures_Graph-1.0.2/docs/html/Structures_Graph/_Structures_Graph_Manipulator_AcyclicTest_php.htmlStructures_Graph-1.0.2/docs/html/Structures_Graph/_Structures_Graph_Manipulator_AcyclicTest_php.html100644   1750   1750        7650 10560475634  27524 <?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
		<head>
			<!-- template designed by Marco Von Ballmoos -->
			<title>Docs for page AcyclicTest.php</title>
			<link rel="stylesheet" href="../media/stylesheet.css" />
			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
		</head>
		<body>
			<div class="page-body">			
<h2 class="file-name">/Structures/Graph/Manipulator/AcyclicTest.php</h2>

<a name="sec-description"></a>
<div class="info-box">
	<div class="info-box-title">Description</div>
	<div class="nav-bar">
					<span class="disabled">Description</span> |
							<a href="#sec-classes">Classes</a>
			|							<a href="#sec-includes">Includes</a>
												</div>
	<div class="info-box-body">	
		<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">This file contains the definition of the Structures_Graph_Manipulator_AcyclicTest graph manipulator.</p>
	<ul class="tags">
				<li><span class="field">see:</span> <a href="../Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html">Structures_Graph_Manipulator_AcyclicTest</a></li>
			</ul>
		
			</div>
</div>
		
	<a name="sec-classes"></a>	
	<div class="info-box">
		<div class="info-box-title">Classes</div>
		<div class="nav-bar">
			<a href="#sec-description">Description</a> |
			<span class="disabled">Classes</span>
			|							<a href="#sec-includes">Includes</a>
																		</div>
		<div class="info-box-body">	
			<table cellpadding="2" cellspacing="0" class="class-table">
				<tr>
					<th class="class-table-header">Class</th>
					<th class="class-table-header">Description</th>
				</tr>
								<tr>
					<td style="padding-right: 2em; vertical-align: top">
						<a href="../Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html">Structures_Graph_Manipulator_AcyclicTest</a>
					</td>
					<td>
											The Structures_Graph_Manipulator_AcyclicTest is a graph manipulator  which tests whether a graph contains a cycle.
										</td>
				</tr>
							</table>
		</div>
	</div>

	<a name="sec-includes"></a>	
	<div class="info-box">
		<div class="info-box-title">Includes</div>
		<div class="nav-bar">
			<a href="#sec-description">Description</a> |
							<a href="#sec-classes">Classes</a>
				|						<span class="disabled">Includes</span>
														</div>
		<div class="info-box-body">	
			<a name="_PEAR_php"><!-- --></a>
<div class="oddrow">
	
	<div>
		<span class="include-title">
			<span class="include-type">require_once</span>
			(<span class="include-name">'PEAR.php'</span>)
			(line <span class="line-number">35</span>)
		</span>
	</div>

	<!-- ========== Info from phpDoc block ========= -->
	
</div>
<a name="_Structures/Graph_php"><!-- --></a>
<div class="evenrow">
	
	<div>
		<span class="include-title">
			<span class="include-type">require_once</span>
			(<span class="include-name"><a href="../Structures_Graph/_Structures_Graph_php.html">'Structures/Graph.php'</a></span>)
			(line <span class="line-number">37</span>)
		</span>
	</div>

	<!-- ========== Info from phpDoc block ========= -->
	
</div>
<a name="_Structures/Graph/Node_php"><!-- --></a>
<div class="oddrow">
	
	<div>
		<span class="include-title">
			<span class="include-type">require_once</span>
			(<span class="include-name"><a href="../Structures_Graph/_Structures_Graph_Node_php.html">'Structures/Graph/Node.php'</a></span>)
			(line <span class="line-number">39</span>)
		</span>
	</div>

	<!-- ========== Info from phpDoc block ========= -->
	
</div>
		</div>
	</div>
	
	
	
	
	<p class="notes" id="credit">
		Documentation generated on Fri, 30 Jan 2004 16:37:28 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
	</p>
	</div></body>
</html>././@LongLink000        152 0             4474 LStructures_Graph-1.0.2/docs/html/Structures_Graph/_Structures_Graph_Manipulator_TopologicalSorter_php.htmlStructures_Graph-1.0.2/docs/html/Structures_Graph/_Structures_Graph_Manipulator_TopologicalSorter_ph100644   1750   1750       10731 10560475634  27657 <?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
		<head>
			<!-- template designed by Marco Von Ballmoos -->
			<title>Docs for page TopologicalSorter.php</title>
			<link rel="stylesheet" href="../media/stylesheet.css" />
			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
		</head>
		<body>
			<div class="page-body">			
<h2 class="file-name">/Structures/Graph/Manipulator/TopologicalSorter.php</h2>

<a name="sec-description"></a>
<div class="info-box">
	<div class="info-box-title">Description</div>
	<div class="nav-bar">
					<span class="disabled">Description</span> |
							<a href="#sec-classes">Classes</a>
			|							<a href="#sec-includes">Includes</a>
												</div>
	<div class="info-box-body">	
		<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">This file contains the definition of the Structures_Graph_Manipulator_TopologicalSorter class.</p>
	<ul class="tags">
				<li><span class="field">see:</span> <a href="../Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.html">Structures_Graph_Manipulator_TopologicalSorter</a></li>
			</ul>
		
			</div>
</div>
		
	<a name="sec-classes"></a>	
	<div class="info-box">
		<div class="info-box-title">Classes</div>
		<div class="nav-bar">
			<a href="#sec-description">Description</a> |
			<span class="disabled">Classes</span>
			|							<a href="#sec-includes">Includes</a>
																		</div>
		<div class="info-box-body">	
			<table cellpadding="2" cellspacing="0" class="class-table">
				<tr>
					<th class="class-table-header">Class</th>
					<th class="class-table-header">Description</th>
				</tr>
								<tr>
					<td style="padding-right: 2em; vertical-align: top">
						<a href="../Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.html">Structures_Graph_Manipulator_TopologicalSorter</a>
					</td>
					<td>
											The Structures_Graph_Manipulator_TopologicalSorter is a manipulator  which is able to return the set of nodes in a graph, sorted by topological  order.
										</td>
				</tr>
							</table>
		</div>
	</div>

	<a name="sec-includes"></a>	
	<div class="info-box">
		<div class="info-box-title">Includes</div>
		<div class="nav-bar">
			<a href="#sec-description">Description</a> |
							<a href="#sec-classes">Classes</a>
				|						<span class="disabled">Includes</span>
														</div>
		<div class="info-box-body">	
			<a name="_PEAR_php"><!-- --></a>
<div class="oddrow">
	
	<div>
		<span class="include-title">
			<span class="include-type">require_once</span>
			(<span class="include-name">'PEAR.php'</span>)
			(line <span class="line-number">35</span>)
		</span>
	</div>

	<!-- ========== Info from phpDoc block ========= -->
	
</div>
<a name="_Structures/Graph_php"><!-- --></a>
<div class="evenrow">
	
	<div>
		<span class="include-title">
			<span class="include-type">require_once</span>
			(<span class="include-name"><a href="../Structures_Graph/_Structures_Graph_php.html">'Structures/Graph.php'</a></span>)
			(line <span class="line-number">37</span>)
		</span>
	</div>

	<!-- ========== Info from phpDoc block ========= -->
	
</div>
<a name="_Structures/Graph/Node_php"><!-- --></a>
<div class="oddrow">
	
	<div>
		<span class="include-title">
			<span class="include-type">require_once</span>
			(<span class="include-name"><a href="../Structures_Graph/_Structures_Graph_Node_php.html">'Structures/Graph/Node.php'</a></span>)
			(line <span class="line-number">39</span>)
		</span>
	</div>

	<!-- ========== Info from phpDoc block ========= -->
	
</div>
<a name="_Structures/Graph/Manipulator/AcyclicTest_php"><!-- --></a>
<div class="evenrow">
	
	<div>
		<span class="include-title">
			<span class="include-type">require_once</span>
			(<span class="include-name"><a href="../Structures_Graph/_Structures_Graph_Manipulator_AcyclicTest_php.html">'Structures/Graph/Manipulator/AcyclicTest.php'</a></span>)
			(line <span class="line-number">41</span>)
		</span>
	</div>

	<!-- ========== Info from phpDoc block ========= -->
	
</div>
		</div>
	</div>
	
	
	
	
	<p class="notes" id="credit">
		Documentation generated on Fri, 30 Jan 2004 16:37:29 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
	</p>
	</div></body>
</html>Structures_Graph-1.0.2/docs/html/Structures_Graph/_Structures_Graph_Node_php.html100644   1750   1750        6526 10560475634  23630 <?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
		<head>
			<!-- template designed by Marco Von Ballmoos -->
			<title>Docs for page Node.php</title>
			<link rel="stylesheet" href="../media/stylesheet.css" />
			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
		</head>
		<body>
			<div class="page-body">			
<h2 class="file-name">/Structures/Graph/Node.php</h2>

<a name="sec-description"></a>
<div class="info-box">
	<div class="info-box-title">Description</div>
	<div class="nav-bar">
					<span class="disabled">Description</span> |
							<a href="#sec-classes">Classes</a>
			|							<a href="#sec-includes">Includes</a>
												</div>
	<div class="info-box-body">	
		<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">This file contains the definition of the Structures_Graph_Node class</p>
	<ul class="tags">
				<li><span class="field">see:</span> <a href="../Structures_Graph/Structures_Graph_Node.html">Structures_Graph_Node</a></li>
			</ul>
		
			</div>
</div>
		
	<a name="sec-classes"></a>	
	<div class="info-box">
		<div class="info-box-title">Classes</div>
		<div class="nav-bar">
			<a href="#sec-description">Description</a> |
			<span class="disabled">Classes</span>
			|							<a href="#sec-includes">Includes</a>
																		</div>
		<div class="info-box-body">	
			<table cellpadding="2" cellspacing="0" class="class-table">
				<tr>
					<th class="class-table-header">Class</th>
					<th class="class-table-header">Description</th>
				</tr>
								<tr>
					<td style="padding-right: 2em; vertical-align: top">
						<a href="../Structures_Graph/Structures_Graph_Node.html">Structures_Graph_Node</a>
					</td>
					<td>
											The Structures_Graph_Node class represents a Node that can be member of a  graph node set.
										</td>
				</tr>
							</table>
		</div>
	</div>

	<a name="sec-includes"></a>	
	<div class="info-box">
		<div class="info-box-title">Includes</div>
		<div class="nav-bar">
			<a href="#sec-description">Description</a> |
							<a href="#sec-classes">Classes</a>
				|						<span class="disabled">Includes</span>
														</div>
		<div class="info-box-body">	
			<a name="_PEAR_php"><!-- --></a>
<div class="evenrow">
	
	<div>
		<span class="include-title">
			<span class="include-type">require_once</span>
			(<span class="include-name">'PEAR.php'</span>)
			(line <span class="line-number">35</span>)
		</span>
	</div>

	<!-- ========== Info from phpDoc block ========= -->
	
</div>
<a name="_Structures/Graph_php"><!-- --></a>
<div class="oddrow">
	
	<div>
		<span class="include-title">
			<span class="include-type">require_once</span>
			(<span class="include-name"><a href="../Structures_Graph/_Structures_Graph_php.html">'Structures/Graph.php'</a></span>)
			(line <span class="line-number">37</span>)
		</span>
	</div>

	<!-- ========== Info from phpDoc block ========= -->
	
</div>
		</div>
	</div>
	
	
	
	
	<p class="notes" id="credit">
		Documentation generated on Fri, 30 Jan 2004 16:37:29 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
	</p>
	</div></body>
</html>Structures_Graph-1.0.2/docs/html/Structures_Graph/_Structures_Graph_php.html100644   1750   1750       10416 10560475634  22674 <?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
		<head>
			<!-- template designed by Marco Von Ballmoos -->
			<title>Docs for page Graph.php</title>
			<link rel="stylesheet" href="../media/stylesheet.css" />
			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
		</head>
		<body>
			<div class="page-body">			
<h2 class="file-name">/Structures/Graph.php</h2>

<a name="sec-description"></a>
<div class="info-box">
	<div class="info-box-title">Description</div>
	<div class="nav-bar">
					<span class="disabled">Description</span> |
							<a href="#sec-classes">Classes</a>
			|							<a href="#sec-includes">Includes</a>
			|							<a href="#sec-constants">Constants</a>
										</div>
	<div class="info-box-body">	
		<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">The Graph.php file contains the definition of the Structures_Graph class</p>
	<ul class="tags">
				<li><span class="field">see:</span> <a href="../Structures_Graph/Structures_Graph.html">Structures_Graph</a></li>
			</ul>
		
			</div>
</div>
		
	<a name="sec-classes"></a>	
	<div class="info-box">
		<div class="info-box-title">Classes</div>
		<div class="nav-bar">
			<a href="#sec-description">Description</a> |
			<span class="disabled">Classes</span>
			|							<a href="#sec-includes">Includes</a>
				|										<a href="#sec-constants">Constants</a>
															</div>
		<div class="info-box-body">	
			<table cellpadding="2" cellspacing="0" class="class-table">
				<tr>
					<th class="class-table-header">Class</th>
					<th class="class-table-header">Description</th>
				</tr>
								<tr>
					<td style="padding-right: 2em; vertical-align: top">
						<a href="../Structures_Graph/Structures_Graph.html">Structures_Graph</a>
					</td>
					<td>
											The Structures_Graph class represents a graph data structure.
										</td>
				</tr>
							</table>
		</div>
	</div>

	<a name="sec-includes"></a>	
	<div class="info-box">
		<div class="info-box-title">Includes</div>
		<div class="nav-bar">
			<a href="#sec-description">Description</a> |
							<a href="#sec-classes">Classes</a>
				|						<span class="disabled">Includes</span>
			|							<a href="#sec-constants">Constants</a>
															</div>
		<div class="info-box-body">	
			<a name="_Structures/Graph/Node_php"><!-- --></a>
<div class="oddrow">
	
	<div>
		<span class="include-title">
			<span class="include-type">require_once</span>
			(<span class="include-name"><a href="../Structures_Graph/_Structures_Graph_Node_php.html">'Structures/Graph/Node.php'</a></span>)
			(line <span class="line-number">37</span>)
		</span>
	</div>

	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">Graph Node</p>
	
</div>
<a name="_PEAR_php"><!-- --></a>
<div class="evenrow">
	
	<div>
		<span class="include-title">
			<span class="include-type">require_once</span>
			(<span class="include-name">'PEAR.php'</span>)
			(line <span class="line-number">35</span>)
		</span>
	</div>

	<!-- ========== Info from phpDoc block ========= -->
<p class="short-description">PEAR base classes</p>
	
</div>
		</div>
	</div>
	
	<a name="sec-constants"></a>	
	<div class="info-box">
		<div class="info-box-title">Constants</div>
		<div class="nav-bar">
			<a href="#sec-description">Description</a> |
							<a href="#sec-classes">Classes</a>
				|										<a href="#sec-includes">Includes</a>
				|						<span class="disabled">Constants</span>
											</div>
		<div class="info-box-body">	
			<a name="defineSTRUCTURES_GRAPH_ERROR_GENERIC"><!-- --></a>
<div class="oddrow">
	
	<div>
		<span class="const-title">
			<span class="const-name">STRUCTURES_GRAPH_ERROR_GENERIC</span> = 100
			(line <span class="line-number">40</span>)
		</span>
	</div>
	
	<!-- ========== Info from phpDoc block ========= -->
	
		
</div>
		</div>
	</div>
	
	
	
	<p class="notes" id="credit">
		Documentation generated on Fri, 30 Jan 2004 16:37:28 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
	</p>
	</div></body>
</html>Structures_Graph-1.0.2/docs/html/classtrees_Structures_Graph.html100644   1750   1750        2563 10560475634  20576 <?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
		<head>
			<!-- template designed by Marco Von Ballmoos -->
			<title></title>
			<link rel="stylesheet" href="media/stylesheet.css" />
			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
		</head>
		<body>
						
<!-- Start of Class Data -->
<H2>
	
</H2>
<h2>Root class Structures_Graph</h2>
<ul>
<li><a href="Structures_Graph/Structures_Graph.html">Structures_Graph</a></li></ul>

<h2>Root class Structures_Graph_Manipulator_AcyclicTest</h2>
<ul>
<li><a href="Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html">Structures_Graph_Manipulator_AcyclicTest</a></li></ul>

<h2>Root class Structures_Graph_Manipulator_TopologicalSorter</h2>
<ul>
<li><a href="Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.html">Structures_Graph_Manipulator_TopologicalSorter</a></li></ul>

<h2>Root class Structures_Graph_Node</h2>
<ul>
<li><a href="Structures_Graph/Structures_Graph_Node.html">Structures_Graph_Node</a></li></ul>

	<p class="notes" id="credit">
		Documentation generated on Fri, 30 Jan 2004 16:37:28 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
	</p>
	</body>
</html>Structures_Graph-1.0.2/docs/html/elementindex.html100644   1750   1750       37131 10560475634  15542 <?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
		<head>
			<!-- template designed by Marco Von Ballmoos -->
			<title></title>
			<link rel="stylesheet" href="media/stylesheet.css" />
			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
		</head>
		<body>
						<a name="top"></a>
<h2>Full index</h2>
<h3>Package indexes</h3>
<ul>
	<li><a href="elementindex_Structures_Graph.html">Structures_Graph</a></li>
</ul>
<br />
<div class="index-letter-menu">
	<a class="index-letter" href="elementindex.html#a">a</a>
	<a class="index-letter" href="elementindex.html#c">c</a>
	<a class="index-letter" href="elementindex.html#g">g</a>
	<a class="index-letter" href="elementindex.html#i">i</a>
	<a class="index-letter" href="elementindex.html#m">m</a>
	<a class="index-letter" href="elementindex.html#n">n</a>
	<a class="index-letter" href="elementindex.html#o">o</a>
	<a class="index-letter" href="elementindex.html#r">r</a>
	<a class="index-letter" href="elementindex.html#s">s</a>
	<a class="index-letter" href="elementindex.html#t">t</a>
	<a class="index-letter" href="elementindex.html#u">u</a>
</div>

	<a name="a"></a>
	<div class="index-letter-section">
		<div style="float: left" class="index-letter-title">a</div>
		<div style="float: right"><a href="#top">top</a></div>
		<div style="clear: both"></div>
	</div>
	<dl>
			<dt class="field">
						<span class="method-title">addNode</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html#methodaddNode">Structures_Graph::addNode()</a> in Graph.php</div>
							<div class="index-item-description">Add a Node to the Graph</div>
					</dd>
			<dt class="field">
						<span class="include-title">AcyclicTest.php</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/_Structures_Graph_Manipulator_AcyclicTest_php.html">AcyclicTest.php</a> in AcyclicTest.php</div>
					</dd>
		</dl>
	<a name="c"></a>
	<div class="index-letter-section">
		<div style="float: left" class="index-letter-title">c</div>
		<div style="float: right"><a href="#top">top</a></div>
		<div style="clear: both"></div>
	</div>
	<dl>
			<dt class="field">
						<span class="method-title">connectsTo</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodconnectsTo">Structures_Graph_Node::connectsTo()</a> in Node.php</div>
							<div class="index-item-description">Test wether this node has an arc to the target node</div>
					</dd>
			<dt class="field">
						<span class="method-title">connectTo</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodconnectTo">Structures_Graph_Node::connectTo()</a> in Node.php</div>
							<div class="index-item-description">Connect this node to another one.</div>
					</dd>
		</dl>
	<a name="g"></a>
	<div class="index-letter-section">
		<div style="float: left" class="index-letter-title">g</div>
		<div style="float: right"><a href="#top">top</a></div>
		<div style="clear: both"></div>
	</div>
	<dl>
			<dt class="field">
						<span class="method-title">getData</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodgetData">Structures_Graph_Node::getData()</a> in Node.php</div>
							<div class="index-item-description">Node data getter.</div>
					</dd>
			<dt class="field">
						<span class="method-title">getGraph</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodgetGraph">Structures_Graph_Node::getGraph()</a> in Node.php</div>
							<div class="index-item-description">Node graph getter</div>
					</dd>
			<dt class="field">
						<span class="method-title">getMetadata</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodgetMetadata">Structures_Graph_Node::getMetadata()</a> in Node.php</div>
							<div class="index-item-description">Node metadata getter</div>
					</dd>
			<dt class="field">
						<span class="method-title">getNeighbours</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodgetNeighbours">Structures_Graph_Node::getNeighbours()</a> in Node.php</div>
							<div class="index-item-description">Return nodes connected to this one.</div>
					</dd>
			<dt class="field">
						<span class="method-title">getNodes</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html#methodgetNodes">Structures_Graph::getNodes()</a> in Graph.php</div>
							<div class="index-item-description">Return the node set, in no particular order. For ordered node sets, use a Graph Manipulator insted.</div>
					</dd>
			<dt class="field">
						<span class="include-title">Graph.php</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/_Structures_Graph_php.html">Graph.php</a> in Graph.php</div>
					</dd>
		</dl>
	<a name="i"></a>
	<div class="index-letter-section">
		<div style="float: left" class="index-letter-title">i</div>
		<div style="float: right"><a href="#top">top</a></div>
		<div style="clear: both"></div>
	</div>
	<dl>
			<dt class="field">
						<span class="method-title">inDegree</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodinDegree">Structures_Graph_Node::inDegree()</a> in Node.php</div>
							<div class="index-item-description">Calculate the in degree of the node.</div>
					</dd>
			<dt class="field">
						<span class="method-title">isAcyclic</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html#methodisAcyclic">Structures_Graph_Manipulator_AcyclicTest::isAcyclic()</a> in AcyclicTest.php</div>
							<div class="index-item-description">isAcyclic returns true if a graph contains no cycles, false otherwise.</div>
					</dd>
			<dt class="field">
						<span class="method-title">isDirected</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html#methodisDirected">Structures_Graph::isDirected()</a> in Graph.php</div>
							<div class="index-item-description">Return true if a graph is directed</div>
					</dd>
		</dl>
	<a name="m"></a>
	<div class="index-letter-section">
		<div style="float: left" class="index-letter-title">m</div>
		<div style="float: right"><a href="#top">top</a></div>
		<div style="clear: both"></div>
	</div>
	<dl>
			<dt class="field">
						<span class="method-title">metadataKeyExists</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodmetadataKeyExists">Structures_Graph_Node::metadataKeyExists()</a> in Node.php</div>
							<div class="index-item-description">Test for existence of metadata under a given key.</div>
					</dd>
		</dl>
	<a name="n"></a>
	<div class="index-letter-section">
		<div style="float: left" class="index-letter-title">n</div>
		<div style="float: right"><a href="#top">top</a></div>
		<div style="clear: both"></div>
	</div>
	<dl>
			<dt class="field">
						<span class="include-title">Node.php</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/_Structures_Graph_Node_php.html">Node.php</a> in Node.php</div>
					</dd>
		</dl>
	<a name="o"></a>
	<div class="index-letter-section">
		<div style="float: left" class="index-letter-title">o</div>
		<div style="float: right"><a href="#top">top</a></div>
		<div style="clear: both"></div>
	</div>
	<dl>
			<dt class="field">
						<span class="method-title">outDegree</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodoutDegree">Structures_Graph_Node::outDegree()</a> in Node.php</div>
							<div class="index-item-description">Calculate the out degree of the node.</div>
					</dd>
		</dl>
	<a name="r"></a>
	<div class="index-letter-section">
		<div style="float: left" class="index-letter-title">r</div>
		<div style="float: right"><a href="#top">top</a></div>
		<div style="clear: both"></div>
	</div>
	<dl>
			<dt class="field">
						<span class="method-title">removeNode</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html#methodremoveNode">Structures_Graph::removeNode()</a> in Graph.php</div>
							<div class="index-item-description">Remove a Node from the Graph</div>
					</dd>
		</dl>
	<a name="s"></a>
	<div class="index-letter-section">
		<div style="float: left" class="index-letter-title">s</div>
		<div style="float: right"><a href="#top">top</a></div>
		<div style="clear: both"></div>
	</div>
	<dl>
			<dt class="field">
						<span class="method-title">setData</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodsetData">Structures_Graph_Node::setData()</a> in Node.php</div>
							<div class="index-item-description">Node data setter</div>
					</dd>
			<dt class="field">
						<span class="method-title">setGraph</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodsetGraph">Structures_Graph_Node::setGraph()</a> in Node.php</div>
							<div class="index-item-description">Node graph setter. This method should not be called directly. Use Graph::addNode instead.</div>
					</dd>
			<dt class="field">
						<span class="method-title">setMetadata</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodsetMetadata">Structures_Graph_Node::setMetadata()</a> in Node.php</div>
							<div class="index-item-description">Node metadata setter</div>
					</dd>
			<dt class="field">
						<span class="method-title">sort</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.html#methodsort">Structures_Graph_Manipulator_TopologicalSorter::sort()</a> in TopologicalSorter.php</div>
							<div class="index-item-description">sort returns the graph's nodes, sorted by topological order.</div>
					</dd>
			<dt class="field">
						Structures_Graph
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html">Structures_Graph</a> in Graph.php</div>
							<div class="index-item-description">The Structures_Graph class represents a graph data structure.</div>
					</dd>
			<dt class="field">
						<span class="method-title">Structures_Graph</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html#methodStructures_Graph">Structures_Graph::Structures_Graph()</a> in Graph.php</div>
							<div class="index-item-description">Constructor</div>
					</dd>
			<dt class="field">
						<span class="const-title">STRUCTURES_GRAPH_ERROR_GENERIC</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/_Structures_Graph_php.html#defineSTRUCTURES_GRAPH_ERROR_GENERIC">STRUCTURES_GRAPH_ERROR_GENERIC</a> in Graph.php</div>
					</dd>
			<dt class="field">
						Structures_Graph_Manipulator_AcyclicTest
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html">Structures_Graph_Manipulator_AcyclicTest</a> in AcyclicTest.php</div>
							<div class="index-item-description">The Structures_Graph_Manipulator_AcyclicTest is a graph manipulator  which tests whether a graph contains a cycle.</div>
					</dd>
			<dt class="field">
						Structures_Graph_Manipulator_TopologicalSorter
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.html">Structures_Graph_Manipulator_TopologicalSorter</a> in TopologicalSorter.php</div>
							<div class="index-item-description">The Structures_Graph_Manipulator_TopologicalSorter is a manipulator  which is able to return the set of nodes in a graph, sorted by topological  order.</div>
					</dd>
			<dt class="field">
						<span class="method-title">Structures_Graph_Node</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodStructures_Graph_Node">Structures_Graph_Node::Structures_Graph_Node()</a> in Node.php</div>
							<div class="index-item-description">Constructor</div>
					</dd>
			<dt class="field">
						Structures_Graph_Node
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html">Structures_Graph_Node</a> in Node.php</div>
							<div class="index-item-description">The Structures_Graph_Node class represents a Node that can be member of a  graph node set.</div>
					</dd>
		</dl>
	<a name="t"></a>
	<div class="index-letter-section">
		<div style="float: left" class="index-letter-title">t</div>
		<div style="float: right"><a href="#top">top</a></div>
		<div style="clear: both"></div>
	</div>
	<dl>
			<dt class="field">
						<span class="include-title">TopologicalSorter.php</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/_Structures_Graph_Manipulator_TopologicalSorter_php.html">TopologicalSorter.php</a> in TopologicalSorter.php</div>
					</dd>
		</dl>
	<a name="u"></a>
	<div class="index-letter-section">
		<div style="float: left" class="index-letter-title">u</div>
		<div style="float: right"><a href="#top">top</a></div>
		<div style="clear: both"></div>
	</div>
	<dl>
			<dt class="field">
						<span class="method-title">unsetMetadata</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodunsetMetadata">Structures_Graph_Node::unsetMetadata()</a> in Node.php</div>
							<div class="index-item-description">Delete metadata by key</div>
					</dd>
		</dl>

<div class="index-letter-menu">
	<a class="index-letter" href="elementindex.html#a">a</a>
	<a class="index-letter" href="elementindex.html#c">c</a>
	<a class="index-letter" href="elementindex.html#g">g</a>
	<a class="index-letter" href="elementindex.html#i">i</a>
	<a class="index-letter" href="elementindex.html#m">m</a>
	<a class="index-letter" href="elementindex.html#n">n</a>
	<a class="index-letter" href="elementindex.html#o">o</a>
	<a class="index-letter" href="elementindex.html#r">r</a>
	<a class="index-letter" href="elementindex.html#s">s</a>
	<a class="index-letter" href="elementindex.html#t">t</a>
	<a class="index-letter" href="elementindex.html#u">u</a>
</div>	</body>
</html>Structures_Graph-1.0.2/docs/html/elementindex_Structures_Graph.html100644   1750   1750       37637 10560475634  21141 <?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
		<head>
			<!-- template designed by Marco Von Ballmoos -->
			<title></title>
			<link rel="stylesheet" href="media/stylesheet.css" />
			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
		</head>
		<body>
						<a name="top"></a>
<h2>[Structures_Graph] element index</h2>
<a href="elementindex.html">All elements</a>
<br />
<div class="index-letter-menu">
	<a class="index-letter" href="elementindex_Structures_Graph.html#a">a</a>
	<a class="index-letter" href="elementindex_Structures_Graph.html#c">c</a>
	<a class="index-letter" href="elementindex_Structures_Graph.html#g">g</a>
	<a class="index-letter" href="elementindex_Structures_Graph.html#i">i</a>
	<a class="index-letter" href="elementindex_Structures_Graph.html#m">m</a>
	<a class="index-letter" href="elementindex_Structures_Graph.html#n">n</a>
	<a class="index-letter" href="elementindex_Structures_Graph.html#o">o</a>
	<a class="index-letter" href="elementindex_Structures_Graph.html#r">r</a>
	<a class="index-letter" href="elementindex_Structures_Graph.html#s">s</a>
	<a class="index-letter" href="elementindex_Structures_Graph.html#t">t</a>
	<a class="index-letter" href="elementindex_Structures_Graph.html#u">u</a>
</div>

	<a name="a"></a>
	<div class="index-letter-section">
		<div style="float: left" class="index-letter-title">a</div>
		<div style="float: right"><a href="#top">top</a></div>
		<div style="clear: both"></div>
	</div>
	<dl>
			<dt class="field">
						<span class="method-title">addNode</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html#methodaddNode">Structures_Graph::addNode()</a> in Graph.php</div>
							<div class="index-item-description">Add a Node to the Graph</div>
					</dd>
			<dt class="field">
						<span class="include-title">AcyclicTest.php</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/_Structures_Graph_Manipulator_AcyclicTest_php.html">AcyclicTest.php</a> in AcyclicTest.php</div>
					</dd>
		</dl>
	<a name="c"></a>
	<div class="index-letter-section">
		<div style="float: left" class="index-letter-title">c</div>
		<div style="float: right"><a href="#top">top</a></div>
		<div style="clear: both"></div>
	</div>
	<dl>
			<dt class="field">
						<span class="method-title">connectsTo</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodconnectsTo">Structures_Graph_Node::connectsTo()</a> in Node.php</div>
							<div class="index-item-description">Test wether this node has an arc to the target node</div>
					</dd>
			<dt class="field">
						<span class="method-title">connectTo</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodconnectTo">Structures_Graph_Node::connectTo()</a> in Node.php</div>
							<div class="index-item-description">Connect this node to another one.</div>
					</dd>
		</dl>
	<a name="g"></a>
	<div class="index-letter-section">
		<div style="float: left" class="index-letter-title">g</div>
		<div style="float: right"><a href="#top">top</a></div>
		<div style="clear: both"></div>
	</div>
	<dl>
			<dt class="field">
						<span class="method-title">getData</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodgetData">Structures_Graph_Node::getData()</a> in Node.php</div>
							<div class="index-item-description">Node data getter.</div>
					</dd>
			<dt class="field">
						<span class="method-title">getGraph</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodgetGraph">Structures_Graph_Node::getGraph()</a> in Node.php</div>
							<div class="index-item-description">Node graph getter</div>
					</dd>
			<dt class="field">
						<span class="method-title">getMetadata</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodgetMetadata">Structures_Graph_Node::getMetadata()</a> in Node.php</div>
							<div class="index-item-description">Node metadata getter</div>
					</dd>
			<dt class="field">
						<span class="method-title">getNeighbours</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodgetNeighbours">Structures_Graph_Node::getNeighbours()</a> in Node.php</div>
							<div class="index-item-description">Return nodes connected to this one.</div>
					</dd>
			<dt class="field">
						<span class="method-title">getNodes</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html#methodgetNodes">Structures_Graph::getNodes()</a> in Graph.php</div>
							<div class="index-item-description">Return the node set, in no particular order. For ordered node sets, use a Graph Manipulator insted.</div>
					</dd>
			<dt class="field">
						<span class="include-title">Graph.php</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/_Structures_Graph_php.html">Graph.php</a> in Graph.php</div>
					</dd>
		</dl>
	<a name="i"></a>
	<div class="index-letter-section">
		<div style="float: left" class="index-letter-title">i</div>
		<div style="float: right"><a href="#top">top</a></div>
		<div style="clear: both"></div>
	</div>
	<dl>
			<dt class="field">
						<span class="method-title">inDegree</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodinDegree">Structures_Graph_Node::inDegree()</a> in Node.php</div>
							<div class="index-item-description">Calculate the in degree of the node.</div>
					</dd>
			<dt class="field">
						<span class="method-title">isAcyclic</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html#methodisAcyclic">Structures_Graph_Manipulator_AcyclicTest::isAcyclic()</a> in AcyclicTest.php</div>
							<div class="index-item-description">isAcyclic returns true if a graph contains no cycles, false otherwise.</div>
					</dd>
			<dt class="field">
						<span class="method-title">isDirected</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html#methodisDirected">Structures_Graph::isDirected()</a> in Graph.php</div>
							<div class="index-item-description">Return true if a graph is directed</div>
					</dd>
		</dl>
	<a name="m"></a>
	<div class="index-letter-section">
		<div style="float: left" class="index-letter-title">m</div>
		<div style="float: right"><a href="#top">top</a></div>
		<div style="clear: both"></div>
	</div>
	<dl>
			<dt class="field">
						<span class="method-title">metadataKeyExists</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodmetadataKeyExists">Structures_Graph_Node::metadataKeyExists()</a> in Node.php</div>
							<div class="index-item-description">Test for existence of metadata under a given key.</div>
					</dd>
		</dl>
	<a name="n"></a>
	<div class="index-letter-section">
		<div style="float: left" class="index-letter-title">n</div>
		<div style="float: right"><a href="#top">top</a></div>
		<div style="clear: both"></div>
	</div>
	<dl>
			<dt class="field">
						<span class="include-title">Node.php</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/_Structures_Graph_Node_php.html">Node.php</a> in Node.php</div>
					</dd>
		</dl>
	<a name="o"></a>
	<div class="index-letter-section">
		<div style="float: left" class="index-letter-title">o</div>
		<div style="float: right"><a href="#top">top</a></div>
		<div style="clear: both"></div>
	</div>
	<dl>
			<dt class="field">
						<span class="method-title">outDegree</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodoutDegree">Structures_Graph_Node::outDegree()</a> in Node.php</div>
							<div class="index-item-description">Calculate the out degree of the node.</div>
					</dd>
		</dl>
	<a name="r"></a>
	<div class="index-letter-section">
		<div style="float: left" class="index-letter-title">r</div>
		<div style="float: right"><a href="#top">top</a></div>
		<div style="clear: both"></div>
	</div>
	<dl>
			<dt class="field">
						<span class="method-title">removeNode</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html#methodremoveNode">Structures_Graph::removeNode()</a> in Graph.php</div>
							<div class="index-item-description">Remove a Node from the Graph</div>
					</dd>
		</dl>
	<a name="s"></a>
	<div class="index-letter-section">
		<div style="float: left" class="index-letter-title">s</div>
		<div style="float: right"><a href="#top">top</a></div>
		<div style="clear: both"></div>
	</div>
	<dl>
			<dt class="field">
						<span class="method-title">setData</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodsetData">Structures_Graph_Node::setData()</a> in Node.php</div>
							<div class="index-item-description">Node data setter</div>
					</dd>
			<dt class="field">
						<span class="method-title">setGraph</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodsetGraph">Structures_Graph_Node::setGraph()</a> in Node.php</div>
							<div class="index-item-description">Node graph setter. This method should not be called directly. Use Graph::addNode instead.</div>
					</dd>
			<dt class="field">
						<span class="method-title">setMetadata</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodsetMetadata">Structures_Graph_Node::setMetadata()</a> in Node.php</div>
							<div class="index-item-description">Node metadata setter</div>
					</dd>
			<dt class="field">
						<span class="method-title">sort</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.html#methodsort">Structures_Graph_Manipulator_TopologicalSorter::sort()</a> in TopologicalSorter.php</div>
							<div class="index-item-description">sort returns the graph's nodes, sorted by topological order.</div>
					</dd>
			<dt class="field">
						Structures_Graph
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html">Structures_Graph</a> in Graph.php</div>
							<div class="index-item-description">The Structures_Graph class represents a graph data structure.</div>
					</dd>
			<dt class="field">
						<span class="method-title">Structures_Graph</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html#methodStructures_Graph">Structures_Graph::Structures_Graph()</a> in Graph.php</div>
							<div class="index-item-description">Constructor</div>
					</dd>
			<dt class="field">
						<span class="const-title">STRUCTURES_GRAPH_ERROR_GENERIC</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/_Structures_Graph_php.html#defineSTRUCTURES_GRAPH_ERROR_GENERIC">STRUCTURES_GRAPH_ERROR_GENERIC</a> in Graph.php</div>
					</dd>
			<dt class="field">
						Structures_Graph_Manipulator_AcyclicTest
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html">Structures_Graph_Manipulator_AcyclicTest</a> in AcyclicTest.php</div>
							<div class="index-item-description">The Structures_Graph_Manipulator_AcyclicTest is a graph manipulator  which tests whether a graph contains a cycle.</div>
					</dd>
			<dt class="field">
						Structures_Graph_Manipulator_TopologicalSorter
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.html">Structures_Graph_Manipulator_TopologicalSorter</a> in TopologicalSorter.php</div>
							<div class="index-item-description">The Structures_Graph_Manipulator_TopologicalSorter is a manipulator  which is able to return the set of nodes in a graph, sorted by topological  order.</div>
					</dd>
			<dt class="field">
						<span class="method-title">Structures_Graph_Node</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodStructures_Graph_Node">Structures_Graph_Node::Structures_Graph_Node()</a> in Node.php</div>
							<div class="index-item-description">Constructor</div>
					</dd>
			<dt class="field">
						Structures_Graph_Node
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html">Structures_Graph_Node</a> in Node.php</div>
							<div class="index-item-description">The Structures_Graph_Node class represents a Node that can be member of a  graph node set.</div>
					</dd>
		</dl>
	<a name="t"></a>
	<div class="index-letter-section">
		<div style="float: left" class="index-letter-title">t</div>
		<div style="float: right"><a href="#top">top</a></div>
		<div style="clear: both"></div>
	</div>
	<dl>
			<dt class="field">
						<span class="include-title">TopologicalSorter.php</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/_Structures_Graph_Manipulator_TopologicalSorter_php.html">TopologicalSorter.php</a> in TopologicalSorter.php</div>
					</dd>
		</dl>
	<a name="u"></a>
	<div class="index-letter-section">
		<div style="float: left" class="index-letter-title">u</div>
		<div style="float: right"><a href="#top">top</a></div>
		<div style="clear: both"></div>
	</div>
	<dl>
			<dt class="field">
						<span class="method-title">unsetMetadata</span>
					</dt>
		<dd class="index-item-body">
			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodunsetMetadata">Structures_Graph_Node::unsetMetadata()</a> in Node.php</div>
							<div class="index-item-description">Delete metadata by key</div>
					</dd>
		</dl>

<div class="index-letter-menu">
	<a class="index-letter" href="elementindex_Structures_Graph.html#a">a</a>
	<a class="index-letter" href="elementindex_Structures_Graph.html#c">c</a>
	<a class="index-letter" href="elementindex_Structures_Graph.html#g">g</a>
	<a class="index-letter" href="elementindex_Structures_Graph.html#i">i</a>
	<a class="index-letter" href="elementindex_Structures_Graph.html#m">m</a>
	<a class="index-letter" href="elementindex_Structures_Graph.html#n">n</a>
	<a class="index-letter" href="elementindex_Structures_Graph.html#o">o</a>
	<a class="index-letter" href="elementindex_Structures_Graph.html#r">r</a>
	<a class="index-letter" href="elementindex_Structures_Graph.html#s">s</a>
	<a class="index-letter" href="elementindex_Structures_Graph.html#t">t</a>
	<a class="index-letter" href="elementindex_Structures_Graph.html#u">u</a>
</div>	</body>
</html>Structures_Graph-1.0.2/docs/html/errors.html100644   1750   1750        1344 10560475634  14352 <?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
		<head>
			<!-- template designed by Marco Von Ballmoos -->
			<title>phpDocumentor Parser Errors and Warnings</title>
			<link rel="stylesheet" href="media/stylesheet.css" />
			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
		</head>
		<body>
						<a href="#Post-parsing">Post-parsing</a><br>
	<p class="notes" id="credit">
		Documentation generated on Fri, 30 Jan 2004 16:37:29 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
	</p>
	</body>
</html>Structures_Graph-1.0.2/docs/html/index.html100644   1750   1750        2010 10560475634  14134 <?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html 
     PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//FR"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
   <html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<!-- Generated by phpDocumentor on Fri, 30 Jan 2004 16:37:28 +0000  -->
  <title>Structures_Graph Documentation</title>
  <meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
</head>

<FRAMESET rows='100,*'>
	<FRAME src='packages.html' name='left_top' frameborder="1" bordercolor="#999999">
	<FRAMESET cols='25%,*'>
		<FRAME src='li_Structures_Graph.html' name='left_bottom' frameborder="1" bordercolor="#999999">
		<FRAME src='Structures_Graph/tutorial_Structures_Graph.pkg.html' name='right' frameborder="1" bordercolor="#999999">
	</FRAMESET>
	<NOFRAMES>
		<H2>Frame Alert</H2>
		<P>This document is designed to be viewed using the frames feature.
		If you see this message, you are using a non-frame-capable web client.</P>
	</NOFRAMES>
</FRAMESET>
</HTML>Structures_Graph-1.0.2/docs/html/li_Structures_Graph.html100644   1750   1750        5044 10560475634  17027 <?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
		<head>
			<!-- template designed by Marco Von Ballmoos -->
			<title></title>
			<link rel="stylesheet" href="media/stylesheet.css" />
			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
		</head>
		<body>
						<div class="package-title">Structures_Graph</div>
<div class="package-details">

	<dl class="tree">
		
		<dt class="folder-title">Description</dt>
		<dd>
			<a href='classtrees_Structures_Graph.html' target='right'>Class trees</a><br />
			<a href='elementindex_Structures_Graph.html' target='right'>Index of elements</a><br />
							<a href="todolist.html" target="right">Todo List</a><br />
					</dd>
	
							
							
									<dt class="folder-title">Tutorials/Manuals</dt>
					<dd>
											<dl class="tree">
						<dt class="folder-title">Package-level</dt>
						<dd>
													<div><a href="Structures_Graph/tutorial_Structures_Graph.pkg.html" target="right">Structures_Graph Tutorial</a></div>

												</dd>
						</dl>
										
										
										</dd>
													<dt class="folder-title">Classes</dt>
											<dd><a href='Structures_Graph/Structures_Graph.html' target='right'>Structures_Graph</a></dd>
											<dd><a href='Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html' target='right'>Structures_Graph_Manipulator_AcyclicTest</a></dd>
											<dd><a href='Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.html' target='right'>Structures_Graph_Manipulator_TopologicalSorter</a></dd>
											<dd><a href='Structures_Graph/Structures_Graph_Node.html' target='right'>Structures_Graph_Node</a></dd>
																						<dt class="folder-title">Files</dt>
											<dd><a href='Structures_Graph/_Structures_Graph_Manipulator_AcyclicTest_php.html' target='right'>AcyclicTest.php</a></dd>
											<dd><a href='Structures_Graph/_Structures_Graph_php.html' target='right'>Graph.php</a></dd>
											<dd><a href='Structures_Graph/_Structures_Graph_Node_php.html' target='right'>Node.php</a></dd>
											<dd><a href='Structures_Graph/_Structures_Graph_Manipulator_TopologicalSorter_php.html' target='right'>TopologicalSorter.php</a></dd>
																	
						
			</dl>
</div>
<p class="notes"><a href="http://www.phpdoc.org" target="_blank">phpDocumentor v <span class="field">1.2.3</span></a></p>
</BODY>
</HTML>Structures_Graph-1.0.2/docs/html/packages.html100644   1750   1750        1673 10560475634  14621 <?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
		<head>
			<!-- template designed by Marco Von Ballmoos -->
			<title></title>
			<link rel="stylesheet" href="media/stylesheet.css" />
			<link rel="stylesheet" href="media/banner.css" />
			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
		</head>
		<body>
			<div class="banner">
				<div class="banner-title">Structures_Graph</div>
				<div class="banner-menu">
	        <table cellpadding="0" cellspacing="0" style="width: 100%">
	          <tr>
              <td>
								              </td>
              <td style="width: 2em">&nbsp;</td>
              <td style="text-align: right">
								              </td>
						</tr>
          </table>
				</div>
			</div>
		</body>
	</html>Structures_Graph-1.0.2/docs/html/todolist.html100644   1750   1750        1600 10560475634  14672 <?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
		<head>
			<!-- template designed by Marco Von Ballmoos -->
			<title>Todo List</title>
			<link rel="stylesheet" href="media/stylesheet.css" />
			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
		</head>
		<body>
						<div align="center"><h1>Todo List</h1></div>
<h2>Structures_Graph</h2>
<h3><a href="Structures_Graph/Structures_Graph.html#methodremoveNode">Structures_Graph::removeNode()</a></h3>
<ul>
    <li>This is unimplemented</li>
</ul>
	<p class="notes" id="credit">
		Documentation generated on Fri, 30 Jan 2004 16:37:29 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
	</p>
	</body>
</html>Structures_Graph-1.0.2/docs/tutorials/Structures_Graph/Structures_Graph.pkg100644   1750   1750        7714 10560475634  22554 <refentry id="{@id package.database.structures_graph.tutorial}">
 <refnamediv>
  <refname><classname>Structures_Graph</classname> Tutorial</refname>
  <refpurpose>A first tour of graph datastructure manipulation</refpurpose>
 </refnamediv>
 <refsect1 id="{@id package.database.structures_graph.tutorial.intro}">
  <title>Introduction</title>
  <para>
  Structures_Graph is a package for creating and manipulating graph datastructures. A graph is a set of objects, called nodes, connected by arcs. When used as a datastructure, usually nodes contain data, and arcs represent relationships between nodes. When arcs have a direction, and can be travelled only one way, graphs are said to be directed. When arcs have no direction, and can always be travelled both ways, graphs are said to be non directed.
  </para>
  <para>
  Structures_Graph provides an object oriented API to create and directly query a graph, as well as a set of Manipulator classes to extract information from the graph.
  </para>
 </refsect1>
 <refsect1 id="{@id package.database.structures_graph.tutorial.creation}">
  <title>Creating a Graph</title>
  <para>
   Creating a graph is done using the simple constructor:
   <programlisting>
    <![CDATA[
require_once 'Structures/Graph.php';

$directedGraph =& new Structures_Graph(true);
$nonDirectedGraph =& new Structures_Graph(false);
    ]]>
   </programlisting>
   and passing the constructor a flag telling it whether the graph should be directed. A directed graph will always be directed during its lifetime. It's a permanent characteristic.
  </para>
  <para>
  To fill out the graph, we'll need to create some nodes, and then call Graph::addNode.
   <programlisting>
    <![CDATA[
require_once 'Structures/Graph/Node.php';

$nodeOne =& new Structures_Graph_Node();
$nodeTwo =& new Structures_Graph_Node();
$nodeThree =& new Structures_Graph_Node();

$directedGraph->addNode(&$nodeOne);
$directedGraph->addNode(&$nodeTwo);
$directedGraph->addNode(&$nodeThree);
    ]]>
   </programlisting>
   and then setup the arcs:
   <programlisting>
    <![CDATA[
$nodeOne->connectTo($nodeTwo);
$nodeOne->connectTo($nodeThree);
    ]]>
   </programlisting>
   Note that arcs can only be created after the nodes have been inserted into the graph. 
  </para>
 </refsect1>
 <refsect1 id="{@id package.database.structures_graph.tutorial.nodesanddata}">
  <title>Associating Data</title>
  <para>
  Graphs are only useful as datastructures if they can hold data. Structure_Graph stores data in nodes. Each node contains a setter and a getter for its data.
   <programlisting>
    <![CDATA[
$nodeOne->setData("Node One's Data is a String");
$nodeTwo->setData(1976);
$nodeThree->setData('Some other string');

print("NodeTwo's Data is an integer: " . $nodeTwo->getData());
    ]]>
   </programlisting>
  </para>
  <para>
  Structure_Graph nodes can also store metadata, alongside with the main data. Metadata differs from regular data just because it is stored under a key, making it possible to store more than one data reference per node. The metadata getter and setter need the key to perform the operation:
   <programlisting>
    <![CDATA[
$nodeOne->setMetadata('example key', "Node One's Sample Metadata");
print("Metadata stored under key 'example key' in node one: " . $nodeOne->getMetadata('example key'));
$nodeOne->unsetMetadata('example key');
    ]]>
   </programlisting>
  </para>
 </refsect1>
 <refsect1 id="{@id package.database.structures_graph.tutorial.querying}">
  <title>Querying a Graph</title>
  <para>
  Structures_Graph provides for basic querying of the graph:
   <programlisting>
    <![CDATA[
// Nodes are able to calculate their indegree and outdegree
print("NodeOne's inDegree: " . $nodeOne->inDegree());
print("NodeOne's outDegree: " . $nodeOne->outDegree());

// and naturally, nodes can report on their arcs
$arcs = $nodeOne->getNeighbours();
for ($i=0;$i<sizeof($arcs);$i++) {
    print("NodeOne has an arc to " . $arcs[$i]->getData());
}
    ]]>
   </programlisting>
  </para>
 </refsect1>
</refentry>
Structures_Graph-1.0.2/docs/generate.sh100644   1750   1750         555 10560475634  13315 #!/bin/sh
(cd ..; tar czf docs/arch.tgz "{arch}")
rm -Rf "../{arch}"
rm -Rf ./html
mkdir -p ./html
phpdoc --directory ../Structures,./tutorials --target ./html --title "Structures_Graph Documentation" --output "HTML:frames" --defaultpackagename structures_graph --defaultcategoryname structures --pear 
(cd ..; tar --absolute-names -xzf docs/arch.tgz)
#rm arch.tgz
Structures_Graph-1.0.2/Structures/Graph/Manipulator/AcyclicTest.php100644   1750   1750       13160 10560475634  20732 <?php
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
// +-----------------------------------------------------------------------------+
// | Copyright (c) 2003 Srgio Gonalves Carvalho                                |
// +-----------------------------------------------------------------------------+
// | This file is part of Structures_Graph.                                      |
// |                                                                             |
// | Structures_Graph is free software; you can redistribute it and/or modify    |
// | it under the terms of the GNU Lesser General Public License as published by |
// | the Free Software Foundation; either version 2.1 of the License, or         |
// | (at your option) any later version.                                         |
// |                                                                             |
// | Structures_Graph is distributed in the hope that it will be useful,         |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
// | GNU Lesser General Public License for more details.                         |
// |                                                                             |
// | You should have received a copy of the GNU Lesser General Public License    |
// | along with Structures_Graph; if not, write to the Free Software             |
// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA                    |
// | 02111-1307 USA                                                              |
// +-----------------------------------------------------------------------------+
// | Author: Srgio Carvalho <sergio.carvalho@portugalmail.com>                  |
// +-----------------------------------------------------------------------------+
//
/**
 * This file contains the definition of the Structures_Graph_Manipulator_AcyclicTest graph manipulator.
 * 
 * @see Structures_Graph_Manipulator_AcyclicTest
 * @package Structures_Graph
 */

/* dependencies {{{ */
/** */
require_once 'PEAR.php';
/** */
require_once 'Structures/Graph.php';
/** */
require_once 'Structures/Graph/Node.php';
/* }}} */

/* class Structures_Graph_Manipulator_AcyclicTest {{{ */
/**
 * The Structures_Graph_Manipulator_AcyclicTest is a graph manipulator
 * which tests whether a graph contains a cycle. 
 * 
 * The definition of an acyclic graph used in this manipulator is that of a 
 * DAG. The graph must be directed, or else it is considered cyclic, even when 
 * there are no arcs.
 *
 * @author		Srgio Carvalho <sergio.carvalho@portugalmail.com> 
 * @copyright	(c) 2004 by Srgio Carvalho
 * @package Structures_Graph
 */
class Structures_Graph_Manipulator_AcyclicTest {
    /* _nonVisitedInDegree {{{ */
    /**
    *
    * This is a variant of Structures_Graph::inDegree which does 
    * not count nodes marked as visited.
    *
    * @access   private
    * @return	integer	 Number of non-visited nodes that link to this one
    */
    function _nonVisitedInDegree(&$node) {
        $result = 0;
        $graphNodes =& $node->_graph->getNodes();
        foreach (array_keys($graphNodes) as $key) {
            if ((!$graphNodes[$key]->getMetadata('acyclic-test-visited')) && $graphNodes[$key]->connectsTo($node)) $result++;
        }
        return $result;
        
    }
    /* }}} */

    /* _isAcyclic {{{ */
    /**
    * @access   private
    */
    function _isAcyclic(&$graph) {
        // Mark every node as not visited
        $nodes =& $graph->getNodes();
        $nodeKeys = array_keys($nodes);
        $refGenerator = array();
        foreach($nodeKeys as $key) {
            $refGenerator[] = false;
            $nodes[$key]->setMetadata('acyclic-test-visited', $refGenerator[sizeof($refGenerator) - 1]);
        }

        // Iteratively peel off leaf nodes
        do {
            // Find out which nodes are leafs (excluding visited nodes)
            $leafNodes = array();
            foreach($nodeKeys as $key) {
                if ((!$nodes[$key]->getMetadata('acyclic-test-visited')) && Structures_Graph_Manipulator_AcyclicTest::_nonVisitedInDegree($nodes[$key]) == 0) {
                    $leafNodes[] =& $nodes[$key];
                }
            }
            // Mark leafs as visited
            for ($i=sizeof($leafNodes) - 1; $i>=0; $i--) {
                $visited =& $leafNodes[$i]->getMetadata('acyclic-test-visited');
                $visited = true;
                $leafNodes[$i]->setMetadata('acyclic-test-visited', $visited);
            }
        } while (sizeof($leafNodes) > 0);

        // If graph is a DAG, there should be no non-visited nodes. Let's try to prove otherwise
        $result = true;
        foreach($nodeKeys as $key) if (!$nodes[$key]->getMetadata('acyclic-test-visited')) $result = false;
        
        // Cleanup visited marks
        foreach($nodeKeys as $key) $nodes[$key]->unsetMetadata('acyclic-test-visited');

        return $result;
    }
    /* }}} */

    /* isAcyclic {{{ */
    /**
    *
    * isAcyclic returns true if a graph contains no cycles, false otherwise.
    *
    * @return	boolean	 true iff graph is acyclic
    * @access	public
    */
    function isAcyclic(&$graph) {
        // We only test graphs
        if (!is_a($graph, 'Structures_Graph')) return Pear::raiseError('Structures_Graph_Manipulator_AcyclicTest::isAcyclic received an object that is not a Structures_Graph', STRUCTURES_GRAPH_ERROR_GENERIC);
        if (!$graph->isDirected()) return false; // Only directed graphs may be acyclic

        return Structures_Graph_Manipulator_AcyclicTest::_isAcyclic($graph);
    }
    /* }}} */
}
/* }}} */
?>
Structures_Graph-1.0.2/Structures/Graph/Manipulator/TopologicalSorter.php100644   1750   1750       15046 10560475634  22203 <?php
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
// +-----------------------------------------------------------------------------+
// | Copyright (c) 2003 Srgio Gonalves Carvalho                                |
// +-----------------------------------------------------------------------------+
// | This file is part of Structures_Graph.                                      |
// |                                                                             |
// | Structures_Graph is free software; you can redistribute it and/or modify    |
// | it under the terms of the GNU Lesser General Public License as published by |
// | the Free Software Foundation; either version 2.1 of the License, or         |
// | (at your option) any later version.                                         |
// |                                                                             |
// | Structures_Graph is distributed in the hope that it will be useful,         |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
// | GNU Lesser General Public License for more details.                         |
// |                                                                             |
// | You should have received a copy of the GNU Lesser General Public License    |
// | along with Structures_Graph; if not, write to the Free Software             |
// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA                    |
// | 02111-1307 USA                                                              |
// +-----------------------------------------------------------------------------+
// | Author: Srgio Carvalho <sergio.carvalho@portugalmail.com>                  |
// +-----------------------------------------------------------------------------+
//
/**
 * This file contains the definition of the Structures_Graph_Manipulator_TopologicalSorter class.
 * 
 * @see Structures_Graph_Manipulator_TopologicalSorter
 * @package Structures_Graph
 */

/* dependencies {{{ */
/** */
require_once 'PEAR.php';
/** */
require_once 'Structures/Graph.php';
/** */
require_once 'Structures/Graph/Node.php';
/** */
require_once 'Structures/Graph/Manipulator/AcyclicTest.php';
/* }}} */

/* class Structures_Graph_Manipulator_TopologicalSorter {{{ */
/**
 * The Structures_Graph_Manipulator_TopologicalSorter is a manipulator 
 * which is able to return the set of nodes in a graph, sorted by topological 
 * order.
 *
 * A graph may only be sorted topologically iff it's a DAG. You can test it
 * with the Structures_Graph_Manipulator_AcyclicTest.
 * 
 * @author		Srgio Carvalho <sergio.carvalho@portugalmail.com> 
 * @copyright	(c) 2004 by Srgio Carvalho
 * @see     Structures_Graph_Manipulator_AcyclicTest
 * @package Structures_Graph
 */
class Structures_Graph_Manipulator_TopologicalSorter {
    /* _nonVisitedInDegree {{{ */
    /**
    *
    * This is a variant of Structures_Graph::inDegree which does 
    * not count nodes marked as visited.
    *
    * @access   private
    * @return	integer	 Number of non-visited nodes that link to this one
    */
    function _nonVisitedInDegree(&$node) {
        $result = 0;
        $graphNodes =& $node->_graph->getNodes();
        foreach (array_keys($graphNodes) as $key) {
            if ((!$graphNodes[$key]->getMetadata('topological-sort-visited')) && $graphNodes[$key]->connectsTo($node)) $result++;
        }
        return $result;
        
    }
    /* }}} */

    /* _sort {{{ */
    /**
    * @access   private
    */
    function _sort(&$graph) {
        // Mark every node as not visited
        $nodes =& $graph->getNodes();
        $nodeKeys = array_keys($nodes);
        $refGenerator = array();
        foreach($nodeKeys as $key) {
            $refGenerator[] = false;
            $nodes[$key]->setMetadata('topological-sort-visited', $refGenerator[sizeof($refGenerator) - 1]);
        }

        // Iteratively peel off leaf nodes
        $topologicalLevel = 0;
        do {
            // Find out which nodes are leafs (excluding visited nodes)
            $leafNodes = array();
            foreach($nodeKeys as $key) {
                if ((!$nodes[$key]->getMetadata('topological-sort-visited')) && Structures_Graph_Manipulator_TopologicalSorter::_nonVisitedInDegree($nodes[$key]) == 0) {
                    $leafNodes[] =& $nodes[$key];
                }
            }
            // Mark leafs as visited
            $refGenerator[] = $topologicalLevel;
            for ($i=sizeof($leafNodes) - 1; $i>=0; $i--) {
                $visited =& $leafNodes[$i]->getMetadata('topological-sort-visited');
                $visited = true;
                $leafNodes[$i]->setMetadata('topological-sort-visited', $visited);
                $leafNodes[$i]->setMetadata('topological-sort-level', $refGenerator[sizeof($refGenerator) - 1]);
            }
            $topologicalLevel++;
        } while (sizeof($leafNodes) > 0);

        // Cleanup visited marks
        foreach($nodeKeys as $key) $nodes[$key]->unsetMetadata('topological-sort-visited');
    }
    /* }}} */

    /* sort {{{ */
    /**
    *
    * sort returns the graph's nodes, sorted by topological order. 
    * 
    * The result is an array with 
    * as many entries as topological levels. Each entry in this array is an array of nodes within
    * the given topological level.
    *
    * @return	array	 The graph's nodes, sorted by topological order.
    * @access	public
    */
    function sort(&$graph) {
        // We only sort graphs
        if (!is_a($graph, 'Structures_Graph')) return Pear::raiseError('Structures_Graph_Manipulator_TopologicalSorter::sort received an object that is not a Structures_Graph', STRUCTURES_GRAPH_ERROR_GENERIC);
        if (!Structures_Graph_Manipulator_AcyclicTest::isAcyclic($graph)) return Pear::raiseError('Structures_Graph_Manipulator_TopologicalSorter::sort received an graph that has cycles', STRUCTURES_GRAPH_ERROR_GENERIC);

        Structures_Graph_Manipulator_TopologicalSorter::_sort($graph);
        $result = array();

        // Fill out result array
        $nodes =& $graph->getNodes();
        $nodeKeys = array_keys($nodes);
        foreach($nodeKeys as $key) {
            if (!array_key_exists($nodes[$key]->getMetadata('topological-sort-level'), $result)) $result[$nodes[$key]->getMetadata('topological-sort-level')] = array();
            $result[$nodes[$key]->getMetadata('topological-sort-level')][] =& $nodes[$key];
            $nodes[$key]->unsetMetadata('topological-sort-level');
        }

        return $result;
    }
    /* }}} */
}
/* }}} */
?>
Structures_Graph-1.0.2/Structures/Graph/Node.php100644   1750   1750       25232 10560475634  15120 <?php
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
// +-----------------------------------------------------------------------------+
// | Copyright (c) 2003 Srgio Gonalves Carvalho                                |
// +-----------------------------------------------------------------------------+
// | This file is part of Structures_Graph.                                      |
// |                                                                             |
// | Structures_Graph is free software; you can redistribute it and/or modify    |
// | it under the terms of the GNU Lesser General Public License as published by |
// | the Free Software Foundation; either version 2.1 of the License, or         |
// | (at your option) any later version.                                         |
// |                                                                             |
// | Structures_Graph is distributed in the hope that it will be useful,         |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
// | GNU Lesser General Public License for more details.                         |
// |                                                                             |
// | You should have received a copy of the GNU Lesser General Public License    |
// | along with Structures_Graph; if not, write to the Free Software             |
// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA                    |
// | 02111-1307 USA                                                              |
// +-----------------------------------------------------------------------------+
// | Author: Srgio Carvalho <sergio.carvalho@portugalmail.com>                  |
// +-----------------------------------------------------------------------------+
//
/**
 * This file contains the definition of the Structures_Graph_Node class
 * 
 * @see Structures_Graph_Node
 * @package Structures_Graph
 */

/* dependencies {{{ */
/** */
require_once 'PEAR.php';
/** */
require_once 'Structures/Graph.php';
/* }}} */

/* class Structures_Graph_Node {{{ */
/**
 * The Structures_Graph_Node class represents a Node that can be member of a 
 * graph node set.
 *
 * A graph node can contain data. Under this API, the node contains default data, 
 * and key index data. It behaves, thus, both as a regular data node, and as a 
 * dictionary (or associative array) node.
 * 
 * Regular data is accessed via getData and setData. Key indexed data is accessed
 * via getMetadata and setMetadata.
 *
 * @author		Srgio Carvalho <sergio.carvalho@portugalmail.com> 
 * @copyright	(c) 2004 by Srgio Carvalho
 * @package Structures_Graph
 */
/* }}} */
class Structures_Graph_Node {
    /* fields {{{ */
    /** 
     * @access private 
     */
    var $_data = null;
    /** @access private */
    var $_metadata = array();
    /** @access private */
    var $_arcs = array();
    /** @access private */
    var $_graph = null;
    /* }}} */

    /* Constructor {{{ */
    /**
    *
    * Constructor
    *
    * @access	public
    */
    function Structures_Graph_Node() {
    }
    /* }}} */

    /* getGraph {{{ */
    /**
    *
    * Node graph getter
    *
    * @return	Structures_Graph	Graph where node is stored
    * @access	public
    */
    function &getGraph() {
        return $this->_graph;
    }
    /* }}} */

    /* setGraph {{{ */
    /**
    *
    * Node graph setter. This method should not be called directly. Use Graph::addNode instead.
    *
    * @param    Structures_Graph   Set the graph for this node. 
    * @see      Structures_Graph::addNode()
    * @access	public
    */
    function setGraph(&$graph) {
        $this->_graph =& $graph;
    }
    /* }}} */

    /* getData {{{ */
    /**
    *
    * Node data getter.
    * 
    * Each graph node can contain a reference to one variable. This is the getter for that reference.
    *
    * @return	mixed	Data stored in node
    * @access	public
    */
    function &getData() {
        return $this->_data;
    }
    /* }}} */

    /* setData {{{ */
    /**
    *
    * Node data setter
    *
    * Each graph node can contain a reference to one variable. This is the setter for that reference.
    *
    * @return	mixed	Data to store in node
    * @access	public
    */
    function setData($data) {
        $this->_data =& $data;
    }
    /* }}} */

    /* metadataKeyExists {{{ */
    /**
    *
    * Test for existence of metadata under a given key.
    *
    * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an 
    * associative array or in a dictionary. This method tests whether a given metadata key exists for this node.
    *
    * @param    string    Key to test
    * @return	boolean	 
    * @access	public
    */
    function metadataKeyExists($key) {
        return array_key_exists($key, $this->_metadata);
    }
    /* }}} */

    /* getMetadata {{{ */
    /**
    *
    * Node metadata getter
    *
    * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an 
    * associative array or in a dictionary. This method gets the data under the given key. If the key does
    * not exist, an error will be thrown, so testing using metadataKeyExists might be needed.
    *
    * @param    string  Key
    * @param    boolean nullIfNonexistent (defaults to false).
    * @return	mixed	Metadata Data stored in node under given key
    * @see      metadataKeyExists
    * @access	public
    */
    function &getMetadata($key, $nullIfNonexistent = false) {
        if (array_key_exists($key, $this->_metadata)) {
            return $this->_metadata[$key];
        } else {
            if ($nullIfNonexistent) {
                $a = null;
                return $a;
            } else {
                $a = Pear::raiseError('Structures_Graph_Node::getMetadata: Requested key does not exist', STRUCTURES_GRAPH_ERROR_GENERIC);
                return $a;
            }
        }
    }
    /* }}} */

    /* unsetMetadata {{{ */
    /**
    *
    * Delete metadata by key
    *
    * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an 
    * associative array or in a dictionary. This method removes any data that might be stored under the provided key.
    * If the key does not exist, no error is thrown, so it is safe using this method without testing for key existence.
    *
    * @param    string  Key
    * @access	public
    */
    function unsetMetadata($key) {
        if (array_key_exists($key, $this->_metadata)) unset($this->_metadata[$key]);
    }
    /* }}} */

    /* setMetadata {{{ */
    /**
    *
    * Node metadata setter
    *
    * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an 
    * associative array or in a dictionary. This method stores data under the given key. If the key already exists,
    * previously stored data is discarded.
    *
    * @param    string  Key
    * @param    mixed   Data 
    * @access	public
    */
    function setMetadata($key, $data) {
        $this->_metadata[$key] =& $data;
    }
    /* }}} */

    /* _connectTo {{{ */
    /** @access private */
    function _connectTo(&$destinationNode) {
        $this->_arcs[] =& $destinationNode;
    }
    /* }}} */

    /* connectTo {{{ */
    /**
    *
    * Connect this node to another one.
    * 
    * If the graph is not directed, the reverse arc, connecting $destinationNode to $this is also created.
    *
    * @param    Structures_Graph Node to connect to
    * @access	public
    */
    function connectTo(&$destinationNode) {
        // We only connect to nodes
        if (!is_a($destinationNode, 'Structures_Graph_Node')) return Pear::raiseError('Structures_Graph_Node::connectTo received an object that is not a Structures_Graph_Node', STRUCTURES_GRAPH_ERROR_GENERIC);
        // Nodes must already be in graphs to be connected
        if ($this->_graph == null) return Pear::raiseError('Structures_Graph_Node::connectTo Tried to connect a node that is not in a graph', STRUCTURES_GRAPH_ERROR_GENERIC);
        if ($destinationNode->getGraph() == null) return Pear::raiseError('Structures_Graph_Node::connectTo Tried to connect to a node that is not in a graph', STRUCTURES_GRAPH_ERROR_GENERIC);
        // Connect here
        $this->_connectTo($destinationNode);
        // If graph is undirected, connect back
        if (!$this->_graph->isDirected()) {
            $destinationNode->_connectTo($this);
        }
    }
    /* }}} */

    /* getNeighbours {{{ */
    /**
    *
    * Return nodes connected to this one.
    * 
    * @return   array   Array of nodes
    * @access	public
    */
    function getNeighbours() {
        return $this->_arcs;
    }
    /* }}} */

    /* connectsTo {{{ */
    /**
    *
    * Test wether this node has an arc to the target node
    *
    * @return	boolean   True if the two nodes are connected
    * @access	public
    */
    function connectsTo(&$target) {
        $copy = $target;
        $arcKeys = array_keys($this->_arcs);
        foreach($arcKeys as $key) {
            /* ZE1 chokes on this expression:
                if ($target === $arc) return true;
              so, we'll use more convoluted stuff
            */
            $arc =& $this->_arcs[$key];
            $target = true;
            if ($arc === true) {
                $target = false;
                if ($arc === false) {
                    $target = $copy;
                    return true;
                }
            }
        }
        $target = $copy;
        return false;
    }
    /* }}} */

    /* inDegree {{{ */
    /**
    *
    * Calculate the in degree of the node.
    * 
    * The indegree for a node is the number of arcs entering the node. For non directed graphs, 
    * the indegree is equal to the outdegree.
    *
    * @return	integer	 In degree of the node
    * @access	public
    */
    function inDegree() {
        if ($this->_graph == null) return 0;
        if (!$this->_graph->isDirected()) return $this->outDegree();
        $result = 0;
        $graphNodes =& $this->_graph->getNodes();
        foreach (array_keys($graphNodes) as $key) {
            if ($graphNodes[$key]->connectsTo($this)) $result++;
        }
        return $result;
        
    }
    /* }}} */

    /* outDegree {{{ */
    /**
    *
    * Calculate the out degree of the node.
    *
    * The outdegree for a node is the number of arcs exiting the node. For non directed graphs,
    * the outdegree is always equal to the indegree.
    * 
    * @return	integer	 Out degree of the node
    * @access	public
    */
    function outDegree() {
        if ($this->_graph == null) return 0;
        return sizeof($this->_arcs);
    }
    /* }}} */
}
?>
Structures_Graph-1.0.2/Structures/Graph.php100644   1750   1750       13163 10560475634  14233 <?php
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
// +-----------------------------------------------------------------------------+
// | Copyright (c) 2003 Srgio Gonalves Carvalho                                |
// +-----------------------------------------------------------------------------+
// | This file is part of Structures_Graph.                                      |
// |                                                                             |
// | Structures_Graph is free software; you can redistribute it and/or modify    |
// | it under the terms of the GNU Lesser General Public License as published by |
// | the Free Software Foundation; either version 2.1 of the License, or         |
// | (at your option) any later version.                                         |
// |                                                                             |
// | Structures_Graph is distributed in the hope that it will be useful,         |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
// | GNU Lesser General Public License for more details.                         |
// |                                                                             |
// | You should have received a copy of the GNU Lesser General Public License    |
// | along with Structures_Graph; if not, write to the Free Software             |
// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA                    |
// | 02111-1307 USA                                                              |
// +-----------------------------------------------------------------------------+
// | Author: Srgio Carvalho <sergio.carvalho@portugalmail.com>                  |
// +-----------------------------------------------------------------------------+
//
/**
 * The Graph.php file contains the definition of the Structures_Graph class 
 *
 * @see Structures_Graph
 * @package Structures_Graph
 */

/* dependencies {{{ */
/** PEAR base classes */
require_once 'PEAR.php';
/** Graph Node */
require_once 'Structures/Graph/Node.php';
/* }}} */

define('STRUCTURES_GRAPH_ERROR_GENERIC', 100);

/* class Structures_Graph {{{ */
/**
 * The Structures_Graph class represents a graph data structure. 
 *
 * A Graph is a data structure composed by a set of nodes, connected by arcs.
 * Graphs may either be directed or undirected. In a directed graph, arcs are 
 * directional, and can be traveled only one way. In an undirected graph, arcs
 * are bidirectional, and can be traveled both ways.
 *
 * @author		Srgio Carvalho <sergio.carvalho@portugalmail.com> 
 * @copyright	(c) 2004 by Srgio Carvalho
 * @package Structures_Graph
 */
/* }}} */
class Structures_Graph {
    /* fields {{{ */
    /**
     * @access private
     */
    var $_nodes = array();
    /**
     * @access private
     */
    var $_directed = false;
    /* }}} */

    /* Constructor {{{ */
    /**
    *
    * Constructor
    *
    * @param    boolean    Set to true if the graph is directed. Set to false if it is not directed. (Optional, defaults to true)
    * @access	public
    */
    function Structures_Graph($directed = true) {
        $this->_directed = $directed;
    }
    /* }}} */

    /* isDirected {{{ */
    /**
    *
    * Return true if a graph is directed
    *
    * @return	boolean	 true if the graph is directed
    * @access	public
    */
    function isDirected() {
        return (boolean) $this->_directed;
    }
    /* }}} */

    /* addNode {{{ */
    /**
    *
    * Add a Node to the Graph
    *
    * @param    Structures_Graph_Node   The node to be added.
    * @access	public
    */
    function addNode(&$newNode) {
        // We only add nodes
        if (!is_a($newNode, 'Structures_Graph_Node')) return Pear::raiseError('Structures_Graph::addNode received an object that is not a Structures_Graph_Node', STRUCTURES_GRAPH_ERROR_GENERIC);
        // Graphs are node *sets*, so duplicates are forbidden. We allow nodes that are exactly equal, but disallow equal references.
        foreach($this->_nodes as $key => $node) {
            /*
             ZE1 equality operators choke on the recursive cycle introduced by the _graph field in the Node object.
             So, we'll check references the hard way (change $this->_nodes[$key] and check if the change reflects in 
             $node)
            */
            $savedData = $this->_nodes[$key];
            $referenceIsEqualFlag = false;
            $this->_nodes[$key] = true;
            if ($node === true) {
                $this->_nodes[$key] = false;
                if ($node === false) $referenceIsEqualFlag = true;
            }
            $this->_nodes[$key] = $savedData;
            if ($referenceIsEqualFlag) return Pear::raiseError('Structures_Graph::addNode received an object that is a duplicate for this dataset', STRUCTURES_GRAPH_ERROR_GENERIC);
        }
        $this->_nodes[] =& $newNode;
        $newNode->setGraph($this);
    }
    /* }}} */

    /* removeNode (unimplemented) {{{ */
    /**
    *
    * Remove a Node from the Graph
    *
    * @todo     This is unimplemented
    * @param    Structures_Graph_Node   The node to be removed from the graph
    * @access	public
    */
    function removeNode(&$node) {
    }
    /* }}} */

    /* getNodes {{{ */
    /**
    *
    * Return the node set, in no particular order. For ordered node sets, use a Graph Manipulator insted.
    *
    * @access   public
    * @see      Structures_Graph_Manipulator_TopologicalSorter
    * @return   array The set of nodes in this graph
    */
    function &getNodes() {
        return $this->_nodes;
    }
    /* }}} */
}
?>
Structures_Graph-1.0.2/tests/testCase/BasicGraph.php100644   1750   1750       22050 10560475634  15722 <?php
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
// +-----------------------------------------------------------------------------+
// | Copyright (c) 2003 Srgio Gonalves Carvalho                                |
// +-----------------------------------------------------------------------------+
// | This file is part of Structures_Graph.                                      |
// |                                                                             |
// | Structures_Graph is free software; you can redistribute it and/or modify    |
// | it under the terms of the GNU Lesser General Public License as published by |
// | the Free Software Foundation; either version 2.1 of the License, or         |
// | (at your option) any later version.                                         |
// |                                                                             |
// | Structures_Graph is distributed in the hope that it will be useful,         |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
// | GNU Lesser General Public License for more details.                         |
// |                                                                             |
// | You should have received a copy of the GNU Lesser General Public License    |
// | along with Structures_Graph; if not, write to the Free Software             |
// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA                    |
// | 02111-1307 USA                                                              |
// +-----------------------------------------------------------------------------+
// | Author: Srgio Carvalho <sergio.carvalho@portugalmail.com>                  |
// +-----------------------------------------------------------------------------+
//

require_once 'Structures/Graph.php';
require_once 'PHPUnit.php';

/**
 * @access private
 */
class BasicGraph extends PHPUnit_TestCase
{
    var $_graph = null;

    // constructor of the test suite
    function StringTest($name) {
       $this->PHPUnit_TestCase($name);
    }

    function setUp() {
    }

    function tearDown() {
    }

    function test_create_graph() {
        $this->_graph = new Structures_Graph();
        $this->assertTrue(is_a($this->_graph, 'Structures_Graph')); 
    }

    function test_add_node() {
        $this->_graph = new Structures_Graph();
        $data = 1;
        $node = new Structures_Graph_Node($data);
        $this->_graph->addNode($node);
        $node = new Structures_Graph_Node($data);
        $this->_graph->addNode($node);
        $node = new Structures_Graph_Node($data);
        $this->_graph->addNode($node);
    }

    function test_connect_node() {
        $this->_graph = new Structures_Graph();
        $data = 1;
        $node1 = new Structures_Graph_Node($data);
        $node2 = new Structures_Graph_Node($data);
        $this->_graph->addNode($node1);
        $this->_graph->addNode($node2);
        $node1->connectTo($node2);

        $node =& $this->_graph->getNodes();
        $node =& $node[0];
        $node = $node->getNeighbours();
        $node =& $node[0];
        /* 
         ZE1 == and === operators fail on $node,$node2 because of the recursion introduced
         by the _graph field in the Node object. So, we'll use the stupid method for reference
         testing
        */
        $node = true;
        $this->assertTrue($node2);
        $node = false;
        $this->assertFalse($node2);
    }

    function test_data_references() {
        $this->_graph = new Structures_Graph();
        $data = 1;
        $node = new Structures_Graph_Node();
        $node->setData(&$data);
        $this->_graph->addNode($node);
        $data = 2;
        $dataInNode =& $this->_graph->getNodes();
        $dataInNode =& $dataInNode[0];
        $dataInNode =& $dataInNode->getData();
        $this->assertTrue($data === $dataInNode);
    }

    function test_metadata_references() {
        $this->_graph = new Structures_Graph();
        $data = 1;
        $node = new Structures_Graph_Node();
        $node->setMetadata('5', &$data);
        $data = 2;
        $dataInNode =& $node->getMetadata('5');
        $this->assertTrue($data === $dataInNode);
    }
   
    function test_metadata_key_exists() {
        $this->_graph = new Structures_Graph();
        $data = 1;
        $node = new Structures_Graph_Node();
        $node->setMetadata('5', $data);
        $this->assertTrue($node->metadataKeyExists('5'));
        $this->assertFalse($node->metadataKeyExists('1'));
    }

    function test_directed_degree() {
        $this->_graph = new Structures_Graph(true);
        $node = array();
        $node[] = new Structures_Graph_Node();
        $node[] = new Structures_Graph_Node();
        $node[] = new Structures_Graph_Node();
        $this->_graph->addNode($node[0]);
        $this->_graph->addNode($node[1]);
        $this->_graph->addNode($node[2]);
        $this->assertEquals(0, $node[0]->inDegree(), 'inDegree test failed for node 0 with 0 arcs');
        $this->assertEquals(0, $node[1]->inDegree(), 'inDegree test failed for node 1 with 0 arcs');
        $this->assertEquals(0, $node[2]->inDegree(), 'inDegree test failed for node 2 with 0 arcs');
        $this->assertEquals(0, $node[0]->outDegree(), 'outDegree test failed for node 0 with 0 arcs');
        $this->assertEquals(0, $node[1]->outDegree(), 'outDegree test failed for node 1 with 0 arcs');
        $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 0 arcs');
        $node[0]->connectTo($node[1]);
        $this->assertEquals(0, $node[0]->inDegree(), 'inDegree test failed for node 0 with 1 arc');
        $this->assertEquals(1, $node[1]->inDegree(), 'inDegree test failed for node 1 with 1 arc');
        $this->assertEquals(0, $node[2]->inDegree(), 'inDegree test failed for node 2 with 1 arc');
        $this->assertEquals(1, $node[0]->outDegree(), 'outDegree test failed for node 0 with 1 arc');
        $this->assertEquals(0, $node[1]->outDegree(), 'outDegree test failed for node 1 with 1 arc');
        $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 1 arc');
        $node[0]->connectTo($node[2]);
        $this->assertEquals(0, $node[0]->inDegree(), 'inDegree test failed for node 0 with 2 arcs');
        $this->assertEquals(1, $node[1]->inDegree(), 'inDegree test failed for node 1 with 2 arcs');
        $this->assertEquals(1, $node[2]->inDegree(), 'inDegree test failed for node 2 with 2 arcs');
        $this->assertEquals(2, $node[0]->outDegree(), 'outDegree test failed for node 0 with 2 arcs');
        $this->assertEquals(0, $node[1]->outDegree(), 'outDegree test failed for node 1 with 2 arcs');
        $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 2 arcs');
    }

    function test_undirected_degree() {
        $this->_graph = new Structures_Graph(false);
        $node = array();
        $node[] = new Structures_Graph_Node();
        $node[] = new Structures_Graph_Node();
        $node[] = new Structures_Graph_Node();
        $this->_graph->addNode($node[0]);
        $this->_graph->addNode($node[1]);
        $this->_graph->addNode($node[2]);
        $this->assertEquals(0, $node[0]->inDegree(), 'inDegree test failed for node 0 with 0 arcs');
        $this->assertEquals(0, $node[1]->inDegree(), 'inDegree test failed for node 1 with 0 arcs');
        $this->assertEquals(0, $node[2]->inDegree(), 'inDegree test failed for node 2 with 0 arcs');
        $this->assertEquals(0, $node[0]->outDegree(), 'outDegree test failed for node 0 with 0 arcs');
        $this->assertEquals(0, $node[1]->outDegree(), 'outDegree test failed for node 1 with 0 arcs');
        $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 0 arcs');
        $node[0]->connectTo($node[1]);
        $this->assertEquals(1, $node[0]->inDegree(), 'inDegree test failed for node 0 with 1 arc');
        $this->assertEquals(1, $node[1]->inDegree(), 'inDegree test failed for node 1 with 1 arc');
        $this->assertEquals(0, $node[2]->inDegree(), 'inDegree test failed for node 2 with 1 arc');
        $this->assertEquals(1, $node[0]->outDegree(), 'outDegree test failed for node 0 with 1 arc');
        $this->assertEquals(1, $node[1]->outDegree(), 'outDegree test failed for node 1 with 1 arc');
        $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 1 arc');
        $node[0]->connectTo($node[2]);
        $this->assertEquals(2, $node[0]->inDegree(), 'inDegree test failed for node 0 with 2 arcs');
        $this->assertEquals(1, $node[1]->inDegree(), 'inDegree test failed for node 1 with 2 arcs');
        $this->assertEquals(1, $node[2]->inDegree(), 'inDegree test failed for node 2 with 2 arcs');
        $this->assertEquals(2, $node[0]->outDegree(), 'outDegree test failed for node 0 with 2 arcs');
        $this->assertEquals(1, $node[1]->outDegree(), 'outDegree test failed for node 1 with 2 arcs');
        $this->assertEquals(1, $node[2]->outDegree(), 'outDegree test failed for node 2 with 2 arcs');
    }
}
?>
Structures_Graph-1.0.2/tests/all-tests.php100644   1750   1750        4373 10560475634  14044 #!/usr/bin/php
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
// +-----------------------------------------------------------------------------+
// | Copyright (c) 2003 Srgio Gonalves Carvalho                                |
// +-----------------------------------------------------------------------------+
// | This file is part of Structures_Graph.                                      |
// |                                                                             |
// | Structures_Graph is free software; you can redistribute it and/or modify    |
// | it under the terms of the GNU Lesser General Public License as published by |
// | the Free Software Foundation; either version 2.1 of the License, or         |
// | (at your option) any later version.                                         |
// |                                                                             |
// | Structures_Graph is distributed in the hope that it will be useful,         |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
// | GNU Lesser General Public License for more details.                         |
// |                                                                             |
// | You should have received a copy of the GNU Lesser General Public License    |
// | along with Structures_Graph; if not, write to the Free Software             |
// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA                    |
// | 02111-1307 USA                                                              |
// +-----------------------------------------------------------------------------+
// | Author: Srgio Carvalho <sergio.carvalho@portugalmail.com>                  |
// +-----------------------------------------------------------------------------+
//

// Place development Structures_Graph ahead in the include_path
ini_set('include_path', realpath(dirname(__FILE__) . "/..") . ":.:" . ini_get('include_path'));

require_once 'testCase/BasicGraph.php';
require_once 'PHPUnit.php';

$suite  = new PHPUnit_TestSuite();
$suite->addTest(new PHPUnit_TestSuite('BasicGraph'));
$result = PHPUnit::run($suite);

echo $result->toString();
?>
Structures_Graph-1.0.2/tests/README100644   1750   1750           0 10560475634  12202 Structures_Graph-1.0.2/genpackage.xml.pl100644   1750   1750         642 10560475634  13455 #!/usr/bin/perl
while (<>) {
    if (!/FILESGOHERE/) {
        print $_;
    } else {
        open FILELIST,'find Structures -type f | grep -v .arch-ids |';
        while (<FILELIST>) {
	    $md5sum = `md5sum $_`;
	    chomp($md5sum);
	    $md5sum = substr $md5sum, 0, 32;
#    $_ =~ s/\//\\\//g;
            chomp($_);
            print "    <file role=\"php\" md5sum=\"$md5sum\" name=\"$_\" />\n";
        }
    }
}
Structures_Graph-1.0.2/LICENSE100644   1750   1750       63476 10560475634  11317 		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 Free Software Foundation, Inc.
     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
not price.  Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.

  You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.

  2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.

  If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License.  However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.

  9. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.

  11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.

If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded.  In such case, this License incorporates the limitation as if
written in the body of this License.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number.  If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation.  If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission.  For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this.  Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.

			    NO WARRANTY

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.

		     END OF TERMS AND CONDITIONS

           How to Apply These Terms to Your New Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

Also add information on how to contact you by electronic and paper mail.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!


Structures_Graph-1.0.2/package.sh100644   1750   1750        3027 10560475634  12203 #!/bin/bash
VERSION=`tla tree-version 2>&1 | sed "s/^.*\([0-9][0-9]*\.[0-9][0-9]*\)$/\1/g"`
TARGET_DIR=BUILD/
TARGET_DIRS=`find Structures -type d | grep -v .arch-ids`
mkdir -p $TARGET_DIR
./genpackage.xml.pl > BUILD/package.xml << EOF
<?xml version="1.0" encoding="ISO-8859-1" ?>
<package version="1.0">
 <name>Structures_Graph</name>
 <summary>Graph datastructure manipulation library</summary>
 <license>LGPL</license>
 <description>
 Structures_Graph is a package for creating and manipulating graph datastructures. It allows building of directed
 and undirected graphs, with data and metadata stored in nodes. The library provides functions for graph traversing
 as well as for characteristic extraction from the graph topology.
 </description>
 <maintainers>
  <maintainer>
   <user>sergiosgc</user>
   <name>Srgio Carvalho</name>
   <email>sergio.carvalho@portugalmail.com</email>
   <role>lead</role>
  </maintainer>
 </maintainers>

 <release>
  <version>1.0.3</version>
  <date>2007-01-30</date>
  <state>stable</state>
  <notes>
  Version 1.0.3 is functionally equivalent to 1.0.2, but released with a v1.0 package.xml to deal with bug #9965:installation problem
  </notes>
  <filelist>
FILESGOHERE
  </filelist>
 </release>
 <deps>
  <dep type="pkg" rel="ge" version="1.2">PEAR</dep>
 </deps>
</package>
EOF
for dir in $TARGET_DIRS
do
    mkdir -p $TARGET_DIR/$dir
    cp `find $dir -maxdepth 1 -type f | grep -v .arch-ids` $TARGET_DIR/$dir
done
cp LICENSE BUILD
(cd BUILD; pear package)
rm -Rf BUILD/package.xml BUILD/LICENSE BUILD/Structures


Structures_Graph-1.0.2/publish.sh100644   1750   1750         411 10560475634  12230 #!/bin/sh
./package.sh
scp BUILD/*.tgz root@sergiocarvalho.com:/home/httpd/vhosts/com/sergiocarvalho/pear-base/pear
(cd docs; ./generate.sh)
scp -r docs/html/* root@iluvatar.portugalmail.pt:/home/httpd/vhosts/com/sergiocarvalho/pear-base/pear/docs/Structures_Graph
package.xml100644   1750   1750        7334 10560475634   6436 <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE package SYSTEM "http://pear.php.net/dtd/package-1.0">
<package version="1.0" packagerversion="1.5.0">
 <name>Structures_Graph</name>
 <summary>Graph datastructure manipulation library</summary>
 <description>Structures_Graph is a package for creating and manipulating graph datastructures. It allows building of directed
and undirected graphs, with data and metadata stored in nodes. The library provides functions for graph traversing
as well as for characteristic extraction from the graph topology.
 </description>
 <maintainers>
  <maintainer>
   <user>sergiosgc</user>
   <name>Sérgio Carvalho</name>
   <email>sergio.carvalho@portugalmail.com</email>
   <role>lead</role>
  </maintainer>
  </maintainers>
 <release>
  <version>1.0.2</version>
  <date>2007-02-01</date>
  <license>LGPL</license>
  <state>stable</state>
  <notes>- Bug #9682 only variables can be returned by reference
- fix Bug #9661 notice in Structures_Graph_Manipulator_Topological::sort()
  </notes>
  <filelist>
   <file role="doc" baseinstalldir="" name="docs/html/media/banner.css"/>
   <file role="doc" baseinstalldir="" name="docs/html/media/stylesheet.css"/>
   <file role="doc" baseinstalldir="" name="docs/html/Structures_Graph/Structures_Graph.html"/>
   <file role="doc" baseinstalldir="" name="docs/html/Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html"/>
   <file role="doc" baseinstalldir="" name="docs/html/Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.html"/>
   <file role="doc" baseinstalldir="" name="docs/html/Structures_Graph/Structures_Graph_Node.html"/>
   <file role="doc" baseinstalldir="" name="docs/html/Structures_Graph/tutorial_Structures_Graph.pkg.html"/>
   <file role="doc" baseinstalldir="" name="docs/html/Structures_Graph/_Structures_Graph_Manipulator_AcyclicTest_php.html"/>
   <file role="doc" baseinstalldir="" name="docs/html/Structures_Graph/_Structures_Graph_Manipulator_TopologicalSorter_php.html"/>
   <file role="doc" baseinstalldir="" name="docs/html/Structures_Graph/_Structures_Graph_Node_php.html"/>
   <file role="doc" baseinstalldir="" name="docs/html/Structures_Graph/_Structures_Graph_php.html"/>
   <file role="doc" baseinstalldir="" name="docs/html/classtrees_Structures_Graph.html"/>
   <file role="doc" baseinstalldir="" name="docs/html/elementindex.html"/>
   <file role="doc" baseinstalldir="" name="docs/html/elementindex_Structures_Graph.html"/>
   <file role="doc" baseinstalldir="" name="docs/html/errors.html"/>
   <file role="doc" baseinstalldir="" name="docs/html/index.html"/>
   <file role="doc" baseinstalldir="" name="docs/html/li_Structures_Graph.html"/>
   <file role="doc" baseinstalldir="" name="docs/html/packages.html"/>
   <file role="doc" baseinstalldir="" name="docs/html/todolist.html"/>
   <file role="doc" baseinstalldir="" name="docs/tutorials/Structures_Graph/Structures_Graph.pkg"/>
   <file role="doc" baseinstalldir="" name="docs/generate.sh"/>
   <file role="php" baseinstalldir="" name="Structures/Graph/Manipulator/AcyclicTest.php"/>
   <file role="php" baseinstalldir="" name="Structures/Graph/Manipulator/TopologicalSorter.php"/>
   <file role="php" baseinstalldir="" name="Structures/Graph/Node.php"/>
   <file role="php" baseinstalldir="" name="Structures/Graph.php"/>
   <file role="test" baseinstalldir="" name="tests/testCase/BasicGraph.php"/>
   <file role="test" baseinstalldir="" name="tests/all-tests.php"/>
   <file role="test" baseinstalldir="" name="tests/README"/>
   <file role="data" baseinstalldir="" name="genpackage.xml.pl"/>
   <file role="data" baseinstalldir="" name="LICENSE"/>
   <file role="data" baseinstalldir="" name="package.sh"/>
   <file role="data" baseinstalldir="" name="publish.sh"/>
  </filelist>
 </release>
</package>
<?php
/**
 * File/Directory manipulation
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * 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 web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   pear
 * @package    System
 * @author     Tomas V.V.Cox <cox@idecnet.com>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: System.php,v 1.62 2008/01/03 20:26:34 cellog Exp $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * base class
 */
require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
require_once 'phar://install-pear-nozlib.phar/' . 'Console/Getopt.php';

$GLOBALS['_System_temp_files'] = array();

/**
* System offers cross plattform compatible system functions
*
* Static functions for different operations. Should work under
* Unix and Windows. The names and usage has been taken from its respectively
* GNU commands. The functions will return (bool) false on error and will
* trigger the error with the PHP trigger_error() function (you can silence
* the error by prefixing a '@' sign after the function call, but this
* is not recommended practice.  Instead use an error handler with
* {@link set_error_handler()}).
*
* Documentation on this class you can find in:
* http://pear.php.net/manual/
*
* Example usage:
* if (!@System::rm('-r file1 dir1')) {
*    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));
*
* @category   pear
* @package    System
* @author     Tomas V.V. Cox <cox@idecnet.com>
* @copyright  1997-2006 The PHP Group
* @license    http://www.php.net/license/3_0.txt  PHP License 3.0
* @version    Release: 1.7.2
* @link       http://pear.php.net/package/PEAR
* @since      Class available since Release 0.1
* @static 
*/
class System
{
    /**
    * returns the commandline arguments of a function
    *
    * @param    string  $argv           the commandline
    * @param    string  $short_options  the allowed option short-tags
    * @param    string  $long_options   the allowed option long-tags
    * @return   array   the given options and there values
    * @static 
    * @access private
    */
    function _parseArgs($argv, $short_options, $long_options = null)
    {
        if (!is_array($argv) && $argv !== null) {
            $argv = preg_split('/\s+/', $argv, -1, PREG_SPLIT_NO_EMPTY);
        }
        return Console_Getopt::getopt2($argv, $short_options);
    }

    /**
    * Output errors with PHP trigger_error(). You can silence the errors
    * with prefixing a "@" sign to the function call: @System::mkdir(..);
    *
    * @param mixed $error a PEAR error or a string with the error message
    * @return bool false
    * @static 
    * @access private
    */
    function raiseError($error)
    {
        if (PEAR::isError($error)) {
            $error = $error->getMessage();
        }
        trigger_error($error, E_USER_WARNING);
        return false;
    }

    /**
    * Creates a nested array representing the structure of a directory
    *
    * System::_dirToStruct('dir1', 0) =>
    *   Array
    *    (
    *    [dirs] => Array
    *        (
    *            [0] => dir1
    *        )
    *
    *    [files] => Array
    *        (
    *            [0] => dir1/file2
    *            [1] => dir1/file3
    *        )
    *    )
    * @param    string  $sPath      Name of the directory
    * @param    integer $maxinst    max. deep of the lookup
    * @param    integer $aktinst    starting deep of the lookup
    * @param    bool    $silent     if true, do not emit errors.
    * @return   array   the structure of the dir
    * @static 
    * @access   private
    */

    function _dirToStruct($sPath, $maxinst, $aktinst = 0, $silent = false)
    {
        $struct = array('dirs' => array(), 'files' => array());
        if (($dir = @opendir($sPath)) === false) {
            if (!$silent) {
                System::raiseError("Could not open dir $sPath");
            }
            return $struct; // XXX could not open error
        }
        $struct['dirs'][] = $sPath = realpath($sPath); // XXX don't add if '.' or '..' ?
        $list = array();
        while (false !== ($file = readdir($dir))) {
            if ($file != '.' && $file != '..') {
                $list[] = $file;
            }
        }
        closedir($dir);
        sort($list);
        if ($aktinst < $maxinst || $maxinst == 0) {
            foreach ($list as $val) {
                $path = $sPath . DIRECTORY_SEPARATOR . $val;
                if (is_dir($path) && !is_link($path)) {
                    $tmp = System::_dirToStruct($path, $maxinst, $aktinst+1, $silent);
                    $struct = array_merge_recursive($tmp, $struct);
                } else {
                    $struct['files'][] = $path;
                }
            }
        }
        return $struct;
    }

    /**
    * Creates a nested array representing the structure of a directory and files
    *
    * @param    array $files Array listing files and dirs
    * @return   array
    * @static 
    * @see System::_dirToStruct()
    */
    function _multipleToStruct($files)
    {
        $struct = array('dirs' => array(), 'files' => array());
        settype($files, 'array');
        foreach ($files as $file) {
            if (is_dir($file) && !is_link($file)) {
                $tmp = System::_dirToStruct($file, 0);
                $struct = array_merge_recursive($tmp, $struct);
            } else {
                $struct['files'][] = $file;
            }
        }
        return $struct;
    }

    /**
    * The rm command for removing files.
    * Supports multiple files and dirs and also recursive deletes
    *
    * @param    string  $args   the arguments for rm
    * @return   mixed   PEAR_Error or true for success
    * @static 
    * @access   public
    */
    function rm($args)
    {
        $opts = System::_parseArgs($args, 'rf'); // "f" does nothing but I like it :-)
        if (PEAR::isError($opts)) {
            return System::raiseError($opts);
        }
        foreach ($opts[0] as $opt) {
            if ($opt[0] == 'r') {
                $do_recursive = true;
            }
        }
        $ret = true;
        if (isset($do_recursive)) {
            $struct = System::_multipleToStruct($opts[1]);
            foreach ($struct['files'] as $file) {
                if (!@unlink($file)) {
                    $ret = false;
                }
            }
            foreach ($struct['dirs'] as $dir) {
                if (!@rmdir($dir)) {
                    $ret = false;
                }
            }
        } else {
            foreach ($opts[1] as $file) {
                $delete = (is_dir($file)) ? 'rmdir' : 'unlink';
                if (!@$delete($file)) {
                    $ret = false;
                }
            }
        }
        return $ret;
    }

    /**
    * Make directories.
    *
    * The -p option will create parent directories
    * @param    string  $args    the name of the director(y|ies) to create
    * @return   bool    True for success
    * @static 
    * @access   public
    */
    function mkDir($args)
    {
        $opts = System::_parseArgs($args, 'pm:');
        if (PEAR::isError($opts)) {
            return System::raiseError($opts);
        }
        $mode = 0777; // default mode
        foreach ($opts[0] as $opt) {
            if ($opt[0] == 'p') {
                $create_parents = true;
            } elseif ($opt[0] == 'm') {
                // if the mode is clearly an octal number (starts with 0)
                // convert it to decimal
                if (strlen($opt[1]) && $opt[1]{0} == '0') {
                    $opt[1] = octdec($opt[1]);
                } else {
                    // convert to int
                    $opt[1] += 0;
                }
                $mode = $opt[1];
            }
        }
        $ret = true;
        if (isset($create_parents)) {
            foreach ($opts[1] as $dir) {
                $dirstack = array();
                while ((!file_exists($dir) || !is_dir($dir)) &&
                        $dir != DIRECTORY_SEPARATOR) {
                    array_unshift($dirstack, $dir);
                    $dir = dirname($dir);
                }
                while ($newdir = array_shift($dirstack)) {
                    if (!is_writeable(dirname($newdir))) {
                        $ret = false;
                        break;
                    }
                    if (!mkdir($newdir, $mode)) {
                        $ret = false;
                    }
                }
            }
        } else {
            foreach($opts[1] as $dir) {
                if ((@file_exists($dir) || !is_dir($dir)) && !mkdir($dir, $mode)) {
                    $ret = false;
                }
            }
        }
        return $ret;
    }

    /**
    * Concatenate files
    *
    * Usage:
    * 1) $var = System::cat('sample.txt test.txt');
    * 2) System::cat('sample.txt test.txt > final.txt');
    * 3) System::cat('sample.txt test.txt >> final.txt');
    *
    * Note: as the class use fopen, urls should work also (test that)
    *
    * @param    string  $args   the arguments
    * @return   boolean true on success
    * @static 
    * @access   public
    */
    function &cat($args)
    {
        $ret = null;
        $files = array();
        if (!is_array($args)) {
            $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
        }

        $count_args = count($args);
        for ($i = 0; $i < $count_args; $i++) {
            if ($args[$i] == '>') {
                $mode = 'wb';
                $outputfile = $args[$i+1];
                break;
            } elseif ($args[$i] == '>>') {
                $mode = 'ab+';
                $outputfile = $args[$i+1];
                break;
            } else {
                $files[] = $args[$i];
            }
        }
        $outputfd = false;
        if (isset($mode)) {
            if (!$outputfd = fopen($outputfile, $mode)) {
                $err = System::raiseError("Could not open $outputfile");
                return $err;
            }
            $ret = true;
        }
        foreach ($files as $file) {
            if (!$fd = fopen($file, 'r')) {
                System::raiseError("Could not open $file");
                continue;
            }
            while ($cont = fread($fd, 2048)) {
                if (is_resource($outputfd)) {
                    fwrite($outputfd, $cont);
                } else {
                    $ret .= $cont;
                }
            }
            fclose($fd);
        }
        if (is_resource($outputfd)) {
            fclose($outputfd);
        }
        return $ret;
    }

    /**
    * Creates temporary files or directories. This function will remove
    * the created files when the scripts finish its execution.
    *
    * Usage:
    *   1) $tempfile = System::mktemp("prefix");
    *   2) $tempdir  = System::mktemp("-d prefix");
    *   3) $tempfile = System::mktemp();
    *   4) $tempfile = System::mktemp("-t /var/tmp prefix");
    *
    * prefix -> The string that will be prepended to the temp name
    *           (defaults to "tmp").
    * -d     -> A temporary dir will be created instead of a file.
    * -t     -> The target dir where the temporary (file|dir) will be created. If
    *           this param is missing by default the env vars TMP on Windows or
    *           TMPDIR in Unix will be used. If these vars are also missing
    *           c:\windows\temp or /tmp will be used.
    *
    * @param   string  $args  The arguments
    * @return  mixed   the full path of the created (file|dir) or false
    * @see System::tmpdir()
    * @static 
    * @access  public
    */
    function mktemp($args = null)
    {
        static $first_time = true;
        $opts = System::_parseArgs($args, 't:d');
        if (PEAR::isError($opts)) {
            return System::raiseError($opts);
        }
        foreach ($opts[0] as $opt) {
            if ($opt[0] == 'd') {
                $tmp_is_dir = true;
            } elseif ($opt[0] == 't') {
                $tmpdir = $opt[1];
            }
        }
        $prefix = (isset($opts[1][0])) ? $opts[1][0] : 'tmp';
        if (!isset($tmpdir)) {
            $tmpdir = System::tmpdir();
        }
        if (!System::mkDir(array('-p', $tmpdir))) {
            return false;
        }
        $tmp = tempnam($tmpdir, $prefix);
        if (isset($tmp_is_dir)) {
            unlink($tmp); // be careful possible race condition here
            if (!mkdir($tmp, 0700)) {
                return System::raiseError("Unable to create temporary directory $tmpdir");
            }
        }
        $GLOBALS['_System_temp_files'][] = $tmp;
        if ($first_time) {
            PEAR::registerShutdownFunc(array('System', '_removeTmpFiles'));
            $first_time = false;
        }
        return $tmp;
    }

    /**
    * Remove temporary files created my mkTemp. This function is executed
    * at script shutdown time
    *
    * @static 
    * @access private
    */
    function _removeTmpFiles()
    {
        if (count($GLOBALS['_System_temp_files'])) {
            $delete = $GLOBALS['_System_temp_files'];
            array_unshift($delete, '-r');
            System::rm($delete);
            $GLOBALS['_System_temp_files'] = array();
        }
    }

    /**
    * Get the path of the temporal directory set in the system
    * by looking in its environments variables.
    * Note: php.ini-recommended removes the "E" from the variables_order setting,
    * making unavaible the $_ENV array, that s why we do tests with _ENV
    *
    * @static 
    * @return string The temporary directory on the system
    */
    function tmpdir()
    {
        if (OS_WINDOWS) {
            if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) {
                return $var;
            }
            if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP')) {
                return $var;
            }
            if ($var = isset($_ENV['USERPROFILE']) ? $_ENV['USERPROFILE'] : getenv('USERPROFILE')) {
                return $var;
            }
            if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir')) {
                return $var;
            }
            return getenv('SystemRoot') . '\temp';
        }
        if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) {
            return $var;
        }
        return realpath('/tmp');
    }

    /**
    * The "which" command (show the full path of a command)
    *
    * @param string $program The command to search for
    * @param mixed  $fallback Value to return if $program is not found
    *
    * @return mixed A string with the full path or false if not found
    * @static 
    * @author Stig Bakken <ssb@php.net>
    */
    function which($program, $fallback = false)
    {
        // enforce API
        if (!is_string($program) || '' == $program) {
            return $fallback;
        }

        // full path given
        if (basename($program) != $program) {
            $path_elements[] = dirname($program);
            $program = basename($program);
        } else {
            // Honor safe mode
            if (!ini_get('safe_mode') || !$path = ini_get('safe_mode_exec_dir')) {
                $path = getenv('PATH');
                if (!$path) {
                    $path = getenv('Path'); // some OSes are just stupid enough to do this
                }
            }
            $path_elements = explode(PATH_SEPARATOR, $path);
        }

        if (OS_WINDOWS) {
            $exe_suffixes = getenv('PATHEXT')
                                ? explode(PATH_SEPARATOR, getenv('PATHEXT'))
                                : array('.exe','.bat','.cmd','.com');
            // allow passing a command.exe param
            if (strpos($program, '.') !== false) {
                array_unshift($exe_suffixes, '');
            }
            // is_executable() is not available on windows for PHP4
            $pear_is_executable = (function_exists('is_executable')) ? 'is_executable' : 'is_file';
        } else {
            $exe_suffixes = array('');
            $pear_is_executable = 'is_executable';
        }

        foreach ($exe_suffixes as $suff) {
            foreach ($path_elements as $dir) {
                $file = $dir . DIRECTORY_SEPARATOR . $program . $suff;
                if (@$pear_is_executable($file)) {
                    return $file;
                }
            }
        }
        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
    * @static 
    *
    */
    function find($args)
    {
        if (!is_array($args)) {
            $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
        }
        $dir = realpath(array_shift($args));
        if (!$dir) {
            return array();
        }
        $patterns = array();
        $depth = 0;
        $do_files = $do_dirs = true;
        $args_count = count($args);
        for ($i = 0; $i < $args_count; $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':
                    $name = preg_quote($args[$i+1], '#');
                    // our magic characters ? and * have just been escaped,
                    // so now we change the escaped versions to PCRE operators
                    $name = strtr($name, array('\?' => '.', '\*' => '.*'));
                    $patterns[] = '('.$name.')';
                    $i++;
                    break;
                case '-maxdepth':
                    $depth = $args[$i+1];
                    break;
            }
        }
        $path = System::_dirToStruct($dir, $depth, 0, true);
        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)) {
            $dsq = preg_quote(DIRECTORY_SEPARATOR, '#');
            $pattern = '#(^|'.$dsq.')'.implode('|', $patterns).'($|'.$dsq.')#';
            $ret = array();
            $files_count = count($files);
            for ($i = 0; $i < $files_count; $i++) {
                // only search in the part of the file below the current directory
                $filepart = basename($files[$i]);
                if (preg_match($pattern, $filepart)) {
                    $ret[] = $files[$i];
                }
            }
            return $ret;
        }
        return $files;
    }
}(цH߷ҦGBMB