home *** CD-ROM | disk | FTP | other *** search
/ Enter 2004 June / ENTER.ISO / files / xampp-win32-1.4.5-installer.exe / xampp / PackageFileManager.php < prev    next >
Encoding:
PHP Script  |  2004-03-24  |  62.7 KB  |  1,417 lines

  1. <?php
  2. //
  3. // +------------------------------------------------------------------------+
  4. // | PEAR :: Package File Manager                                           |
  5. // +------------------------------------------------------------------------+
  6. // | Copyright (c) 2003-2004 Gregory Beaver                                 |
  7. // | Email         cellog@phpdoc.org                                        |
  8. // +------------------------------------------------------------------------+
  9. // | This source file is subject to version 3.00 of the PHP License,        |
  10. // | that is available at http://www.php.net/license/3_0.txt.               |
  11. // | If you did not receive a copy of the PHP license and are unable to     |
  12. // | obtain it through the world-wide-web, please send a note to            |
  13. // | license@php.net so we can mail you a copy immediately.                 |
  14. // +------------------------------------------------------------------------+
  15. // | Portions of this code based on phpDocumentor                           |
  16. // | Web           http://www.phpdoc.org                                    |
  17. // | Mirror        http://phpdocu.sourceforge.net/                          |
  18. // +------------------------------------------------------------------------+
  19. // $Id: PackageFileManager.php,v 1.33 2004/02/07 18:03:59 cellog Exp $
  20. //
  21.  
  22. /**
  23.  * @package PEAR_PackageFileManager
  24.  */
  25. /**
  26.  * PEAR installer
  27.  */
  28. require_once 'PEAR/Common.php';
  29. /**#@+
  30.  * Error Codes
  31.  */
  32. define('PEAR_PACKAGEFILEMANAGER_NOSTATE', 1);
  33. define('PEAR_PACKAGEFILEMANAGER_NOVERSION', 2);
  34. define('PEAR_PACKAGEFILEMANAGER_NOPKGDIR', 3);
  35. define('PEAR_PACKAGEFILEMANAGER_NOBASEDIR', 4);
  36. define('PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND', 5);
  37. define('PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND_ANYWHERE', 6);
  38. define('PEAR_PACKAGEFILEMANAGER_CANTWRITE_PKGFILE', 7);
  39. define('PEAR_PACKAGEFILEMANAGER_DEST_UNWRITABLE', 8);
  40. define('PEAR_PACKAGEFILEMANAGER_CANTCOPY_PKGFILE', 9);
  41. define('PEAR_PACKAGEFILEMANAGER_CANTOPEN_TMPPKGFILE', 10);
  42. define('PEAR_PACKAGEFILEMANAGER_PATH_DOESNT_EXIST', 11);
  43. define('PEAR_PACKAGEFILEMANAGER_NOCVSENTRIES', 12);
  44. define('PEAR_PACKAGEFILEMANAGER_DIR_DOESNT_EXIST', 13);
  45. define('PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS', 14);
  46. define('PEAR_PACKAGEFILEMANAGER_NOPACKAGE', 15);
  47. define('PEAR_PACKAGEFILEMANAGER_WRONG_MROLE', 16);
  48. define('PEAR_PACKAGEFILEMANAGER_NOSUMMARY', 17);
  49. define('PEAR_PACKAGEFILEMANAGER_NODESC', 18);
  50. define('PEAR_PACKAGEFILEMANAGER_ADD_MAINTAINERS', 19);
  51. define('PEAR_PACKAGEFILEMANAGER_NO_FILES', 20);
  52. define('PEAR_PACKAGEFILEMANAGER_IGNORED_EVERYTHING', 21);
  53. define('PEAR_PACKAGEFILEMANAGER_INVALID_PACKAGE', 22);
  54. define('PEAR_PACKAGEFILEMANAGER_INVALID_REPLACETYPE', 23);
  55. define('PEAR_PACKAGEFILEMANAGER_INVALID_ROLE', 24);
  56. define('PEAR_PACKAGEFILEMANAGER_PHP_NOT_PACKAGE', 25);
  57. define('PEAR_PACKAGEFILEMANAGER_CVS_PACKAGED', 26);
  58. /**#@-*/
  59. /**
  60.  * Error messages
  61.  * @global array $GLOBALS['_PEAR_PACKAGEFILEMANAGER_ERRORS']
  62.  */
  63. $GLOBALS['_PEAR_PACKAGEFILEMANAGER_ERRORS'] =
  64. array(
  65.     'en' =>
  66.     array(
  67.         PEAR_PACKAGEFILEMANAGER_NOSTATE =>
  68.             'Release State (option \'state\') must by specified in PEAR_PackageFileManager setOptions (alpha|beta|stable)',
  69.         PEAR_PACKAGEFILEMANAGER_NOVERSION =>
  70.             'Release Version (option \'version\') must be specified in PEAR_PackageFileManager setOptions',
  71.         PEAR_PACKAGEFILEMANAGER_NOPKGDIR =>
  72.             'Package source base directory (option \'packagedirectory\') must be ' .
  73.             'specified in PEAR_PackageFileManager setOptions',
  74.         PEAR_PACKAGEFILEMANAGER_NOBASEDIR =>
  75.             'Package install base directory (option \'baseinstalldir\') must be ' .
  76.             'specified in PEAR_PackageFileManager setOptions',
  77.         PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND =>
  78.             'Base class "%s" can\'t be located',
  79.         PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND_ANYWHERE =>
  80.             'Base class "%s" can\'t be located in default or user-specified directories',
  81.         PEAR_PACKAGEFILEMANAGER_CANTWRITE_PKGFILE =>
  82.             'Failed to write package.xml file to destination directory',
  83.         PEAR_PACKAGEFILEMANAGER_DEST_UNWRITABLE =>
  84.             'Destination directory "%s" is unwritable',
  85.         PEAR_PACKAGEFILEMANAGER_CANTCOPY_PKGFILE =>
  86.             'Failed to copy package.xml.tmp file to package.xml',
  87.         PEAR_PACKAGEFILEMANAGER_CANTOPEN_TMPPKGFILE =>
  88.             'Failed to open temporary file "%s" for writing',
  89.         PEAR_PACKAGEFILEMANAGER_PATH_DOESNT_EXIST =>
  90.             'package.xml file path "%s" doesn\'t exist or isn\'t a directory',
  91.         PEAR_PACKAGEFILEMANAGER_NOCVSENTRIES =>
  92.             'Directory "%s" is not a CVS directory (it must have the CVS/Entries file)',
  93.         PEAR_PACKAGEFILEMANAGER_DIR_DOESNT_EXIST =>
  94.             'Package source base directory "%s" doesn\'t exist or isn\'t a directory',
  95.         PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS =>
  96.             'Run $managerclass->setOptions() before any other methods',
  97.         PEAR_PACKAGEFILEMANAGER_NOPACKAGE =>
  98.             'Package Name (option \'package\') must by specified in PEAR_PackageFileManager '.
  99.             'setOptions to create a new package.xml',
  100.         PEAR_PACKAGEFILEMANAGER_NOSUMMARY =>
  101.             'Package Summary (option \'summary\') must by specified in PEAR_PackageFileManager' .
  102.             ' setOptions to create a new package.xml',
  103.         PEAR_PACKAGEFILEMANAGER_NODESC =>
  104.             'Detailed Package Description (option \'description\') must be' .
  105.             ' specified in PEAR_PackageFileManager setOptions to create a new package.xml',
  106.         PEAR_PACKAGEFILEMANAGER_WRONG_MROLE =>
  107.             'Maintainer role must be one of "%s", was "%s"',
  108.         PEAR_PACKAGEFILEMANAGER_ADD_MAINTAINERS =>
  109.             'Add maintainers to a package before generating the package.xml',
  110.         PEAR_PACKAGEFILEMANAGER_NO_FILES =>
  111.             'No files found, check the path "%s"',
  112.         PEAR_PACKAGEFILEMANAGER_IGNORED_EVERYTHING =>
  113.             'No files left, check the path "%s" and ignore option "%s"',
  114.         PEAR_PACKAGEFILEMANAGER_INVALID_PACKAGE =>
  115.             'Package validation failed:%s%s',
  116.         PEAR_PACKAGEFILEMANAGER_INVALID_REPLACETYPE =>
  117.             'Replacement Type must be one of "%s", was passed "%s"',
  118.         PEAR_PACKAGEFILEMANAGER_INVALID_ROLE =>
  119.             'Invalid file role passed to addRole, must be one of "%s", was passed "%s"',
  120.         PEAR_PACKAGEFILEMANAGER_PHP_NOT_PACKAGE =>
  121.             'addDependency had PHP as a package, use type="php"',
  122.         PEAR_PACKAGEFILEMANAGER_CVS_PACKAGED =>
  123.             'path "%path%" contains CVS directory',
  124.         ),
  125.         // other language translations go here
  126.      );
  127. /**
  128.  * PEAR :: PackageFileManager updates the <filelist></filelist> section
  129.  * of a PEAR package.xml file to reflect the current files in
  130.  * preparation for a release.
  131.  *
  132.  * The PEAR_PackageFileManager class uses a plugin system to generate the
  133.  * list of files in a package.  This allows both standard recursive
  134.  * directory parsing (plugin type file) and more intelligent options
  135.  * such as the CVS browser {@link PEAR_PackageFileManager_Cvs}, which
  136.  * grabs all files in a local CVS checkout to create the list, ignoring
  137.  * any other local files.
  138.  *
  139.  * Other options include specifying roles for file extensions (all .php
  140.  * files are role="php", for example), roles for directories (all directories
  141.  * named "tests" are given role="tests" by default), and exceptions.
  142.  * Exceptions are specific pathnames with * and ? wildcards that match
  143.  * a default role, but should have another.  For example, perhaps
  144.  * a debug.tpl template would normally be data, but should be included
  145.  * in the docs role.  Along these lines, to exclude files entirely,
  146.  * use the ignore option.
  147.  *
  148.  * Required options for a release include version, baseinstalldir, state,
  149.  * and packagedirectory (the full path to the local location of the
  150.  * package to create a package.xml file for)
  151.  *
  152.  * Example usage:
  153.  * <code>
  154.  * <?php
  155.  * require_once('PEAR/PackageFileManager.php');
  156.  * $packagexml = new PEAR_PackageFileManager;
  157.  * $e = $packagexml->setOptions(
  158.  * array('baseinstalldir' => 'PhpDocumentor',
  159.  *  'version' => '1.2.1',
  160.  *  'packagedirectory' => 'C:/Web Pages/chiara/phpdoc2/',
  161.  *  'state' => 'stable',
  162.  *  'filelistgenerator' => 'cvs', // generate from cvs, use file for directory
  163.  *  'notes' => 'We\'ve implemented many new and exciting features',
  164.  *  'ignore' => array('TODO', 'tests/'), // ignore TODO, all files in tests/
  165.  *  'installexceptions' => array('phpdoc' => '/*'), // baseinstalldir ="/" for phpdoc
  166.  *  'dir_roles' => array('tutorials' => 'doc'),
  167.  *  'exceptions' => array('README' => 'doc', // README would be data, now is doc
  168.  *                        'PHPLICENSE.txt' => 'doc'))); // same for the license
  169.  * if (PEAR::isError($e)) {
  170.  *     echo $e->getMessage();
  171.  *     die();
  172.  * }
  173.  * $e = $test->addPlatformException('pear-phpdoc.bat', 'windows');
  174.  * if (PEAR::isError($e)) {
  175.  *     echo $e->getMessage();
  176.  *     exit;
  177.  * }
  178.  * $packagexml->addRole('pkg', 'doc'); // add a new role mapping
  179.  * if (PEAR::isError($e)) {
  180.  *     echo $e->getMessage();
  181.  *     exit;
  182.  * }
  183.  * // replace @PHP-BIN@ in this file with the path to php executable!  pretty neat
  184.  * $e = $test->addReplacement('pear-phpdoc', 'pear-config', '@PHP-BIN@', 'php_bin');
  185.  * if (PEAR::isError($e)) {
  186.  *     echo $e->getMessage();
  187.  *     exit;
  188.  * }
  189.  * $e = $test->addReplacement('pear-phpdoc.bat', 'pear-config', '@PHP-BIN@', 'php_bin');
  190.  * if (PEAR::isError($e)) {
  191.  *     echo $e->getMessage();
  192.  *     exit;
  193.  * }
  194.  * // note use of {@link debugPackageFile()} - this is VERY important
  195.  * if (isset($_GET['make']) || $_SERVER['argv'][2] == 'make') {
  196.  *     $e = $packagexml->writePackageFile();
  197.  * } else {
  198.  *     $e = $packagexml->debugPackageFile();
  199.  * }
  200.  * if (PEAR::isError($e)) {
  201.  *     echo $e->getMessage();
  202.  *     die();
  203.  * }
  204.  * ?>
  205.  * </code>
  206.  * 
  207.  * In addition, a package.xml file can now be generated from
  208.  * scratch, with the usage of new options package, summary, description, and
  209.  * the use of the {@link addMaintainer()} method
  210.  * @package PEAR_PackageFileManager
  211.  */
  212. class PEAR_PackageFileManager
  213. {
  214.     /**
  215.      * Format: array(array(regexp-ready string to search for whole path,
  216.      * regexp-ready string to search for basename of ignore strings),...)
  217.      * @var false|array
  218.      * @access private
  219.      */
  220.     var $_ignore = false;
  221.     
  222.     /**
  223.      * Contents of the package.xml file
  224.      * @var string
  225.      * @access private
  226.      */
  227.     var $_packageXml = false;
  228.     
  229.     /**
  230.      * Contents of the original package.xml file, if any
  231.      * @var string
  232.      * @access private
  233.      */
  234.     var $_oldPackageXml = false;
  235.     
  236.     /**
  237.      * @access private
  238.      * @var PEAR_Common
  239.      */
  240.     var $_pear;
  241.     
  242.     /**
  243.      * @access private
  244.      * @var array
  245.      */
  246.     var $_warningStack = array();
  247.     
  248.     /**
  249.      * @access private
  250.      * @var string
  251.      */
  252.     var $_options = array(
  253.                       'packagefile' => 'package.xml',
  254.                       'doctype' => 'http://pear.php.net/dtd/package-1.0',
  255.                       'filelistgenerator' => 'file',
  256.                       'license' => 'PHP License',
  257.                       'changelogoldtonew' => true,
  258.                       'roles' =>
  259.                         array(
  260.                             'php' => 'php',
  261.                             'html' => 'doc',
  262.                             '*' => 'data',
  263.                              ),
  264.                       'dir_roles' =>
  265.                         array(
  266.                             'docs' => 'doc',
  267.                             'examples' => 'doc',
  268.                             'tests' => 'test',
  269.                              ),
  270.                       'exceptions' => array(),
  271.                       'installexceptions' => array(),
  272.                       'installas' => array(),
  273.                       'platformexceptions' => array(),
  274.                       'scriptphaseexceptions' => array(),
  275.                       'ignore' => array(),
  276.                       'include' => false,
  277.                       'deps' => false,
  278.                       'maintainers' => false,
  279.                       'notes' => '',
  280.                       'changelognotes' => false,
  281.                       'outputdirectory' => false,
  282.                       'pathtopackagefile' => false,
  283.                       'lang' => 'en',
  284.                       'configure_options' => array(),
  285.                       'replacements' => array(),
  286.                       'pearcommonclass' => false,
  287.                       'simpleoutput' => false,
  288.                       'addhiddenfiles' => false,
  289.                       );
  290.     
  291.     /**
  292.      * Does nothing, use setOptions
  293.      *
  294.      * The constructor is not used in order to be able to
  295.      * return a PEAR_Error from setOptions
  296.      * @see setOptions()
  297.      */
  298.     function PEAR_PackageFileManager()
  299.     {
  300.     }
  301.     
  302.     /**
  303.      * Set package.xml generation options
  304.      *
  305.      * The options array is indexed as follows:
  306.      * <code>
  307.      * $options = array('option_name' => <optionvalue>);
  308.      * </code>
  309.      *
  310.      * The documentation below simplifies this description through
  311.      * the use of option_name without quotes
  312.      *
  313.      * Configuration options:
  314.      * - lang: lang controls the language in which error messages are
  315.      *         displayed.  There are currently only English error messages,
  316.      *         but any contributed will be added over time.<br>
  317.      *         Possible values: en (default)
  318.      * - packagefile: the name of the packagefile, defaults to package.xml
  319.      * - pathtopackagefile: the path to an existing package file to read in,
  320.      *                      if different from the packagedirectory
  321.      * - packagedirectory: the path to the base directory of the package.  For
  322.      *                     package PEAR_PackageFileManager, this path is
  323.      *                     /path/to/pearcvs/pear/PEAR_PackageFileManager where
  324.      *                     /path/to/pearcvs is a local path on your hard drive
  325.      * - outputdirectory: the path in which to place the generated package.xml
  326.      *                    by default, this is ignored, and the package.xml is
  327.      *                    created in the packagedirectory
  328.      * - filelistgenerator: the <filelist> section plugin which will be used.
  329.      *                      In this release, there are two generator plugins,
  330.      *                      file and cvs.  For details, see the docs for these
  331.      *                      plugins
  332.      * - usergeneratordir: For advanced users.  If you write your own filelist
  333.      *                     generator plugin, use this option to tell
  334.      *                     PEAR_PackageFileManager where to find the file that
  335.      *                     contains it.  If the plugin is named foo, the class
  336.      *                     must be named PEAR_PackageFileManager_Foo
  337.      *                     no matter where it is located.  By default, the Foo
  338.      *                     plugin is located in PEAR/PackageFileManager/Foo.php.
  339.      *                     If you pass /path/to/foo in this option, setOptions
  340.      *                     will look for PEAR_PackageFileManager_Foo in
  341.      *                     /path/to/foo/Foo.php
  342.      * - doctype: Specifies the DTD of the package.xml file.  Default is
  343.      *            http://pear.php.net/dtd/package-1.0
  344.      * - pearcommonclass: Specifies the name of the class to instantiate, default
  345.      *                    is PEAR_Common, but users can override this with a custom
  346.      *                    class that implements PEAR_Common's method interface
  347.      * - changelogoldtonew: True if the ChangeLog should list from oldest entry to
  348.      *                      newest.  Set to false if you would like new entries first
  349.      * - simpleoutput: True if the package.xml should not contain md5sum or <provides />
  350.      *                 for readability
  351.      * - addhiddenfiles: True if you wish to add hidden files/directories that begin with .
  352.      *                   like .bashrc.  This is only used by the File generator.  The CVS
  353.      *                   generator will use all files in CVS regardless of format
  354.      *
  355.      * package.xml simple options:
  356.      * - baseinstalldir: The base directory to install this package in.  For
  357.      *                   package PEAR_PackageFileManager, this is "PEAR", for
  358.      *                   package PEAR, this is "/"
  359.      * - license: The license this release is released under.  Default is
  360.      *            PHP License if left unspecified
  361.      * - notes: Release notes, any text describing what makes this release unique
  362.      * - changelognotes: notes for the changelog, this should be more detailed than
  363.      *                   the release notes.  By default, PEAR_PackageFileManager uses
  364.      *                   the notes option for the changelog as well
  365.      * - version: The version number for this release.  Remember the convention for
  366.      *            numbering: initial alpha is between 0 and 1, add b<beta number> for
  367.      *            beta as in 1.0b1, the integer portion of the version should specify
  368.      *            backwards compatibility, as in 1.1 is backwards compatible with 1.0,
  369.      *            but 2.0 is not backwards compatible with 1.10.  Also note that 1.10
  370.      *            is a greater release version than 1.1 (think of it as "one point ten"
  371.      *            and "one point one").  Bugfix releases should be a third decimal as in
  372.      *            1.0.1, 1.0.2
  373.      * - package: [optional] Package name.  Use this to create a new package.xml, or
  374.      *            overwrite an existing one from another package used as a template
  375.      * - summary: [optional] Summary of package purpose
  376.      * - description: [optional] Description of package purpose.  Note that the above
  377.      *                three options are not optional when creating a new package.xml
  378.      *                from scratch
  379.      *
  380.      * <b>WARNING</b>: all complex options that require a file path are case-sensitive
  381.      *
  382.      * package.xml complex options:
  383.      * - ignore: an array of filenames, directory names, or wildcard expressions specifying
  384.      *           files to exclude entirely from the package.xml.  Wildcards are operating system
  385.      *           wildcards * and ?.  file*foo.php will exclude filefoo.php, fileabrfoo.php and
  386.      *           filewho_is_thisfoo.php.  file?foo.php will exclude fileafoo.php and will not
  387.      *           exclude fileaafoo.php.  test/ will exclude all directories and subdirectories of
  388.      *           ANY directory named test encountered in directory parsing.  *test* will exclude
  389.      *           all files and directories that contain test in their name
  390.      * - include: an array of filenames, directory names, or wildcard expressions specifying
  391.      *            files to include in the listing.  All other files will be ignored.
  392.      *            Wildcards are in the same format as ignore
  393.      * - roles: this is an array mapping file extension to install role.  This
  394.      *          specifies default behavior that can be overridden by the exceptions
  395.      *          option and dir_roles option.  use {@link addRole()} to add a new
  396.      *          role to the pre-existing array
  397.      * - dir_roles: this is an array mapping directory name to install role.  All
  398.      *              files in a directory whose name matches the directory will be
  399.      *              given the install role specified.  Single files can be excluded
  400.      *              from this using the exceptions option.  The directory should be
  401.      *              a relative path from the baseinstalldir, or "/" for the baseinstalldir
  402.      * - exceptions: specify file role for specific files.  This array maps all files
  403.      *               matching the exact name of a file to a role as in "file.ext" => "role"
  404.      * - deps: dependency array.  Pass in an empty array to clear all dependencies, and use
  405.      *         {@link addDependency()} to add new ones/replace existing ones
  406.      * - maintainers: maintainers array.  Pass in an empty array to clear all maintainers, and
  407.      *                use {@link addMaintainer()} to add a new maintainer/replace existing maintainer
  408.      * - installexceptions: array mapping of specific filenames to baseinstalldir values.  Use
  409.      *                      this to force the installation of a file into another directory,
  410.      *                      such as forcing a script to be in the root scripts directory so that
  411.      *                      it will be in the path.  The filename must be a relative path to the
  412.      *                      packagedirectory
  413.      * - platformexceptions: array mapping of specific filenames to the platform they should be
  414.      *                       installed on.  Use this to specify unix-only files or windows-only
  415.      *                       files.  The format of the platform string must be
  416.      *                       OS-version-cpu-extra if any more specific information is needed,
  417.      *                       and the OS must be in lower case as in "windows."  The match is
  418.      *                       performed using a regular expression, but uses * and ? wildcards
  419.      *                       instead of .* and .?.  Note that hpux/aix/irix/linux are all
  420.      *                       exclusive.  To select non-windows, use (*ix|*ux)
  421.      * - scriptphaseexceptions: array mapping of scripts to their install phase.  This can be
  422.      *                          one of: pre-install, post-install, pre-uninstall, post-uninstall,
  423.      *                          pre-build, post-build, pre-setup, or post-setup
  424.      * - installas: array mapping of specific filenames to the filename they should be installed as.
  425.      *              Use this to specify new filenames for files that should be installed.  This will
  426.      *              often be used in conjunction with platformexceptions if there are two files for
  427.      *              different OSes that must have the same name when installed.
  428.      * - replacements: array mapping of specific filenames to complex text search-and-replace that
  429.      *                 should be performed upon install.  The format is:
  430.      *   <pre>
  431.      *   filename => array('type' => php-const|pear-config|package-info
  432.      *                     'from' => text in file
  433.      *                     'to' => name of variable)
  434.      *   </pre>
  435.      *                 if type is php-const, then 'to' must be the name of a PHP Constant.
  436.      *                 If type is pear-config, then 'to' must be the name of a PEAR config
  437.      *                 variable accessible through a PEAR_Config class->get() method.  If
  438.      *                 type is package-info, then 'to' must be the name of a section from
  439.      *                 the package.xml file used to install this file.
  440.      * - configure_options: array specifies build options for PECL packages (you should probably
  441.      *                      use PECL_Gen instead, but it's here for completeness)
  442.      * @see PEAR_PackageFileManager_File
  443.      * @see PEAR_PackageFileManager_CVS
  444.      * @return void|PEAR_Error
  445.      * @throws PEAR_PACKAGEFILEMANAGER_NOSTATE
  446.      * @throws PEAR_PACKAGEFILEMANAGER_NOVERSION
  447.      * @throws PEAR_PACKAGEFILEMANAGER_NOPKGDIR
  448.      * @throws PEAR_PACKAGEFILEMANAGER_NOBASEDIR
  449.      * @throws PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND_ANYWHERE
  450.      * @throws PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND
  451.      * @param array
  452.      */
  453.     function setOptions($options = array())
  454.     {
  455.         if (!isset($options['state'])) {
  456.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NOSTATE);
  457.         }
  458.         if (!isset($options['version'])) {
  459.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NOVERSION);
  460.         }
  461.         if (!isset($options['packagedirectory'])) {
  462.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NOPKGDIR);
  463.         } else {
  464.             $options['packagedirectory'] = str_replace(DIRECTORY_SEPARATOR,
  465.                                                      '/',
  466.                                                      realpath($options['packagedirectory']));
  467.             if ($options['packagedirectory']{strlen($options['packagedirectory']) - 1} != '/') {
  468.                 $options['packagedirectory'] .= '/';
  469.             }
  470.         }
  471.         if (isset($options['pathtopackagefile'])) {
  472.             $options['pathtopackagefile'] = str_replace(DIRECTORY_SEPARATOR,
  473.                                                      '/',
  474.                                                      realpath($options['pathtopackagefile']));
  475.             if ($options['pathtopackagefile']{strlen($options['pathtopackagefile']) - 1} != '/') {
  476.                 $options['pathtopackagefile'] .= '/';
  477.             }
  478.         }
  479.         if (!isset($options['baseinstalldir'])) {
  480.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NOBASEDIR);
  481.         }
  482.         $this->_options = array_merge($this->_options, $options);
  483.         
  484.         if (!class_exists($this->_options['pearcommonclass'])) {
  485.             if ($this->_options['simpleoutput']) {
  486.                 require_once 'PEAR/PackageFileManager/XMLOutput.php';
  487.                 $this->_options['pearcommonclass'] = 'PEAR_PackageFileManager_XMLOutput';
  488.             } else {
  489.                 $this->_options['pearcommonclass'] = 'PEAR_Common';
  490.             }
  491.         }
  492.         $path = ($this->_options['pathtopackagefile'] ?
  493.                     $this->_options['pathtopackagefile'] : $this->_options['packagedirectory']);
  494.         $this->_options['filelistgenerator'] = ucfirst(strtolower($this->_options['filelistgenerator']));
  495.         if (PEAR::isError($res = $this->_getExistingPackageXML($path, $this->_options['packagefile']))) {
  496.             return $res;
  497.         }
  498.         if (!class_exists('PEAR_PackageFileManager_' . $this->_options['filelistgenerator'])) {
  499.             // attempt to load the interface from the standard PEAR location
  500.             if ($this->isIncludeable('PEAR/PackageFileManager/' . $this->_options['filelistgenerator'] . '.php')) {
  501.                 include_once('PEAR/PackageFileManager/' . $this->_options['filelistgenerator'] . '.php');
  502.             } elseif (isset($this->_options['usergeneratordir'])) {
  503.                 // attempt to load from a user-specified directory
  504.                 if (is_dir(realpath($this->_options['usergeneratordir']))) {
  505.                     $this->_options['usergeneratordir'] =
  506.                         str_replace(DIRECTORY_SEPARATOR,
  507.                                     '/',
  508.                                     realpath($this->_options['usergeneratordir']));
  509.                     if ($this->_options['usergeneratordir']{strlen($this->_options['usergeneratordir']) - 1} != '/') {
  510.                         $this->_options['usergeneratordir'] .= '/';
  511.                     }
  512.                 } else {
  513.                     $this->_options['usergeneratordir'] = '////';
  514.                 }
  515.                 if (file_exists($this->_options['usergeneratordir'] .
  516.                       $this->_options['filelistgenerator'] . '.php') &&
  517.                       is_readable($this->_options['usergeneratordir'] .
  518.                       $this->_options['filelistgenerator'] . '.php')) {
  519.                     include_once($this->_options['usergeneratordir'] .
  520.                         $this->_options['filelistgenerator'] . '.php');
  521.                 }
  522.                 if (!class_exists('PEAR_PackageFileManager_' . $this->_options['filelistgenerator'])) {
  523.                     return $this->raiseError(PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND_ANYWHERE,
  524.                             'PEAR_PackageFileManager_' . $this->_options['filelistgenerator']);
  525.                 }
  526.             } else {
  527.                 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND,
  528.                         'PEAR_PackageFileManager_' . $this->_options['filelistgenerator']);
  529.             }
  530.         }
  531.     }
  532.     
  533.     /**
  534.      * Add an extension/role mapping to the role mapping option
  535.      *
  536.      * Roles influence both where a file is installed and how it is installed.
  537.      * Files with role="data" are in a completely different directory hierarchy
  538.      * from the program files of role="php"
  539.      * 
  540.      * In PEAR 1.3b2, these roles are
  541.      * - php (most common)
  542.      * - data
  543.      * - doc
  544.      * - test
  545.      * - script (gives the file an executable attribute)
  546.      * - src
  547.      * @param string file extension
  548.      * @param string role
  549.      * @throws PEAR_PACKAGEFILEMANAGER_INVALID_ROLE
  550.      */
  551.     function addRole($extension, $role)
  552.     {
  553.         $roles = call_user_func(array($this->_options['pearcommonclass'], 'getfileroles'));
  554.         if (!in_array($role, $roles)) {
  555.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_INVALID_ROLE, implode($roles, ', '), $role);
  556.         }
  557.         $this->_options['roles'][$extension] = $role;
  558.     }
  559.     
  560.     /**
  561.      * Add an install-time platform conditional install for a file
  562.      *
  563.      * The format of the platform string must be
  564.      * OS-version-cpu-extra if any more specific information is needed,
  565.      * and the OS must be in lower case as in "windows."  The match is
  566.      * performed using a regular expression, but uses * and ? wildcards
  567.      * instead of .* and .?.  Note that hpux/aix/irix/linux are all
  568.      * exclusive.  To select non-windows, use (*ix|*ux)
  569.      *
  570.      * This information is based on eyeing the source for OS/Guess.php, so
  571.      * if you are unsure of what to do, read that file.
  572.      * @param string relative path of file (relative to packagedirectory option)
  573.      * @param string platform descriptor string
  574.      */
  575.     function addPlatformException($path, $platform)
  576.     {
  577.         if (!isset($this->_options['platformexceptions'])) {
  578.             $this->_options['platformexceptions'] = array();
  579.         }
  580.         $this->_options['platformexceptions'][$path] = $platform;
  581.     }
  582.     
  583.     /**
  584.      * Add a replacement option for a file
  585.      *
  586.      * This sets an install-time complex search-and-replace function
  587.      * allowing the setting of platform-specific variables in an
  588.      * installed file.
  589.      *
  590.      * if $type is php-const, then $to must be the name of a PHP Constant.
  591.      * If $type is pear-config, then $to must be the name of a PEAR config
  592.      * variable accessible through a {@link PEAR_Config::get()} method.  If
  593.      * type is package-info, then $to must be the name of a section from
  594.      * the package.xml file used to install this file.
  595.      * @param string relative path of file (relative to packagedirectory option)
  596.      * @param string variable type, either php-const, pear-config or package-info
  597.      * @param string text to replace in the source file
  598.      * @param string variable name to use for replacement
  599.      * @throws PEAR_PACKAGEFILEMANAGER_INVALID_REPLACETYPE
  600.      */
  601.     function addReplacement($path, $type, $from, $to)
  602.     {
  603.         if (!isset($this->_options['replacements'])) {
  604.             $this->_options['replacements'] = array();
  605.         }
  606.         $types = call_user_func(array($this->_options['pearcommonclass'], 'getreplacementtypes'));
  607.         if (!in_array($type, $types)) {
  608.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_INVALID_REPLACETYPE,
  609.                 implode($types, ', '), $type);
  610.         }
  611.         $this->_options['replacements'][$path][] = array('type' => $type, 'from' => $from, 'to' => $to);
  612.     }
  613.     
  614.     /**
  615.      * Add a maintainer to the list of maintainers.
  616.      *
  617.      * Every maintainer must have a valid account at pear.php.net.  The
  618.      * first parameter is the account name (for instance, cellog is the
  619.      * handle for Greg Beaver at pear.php.net).  Every maintainer has
  620.      * one of four possible roles:
  621.      * - lead: the primary maintainer
  622.      * - developer: an important developer on the project
  623.      * - contributor: self-explanatory
  624.      * - helper: ditto
  625.      *
  626.      * Finally, specify the name and email of the maintainer
  627.      * @param string username on pear.php.net of maintainer
  628.      * @param lead|developer|contributor|helper role of maintainer
  629.      * @param string full name of maintainer
  630.      * @param string email address of maintainer
  631.      */
  632.     function addMaintainer($handle, $role, $name, $email)
  633.     {
  634.         if (!$this->_packageXml) {
  635.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS);
  636.         }
  637.         if (!in_array($role, $GLOBALS['_PEAR_Common_maintainer_roles'])) {
  638.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_WRONG_MROLE,
  639.                 implode(', ', call_user_func(array($this->_options['pearcommonclass'],
  640.                     'getUserRoles'))),
  641.                 $role);
  642.         }
  643.         if (!isset($this->_packageXml['maintainers'])) {
  644.             $this->_packageXml['maintainers'] = array();
  645.         }
  646.         $found = false;
  647.         foreach($this->_packageXml['maintainers'] as $index => $maintainer) {
  648.             if ($maintainer['handle'] == $handle) {
  649.                 $found = $index;
  650.                 break;
  651.             }
  652.         }
  653.         $maintainer =
  654.             array('handle' => $handle, 'role' => $role, 'name' => $name, 'email' => $email);
  655.         if ($found !== false) {
  656.             $this->_packageXml['maintainers'][$found] = $maintainer;
  657.         } else {
  658.             $this->_packageXml['maintainers'][] = $maintainer;
  659.         }
  660.     }
  661.     
  662.     /**
  663.      * Add an install-time configuration option for building of source
  664.      *
  665.      * This option is only useful to PECL projects that are built upon
  666.      * installation
  667.      * @param string name of the option
  668.      * @param string prompt to display to the user
  669.      * @param string default value
  670.      * @throws PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS
  671.      * @return void|PEAR_Error
  672.      */
  673.     function addConfigureOption($name, $prompt, $default = null)
  674.     {
  675.         if (!$this->_packageXml) {
  676.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS);
  677.         }
  678.         if (!isset($this->_packageXml['configure_options'])) {
  679.             $this->_packageXml['configure_options'] = array();
  680.         }
  681.         $found = false;
  682.         foreach($this->_packageXml['configure_options'] as $index => $option) {
  683.             if ($option['name'] == $name) {
  684.                 $found = $index;
  685.                 break;
  686.             }
  687.         }
  688.         $option = array('name' => $name, 'prompt' => $prompt);
  689.         if (isset($default)) {
  690.             $option['default'] = $default;
  691.         }
  692.         if ($found !== false) {
  693.             $this->_packageXml['configure_options'][$found] = $option;
  694.         } else {
  695.             $this->_packageXml['configure_options'][] = $option;
  696.         }
  697.     }
  698.     
  699.     /**
  700.      * Add a dependency on another package, or an extension/php
  701.      *
  702.      * This will overwrite an existing dependency if it is found.  In
  703.      * other words, if a dependency on PHP 4.1.0 exists, and
  704.      * addDependency('php', '4.3.0', 'ge', 'php') is called, the existing
  705.      * dependency on PHP 4.1.0 will be overwritten with the new one on PHP 4.3.0
  706.      * @param string Dependency element name
  707.      * @param string Dependency version
  708.      * @param string A specific operator for the version, this can be one of:
  709.      *   'has', 'not', 'lt', 'le', 'eq', 'ne', 'ge', or 'gt'
  710.      * @param string Dependency type.  This can be one of:
  711.      *   'pkg', 'ext', 'php', 'prog', 'os', 'sapi', or 'zend'
  712.      * @param boolean true if dependency is optional
  713.      * @throws PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS
  714.      * @throws PEAR_PACKAGEFILEMANAGER_PHP_NOT_PACKAGE
  715.      * @return void|PEAR_Error
  716.      */
  717.     function addDependency($name, $version = false, $operator = 'ge', $type = 'pkg', $optional = false)
  718.     {
  719.         if (!$this->_packageXml) {
  720.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS);
  721.         }
  722.         if ((strtolower($name) == 'php') && (strtolower($type) == 'pkg')) {
  723.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_PHP_NOT_PACKAGE);
  724.         }
  725.         if (!isset($this->_packageXml['release_deps'])) {
  726.             $this->_packageXml['release_deps'] = array();
  727.         }
  728.         $found = false;
  729.         foreach($this->_packageXml['release_deps'] as $index => $dep) {
  730.             if ($type == 'php') {
  731.                 if ($dep['type'] == 'php') {
  732.                     $found = $index;
  733.                     break;
  734.                 }
  735.             } else {
  736.                 if (isset($dep['name']) && $dep['name'] == $name && $dep['type'] == $type) {
  737.                     $found = $index;
  738.                     break;
  739.                 }
  740.             }
  741.         }
  742.         $dep =
  743.             array(
  744.                 'name' => $name,
  745.                 'type' => $type);
  746.         if ($type == 'php') {
  747.             unset($dep['name']);
  748.         }
  749.         if ($operator) {
  750.             $dep['rel'] = $operator;
  751.             if ($dep['rel'] != 'has' && $version) {
  752.                 $dep['version'] = $version;
  753.             }
  754.         }
  755.         
  756.         if ($optional) {
  757.             $dep['optional'] = 'yes';
  758.         } else {
  759.             $dep['optional'] = 'no';
  760.         }
  761.  
  762.         if ($found !== false) {
  763.             $this->_packageXml['release_deps'][$found] = $dep; // overwrite existing dependency
  764.         } else {
  765.             $this->_packageXml['release_deps'][] = $dep; // add new dependency
  766.         }
  767.     }
  768.  
  769.     /**
  770.      * Writes the package.xml file out with the newly created <release></release> tag
  771.      *
  772.      * ALWAYS use {@link debugPackageFile} to verify that output is correct before
  773.      * overwriting your package.xml
  774.      * @param boolean null if no debugging, true if web interface, false if command-line
  775.      * @throws PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS
  776.      * @throws PEAR_PACKAGEFILEMANAGER_ADD_MAINTAINERS
  777.      * @throws PEAR_PACKAGEFILEMANAGER_CANTWRITE_PKGFILE
  778.      * @throws PEAR_PACKAGEFILEMANAGER_CANTCOPY_PKGFILE
  779.      * @throws PEAR_PACKAGEFILEMANAGER_CANTOPEN_TMPPKGFILE
  780.      * @throws PEAR_PACKAGEFILEMANAGER_DEST_UNWRITABLE
  781.      * @return void|PEAR_Error
  782.      */
  783.     function writePackageFile($debuginterface = null)
  784.     {
  785.         if (!$this->_packageXml) {
  786.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS);
  787.         }
  788.         if (!isset($this->_packageXml['maintainers']) || empty($this->_packageXml['maintainers'])) {
  789.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_ADD_MAINTAINERS);
  790.         }
  791.         extract($this->_options);
  792.         $date = date('Y-m-d');
  793.         if (isset($package)) {
  794.             $this->_packageXml['package'] = $package;
  795.         }
  796.         if (isset($summary)) {
  797.             $this->_packageXml['summary'] = $summary;
  798.         }
  799.         if (isset($description)) {
  800.             $this->_packageXml['description'] = $description;
  801.         }
  802.         $this->_packageXml['release_date'] = $date;
  803.         $this->_packageXml['version'] = $version;
  804.         $this->_packageXml['release_license'] = $license;
  805.         $this->_packageXml['release_state'] = $state;
  806.         $this->_packageXml['release_notes'] = $notes;
  807.         $PEAR_Common = $this->_options['pearcommonclass'];
  808.         $this->_pear = new $PEAR_Common;
  809.         $this->_packageXml['filelist'] = $this->_getFileList();
  810.         $warnings = $this->getWarnings();
  811.         if (count($warnings)) {
  812.             $nl = (isset($debuginterface) && $debuginterface ? '<br />' : "\n");
  813.             foreach($warnings as $errmsg) {
  814.                 echo 'WARNING: ' . $errmsg['message'] . $nl;
  815.             }
  816.         }
  817.         if (PEAR::isError($this->_packageXml['filelist'])) {
  818.             return $this->_packageXml['filelist'];
  819.         }
  820.         if (isset($this->_pear->pkginfo['provides'])) {
  821.             $this->_packageXml['provides'] = $this->_pear->pkginfo['provides'];
  822.         }
  823.         if ($this->_options['simpleoutput']) {
  824.             unset($this->_packageXml['provides']);
  825.         }
  826.         $this->_packageXml['release_deps'] = $this->_getDependencies();
  827.         $this->_updateChangeLog();
  828.         
  829.         $common = &$this->_pear;
  830.         $warnings = $errors = array();
  831.         $packagexml = $common->xmlFromInfo($this->_packageXml);
  832.         $common->validatePackageInfo($packagexml, $warnings, $errors, $this->_options['packagedirectory']);
  833.         if (count($errors)) {
  834.             $ret = '';
  835.             $nl = (isset($debuginterface) && $debuginterface ? '<br />' : "\n");
  836.             foreach($errors as $errmsg) {
  837.                 $ret .= $errmsg . $nl;
  838.             }
  839.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_INVALID_PACKAGE, $nl, $ret);
  840.         }
  841.         if (count($warnings)) {
  842.             $nl = (isset($debuginterface) && $debuginterface ? '<br />' : "\n");
  843.             foreach($warnings as $errmsg) {
  844.                 echo $errmsg . $nl;
  845.             }
  846.         }
  847.         if (!strpos($packagexml, '<!DOCTYPE')) {
  848.             // hack to fix pear
  849.             $packagexml = str_replace('<package version="1.0">',
  850.                 '<!DOCTYPE package SYSTEM "' . $this->_options['doctype'] .
  851.                 "\">\n<package version=\"1.0\">",
  852.                 $packagexml);
  853.         }
  854.         if (isset($debuginterface)) {
  855.             if ($debuginterface) {
  856.                 echo '<pre>' . htmlentities($packagexml) . '</pre>';
  857.             } else {
  858.                 echo $packagexml;
  859.             }
  860.             return true;
  861.         }
  862.         $outputdir = ($this->_options['outputdirectory'] ?
  863.                         $this->_options['outputdirectory'] : $this->_options['packagedirectory']);
  864.         if ((file_exists($outputdir . $this->_options['packagefile']) &&
  865.                 is_writable($outputdir . $this->_options['packagefile']))
  866.                 ||
  867.                 @touch($outputdir . $this->_options['packagefile'])) {
  868.             if ($fp = @fopen($outputdir . $this->_options['packagefile'] . '.tmp', "w")) {
  869.                 $written = @fwrite($fp, $packagexml);
  870.                 @fclose($fp);
  871.                 if ($written === false) {
  872.                     return $this->raiseError(PEAR_PACKAGEFILEMANAGER_CANTWRITE_PKGFILE);
  873.                 }
  874.                 if (!@copy($outputdir . $this->_options['packagefile'] . '.tmp',
  875.                         $outputdir . $this->_options['packagefile'])) {
  876.                     return $this->raiseError(PEAR_PACKAGEFILEMANAGER_CANTCOPY_PKGFILE);
  877.                 } else {
  878.                     @unlink($outputdir . $this->_options['packagefile'] . '.tmp');
  879.                     return true;
  880.                 }
  881.             } else {
  882.                 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_CANTOPEN_TMPPKGFILE,
  883.                     $outputdir . $this->_options['packagefile'] . '.tmp');
  884.             }
  885.         } else {
  886.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_DEST_UNWRITABLE, $outputdir);
  887.         }
  888.     }
  889.     
  890.     /**
  891.      * ALWAYS use this to test output before overwriting your package.xml!!
  892.      *
  893.      * This method instructs writePackageFile() to simply print the package.xml
  894.      * to output, either command-line or web-friendly (this is automatic
  895.      * based on the existence of $_SERVER['PATH_TRANSLATED']
  896.      * @uses writePackageFile() calls with the debug parameter set based on
  897.      *       whether it is called from the command-line or web interface
  898.      */
  899.     function debugPackageFile()
  900.     {
  901.         $webinterface = isset($_SERVER['PATH_TRANSLATED']);
  902.         return $this->writePackageFile($webinterface);
  903.     }
  904.     
  905.     /**
  906.      * Store a warning on the warning stack
  907.      */
  908.     function pushWarning($code, $info)
  909.     {
  910.         $this->_warningStack[] = array('code' => $code,
  911.                                        'message' => $this->_getMessage($code, $info));
  912.     }
  913.     
  914.     /**
  915.      * Retrieve the list of warnings
  916.      * @return array
  917.      */
  918.     function getWarnings()
  919.     {
  920.         $a = $this->_warningStack;
  921.         $this->_warningStack = array();
  922.         return $a;
  923.     }
  924.     
  925.     /**
  926.      * Retrieve an error message from a code
  927.      * @access private
  928.      * @return string Error message
  929.      */
  930.     function _getMessage($code, $info)
  931.     {
  932.         $msg = $GLOBALS['_PEAR_PACKAGEFILEMANAGER_ERRORS'][$this->_options['lang']][$code];
  933.         foreach ($info as $name => $value) {
  934.             $msg = str_replace('%' . $name . '%', $value, $msg);
  935.         }
  936.         return $msg;
  937.     }
  938.     
  939.     /**
  940.      * Utility function to shorten error generation code
  941.      *
  942.      * {@source}
  943.      * @return PEAR_Error
  944.      * @static
  945.      */
  946.     function raiseError($code, $i1 = '', $i2 = '')
  947.     {
  948.         return PEAR::raiseError('PEAR_PackageFileManager Error: ' .
  949.                     sprintf($GLOBALS['_PEAR_PACKAGEFILEMANAGER_ERRORS'][$this->_options['lang']][$code],
  950.                     $i1, $i2), $code);
  951.     }
  952.     
  953.     /**
  954.      * Uses {@link PEAR_Common::analyzeSourceCode()} and {@link PEAR_Common::buildProvidesArray()}
  955.      * to create the <provides></provides> section of the package.xml
  956.      * @param PEAR_Common
  957.      * @param string path to source file
  958.      * @access private
  959.      */
  960.     function _addProvides(&$pear, $file)
  961.     {
  962.         if (!($a = $pear->analyzeSourceCode($file))) {
  963.             return;
  964.         } else {
  965.             $pear->buildProvidesArray($a);
  966.         }
  967.     }
  968.     
  969.     /**
  970.      * @uses getDirTag() generate the xml from the array
  971.      * @return string
  972.      * @access private
  973.      */
  974.     function _getFileList()
  975.     {
  976.         $generatorclass = 'PEAR_PackageFileManager_' . $this->_options['filelistgenerator'];
  977.         $generator = new $generatorclass($this, $this->_options);
  978.         if ($this->_options['simpleoutput']) {
  979.             return $this->_getSimpleDirTag($generator->getFileList());
  980.         }
  981.         return $this->_getDirTag($generator->getFileList()); 
  982.     }
  983.     
  984.     /**
  985.      * Recursively generate the <filelist> section's <dir> and <file> tags, but with
  986.      * simple human-readable output
  987.      * @param array|PEAR_Error the sorted directory structure, or an error
  988.      *                         from filelist generation
  989.      * @param false|string whether the parent directory has a role this should
  990.      * inherit
  991.      * @param integer indentation level
  992.      * @return array|PEAR_Error
  993.      * @access private
  994.      */
  995.     function _getSimpleDirTag($struc, $role = false, $_curdir = '')
  996.     {
  997.         if (PEAR::isError($struc)) {
  998.             return $struc;
  999.         }
  1000.         extract($this->_options);
  1001.         $ret = array();
  1002.         foreach($struc as $dir => $files) {
  1003.             if (false && $dir === '/') {
  1004.                 // global directory role? overrides all exceptions except file exceptions
  1005.                 if (isset($dir_roles['/'])) {
  1006.                     $role = $dir_roles['/'];
  1007.                 }
  1008.                 return array(
  1009.                     'baseinstalldir' => $this->_options['baseinstalldir'],
  1010.                     '##files' => $this->_getSimpleDirTag($struc[$dir], $role, ''),
  1011.                     'name' => '/');
  1012.             } else {
  1013.                 if (!isset($files['file'])) {
  1014.                     if (isset($dir_roles[$_curdir . $dir])) {
  1015.                         $myrole = $dir_roles[$_curdir . $dir];
  1016.                     } else {
  1017.                         $myrole = $role;
  1018.                     }
  1019.                     $ret[$dir] = array();
  1020.                     if ($dir == '/') {
  1021.                         $ret[$dir]['baseinstalldir'] = $this->_options['baseinstalldir'];
  1022.                     }
  1023.                     $ret[$dir]['name'] = $dir;
  1024.                     $recurdir = ($_curdir == '') ? $dir . '/' : $_curdir . $dir . '/';
  1025.                     if ($recurdir == '//') {
  1026.                         $recurdir = '';
  1027.                     }
  1028.                     $ret[$dir]['##files'] = $this->_getSimpleDirTag($files, $myrole, $recurdir);
  1029.                 } else {
  1030.                     $myrole = '';
  1031.                     if (!$role)
  1032.                     {
  1033.                         $myrole = false;
  1034.                         if (isset($exceptions[$files['path']])) {
  1035.                             $myrole = $exceptions[$files['path']];
  1036.                         } elseif (isset($roles[$files['ext']])) {
  1037.                             $myrole = $roles[$files['ext']];
  1038.                         } else {
  1039.                             $myrole = $roles['*'];
  1040.                         }
  1041.                     } else {
  1042.                         $myrole = $role;
  1043.                         if (isset($exceptions[$files['path']])) {
  1044.                             $myrole = $exceptions[$files['path']];
  1045.                         }
  1046.                     }
  1047.                     $test = explode('/', $files['path']);
  1048.                     foreach ($test as $subpath) {
  1049.                         if ($subpath == 'CVS') {
  1050.                             $this->pushWarning(PEAR_PACKAGEFILEMANAGER_CVS_PACKAGED, array('path' => $files['path']));
  1051.                         }
  1052.                     }
  1053.                     $ret[$files['file']] = array('role' => $myrole);
  1054.                     if (isset($installexceptions[$files['path']])) {
  1055.                         $ret[$files['file']]['baseinstalldir'] = $installexceptions[$files['path']];
  1056.                     }
  1057.                     if (isset($platformexceptions[$files['path']])) {
  1058.                         $ret[$files['file']]['platform'] = $platformexceptions[$files['path']];
  1059.                     }
  1060.                     if (isset($installas[$files['path']])) {
  1061.                         $ret[$files['file']]['install-as'] = $installas[$files['path']];
  1062.                     }
  1063.                     if (isset($replacements[$files['path']])) {
  1064.                         $ret[$files['file']]['replacements'] = $replacements[$files['path']];
  1065.                     }
  1066.                 }
  1067.             }
  1068.         }
  1069.         return $ret;
  1070.     }
  1071.     
  1072.     /**
  1073.      * Recursively generate the <filelist> section's <dir> and <file> tags
  1074.      * @param array|PEAR_Error the sorted directory structure, or an error
  1075.      *                         from filelist generation
  1076.      * @param false|string whether the parent directory has a role this should
  1077.      * inherit
  1078.      * @param integer indentation level
  1079.      * @return array|PEAR_Error
  1080.      * @access private
  1081.      */
  1082.     function _getDirTag($struc, $role=false, $_curdir = '')
  1083.     {
  1084.         if (PEAR::isError($struc)) {
  1085.             return $struc;
  1086.         }
  1087.         extract($this->_options);
  1088.         $ret = array();
  1089.         foreach($struc as $dir => $files) {
  1090.             if ($dir === '/') {
  1091.                 // global directory role? overrides all exceptions except file exceptions
  1092.                 if (isset($dir_roles['/'])) {
  1093.                     $role = $dir_roles['/'];
  1094.                 }
  1095.                 return $this->_getDirTag($struc[$dir], $role, '');
  1096.             } else {
  1097.                 if (!isset($files['file'])) {
  1098.                     $myrole = '';
  1099.                     if (isset($dir_roles[$_curdir . $dir])) {
  1100.                         $myrole = $dir_roles[$_curdir . $dir];
  1101.                     } elseif ($role) {
  1102.                         $myrole = $role;
  1103.                     }
  1104.                     $ret = array_merge($ret, $this->_getDirTag($files, $myrole, $_curdir . $dir . '/'));
  1105.                 } else {
  1106.                     $myrole = '';
  1107.                     if (!$role)
  1108.                     {
  1109.                         $myrole = false;
  1110.                         if (isset($exceptions[$files['path']])) {
  1111.                             $myrole = $exceptions[$files['path']];
  1112.                         } elseif (isset($roles[$files['ext']])) {
  1113.                             $myrole = $roles[$files['ext']];
  1114.                         } else {
  1115.                             $myrole = $roles['*'];
  1116.                         }
  1117.                     } else {
  1118.                         $myrole = $role;
  1119.                         if (isset($exceptions[$files['path']])) {
  1120.                             $myrole = $exceptions[$files['path']];
  1121.                         }
  1122.                     }
  1123.                     if (isset($installexceptions[$files['path']])) {
  1124.                         $bi = $installexceptions[$files['path']];
  1125.                     } else {
  1126.                         $bi = $this->_options['baseinstalldir'];
  1127.                     }
  1128.                     $test = explode('/', $files['path']);
  1129.                     foreach ($test as $subpath) {
  1130.                         if ($subpath == 'CVS') {
  1131.                             $this->pushWarning(PEAR_PACKAGEFILEMANAGER_CVS_PACKAGED, array('path' => $files['path']));
  1132.                         }
  1133.                     }
  1134.                     $ret[$files['path']] =
  1135.                         array('role' => $myrole,
  1136.                               'baseinstalldir' => $bi,
  1137.                               );
  1138.                     $md5sum = @md5_file($this->_options['packagedirectory'] . $files['path']);
  1139.                     if (!empty($md5sum)) {
  1140.                         $ret[$files['path']]['md5sum'] = $md5sum;
  1141.                     }
  1142.                     if ($this->_options['simpleoutput']) {
  1143.                         unset($ret[$files['path']]['md5sum']);
  1144.                     }
  1145.                     if (isset($platformexceptions[$files['path']])) {
  1146.                         $ret[$files['path']]['platform'] = $platformexceptions[$files['path']];
  1147.                     }
  1148.                     if (isset($installas[$files['path']])) {
  1149.                         $ret[$files['path']]['install-as'] = $installas[$files['path']];
  1150.                     }
  1151.                     if (isset($replacements[$files['path']])) {
  1152.                         $ret[$files['path']]['replacements'] = $replacements[$files['path']];
  1153.                     }
  1154.                     if ($myrole == 'php' && !$this->_options['simpleoutput']) {
  1155.                         $this->_addProvides($this->_pear, $files['fullpath']);
  1156.                     }
  1157.                 }
  1158.             }
  1159.         }
  1160.         return $ret;
  1161.     }
  1162.  
  1163.     /**
  1164.      * Retrieve the 'deps' option passed to the constructor
  1165.      * @access private
  1166.      * @return array
  1167.      */
  1168.     function _getDependencies()
  1169.     {
  1170.         if (isset($this->_packageXml['release_deps']) &&
  1171.               is_array($this->_packageXml['release_deps'])) {
  1172.             return $this->_packageXml['release_deps'];
  1173.         } else {
  1174.             return array();
  1175.         }
  1176.     }
  1177.  
  1178.     /**
  1179.      * Creates a changelog entry with the current release
  1180.      * notes and dates, or overwrites a previous creation
  1181.      * @access private
  1182.      */
  1183.     function _updateChangeLog()
  1184.     {
  1185.         $curlog = $oldchangelog = false;
  1186.         if (!isset($this->_packageXml['changelog'])) {
  1187.             $changelog = array();
  1188.             if (isset($this->_oldPackageXml['release_notes'])) {
  1189.                 $changelog['release_notes'] = $this->_oldPackageXml['release_notes'];
  1190.             }
  1191.             if (isset($this->_oldPackageXml['version'])) {
  1192.                 $changelog['version'] = $this->_oldPackageXml['version'];
  1193.             }
  1194.             if (isset($this->_oldPackageXml['release_date'])) {
  1195.                 $changelog['release_date'] = $this->_oldPackageXml['release_date'];
  1196.             }
  1197.             if (isset($this->_oldPackageXml['release_license'])) {
  1198.                 $changelog['release_license'] = $this->_oldPackageXml['release_license'];
  1199.             }
  1200.             if (isset($this->_oldPackageXml['release_state'])) {
  1201.                 $changelog['release_state'] = $this->_oldPackageXml['release_state'];
  1202.             }
  1203.             if (count($changelog)) {
  1204.                 $this->_packageXml['changelog'] = array($changelog);
  1205.             } else {
  1206.                 $this->_packageXml['changelog'] = array();
  1207.             }
  1208.         } else {
  1209.             if (isset($this->_oldPackageXml['release_notes'])) {
  1210.                 $oldchangelog['release_notes'] = $this->_oldPackageXml['release_notes'];
  1211.             }
  1212.             if (isset($this->_oldPackageXml['version'])) {
  1213.                 $oldchangelog['version'] = $this->_oldPackageXml['version'];
  1214.             }
  1215.             if (isset($this->_oldPackageXml['release_date'])) {
  1216.                 $oldchangelog['release_date'] = $this->_oldPackageXml['release_date'];
  1217.             }
  1218.             if (isset($this->_oldPackageXml['release_license'])) {
  1219.                 $oldchangelog['release_license'] = $this->_oldPackageXml['release_license'];
  1220.             }
  1221.             if (isset($this->_oldPackageXml['release_state'])) {
  1222.                 $oldchangelog['release_state'] = $this->_oldPackageXml['release_state'];
  1223.             }
  1224.         }
  1225.         $hasoldversion = false;
  1226.         foreach($this->_packageXml['changelog'] as $index => $changelog) {
  1227.             if ($oldchangelog && isset($oldchangelog['version']) 
  1228.                     && strnatcasecmp($oldchangelog['version'], $changelog['version']) == 0) {
  1229.                 $hasoldversion = true;
  1230.             }
  1231.             if (isset($changelog['version']) && strnatcasecmp($changelog['version'], $this->_options['version']) == 0) {
  1232.                 $curlog = $index;
  1233.             }
  1234.             if (isset($this->_packageXml['changelog'][$index]['release_notes'])) {
  1235.                 $this->_packageXml['changelog'][$index]['release_notes'] = trim($changelog['release_notes']);
  1236.             }
  1237.             // the parsing of the release notes adds a \n for some reason
  1238.         }
  1239.         if (!$hasoldversion && $oldchangelog && count($oldchangelog)
  1240.               && $oldchangelog['version'] != $this->_options['version']) {
  1241.             $this->_packageXml['changelog'][] = $oldchangelog;
  1242.         }
  1243.         $notes = ($this->_options['changelognotes'] ?
  1244.                     $this->_options['changelognotes'] : $this->_options['notes']);
  1245.         $changelog = array('version' => $this->_options['version'],
  1246.                            'release_date' => date('Y-m-d'),
  1247.                            'release_license' => $this->_options['license'],
  1248.                            'release_state' => $this->_options['state'],
  1249.                            'release_notes' => $notes,
  1250.                            );
  1251.         if ($curlog !== false) {
  1252.             $this->_packageXml['changelog'][$curlog] = $changelog;
  1253.         } else {
  1254.             $this->_packageXml['changelog'][] = $changelog;
  1255.         }
  1256.         usort($this->_packageXml['changelog'], array($this, '_changelogsort'));
  1257.     }
  1258.     
  1259.     /**
  1260.      * @static
  1261.      * @access private
  1262.      */
  1263.     function _changelogsort($a, $b)
  1264.     {
  1265.         if ($this->_options['changelogoldtonew']) {
  1266.             $c = strtotime($a['release_date']);
  1267.             $d = strtotime($b['release_date']);
  1268.             $v1 = $a['version'];
  1269.             $v2 = $b['version'];
  1270.          } else {
  1271.             $d = strtotime($a['release_date']);
  1272.             $c = strtotime($b['release_date']);
  1273.             $v2 = $a['version'];
  1274.             $v1 = $b['version'];
  1275.         }
  1276.         if ($c - $d > 0) {
  1277.             return 1;
  1278.         } elseif ($c - $d < 0) {
  1279.             return -1;
  1280.          }
  1281.         return version_compare($v1, $v2);
  1282.     }
  1283.  
  1284.     /**
  1285.      * @return true|PEAR_Error
  1286.      * @uses _generateNewPackageXML() if no package.xml is found, it
  1287.      *       calls this to create a new one
  1288.      * @param string full path to package file
  1289.      * @param string name of package file
  1290.      * @throws PEAR_PACKAGEFILEMANAGER_PATH_DOESNT_EXIST
  1291.      * @access private
  1292.      */
  1293.     function _getExistingPackageXML($path, $packagefile = 'package.xml')
  1294.     {
  1295.         if (is_string($path) && is_dir($path)) {
  1296.             $contents = false;
  1297.             if (file_exists($path . $packagefile)) {
  1298.                 $contents = file_get_contents($path . $packagefile);
  1299.             }
  1300.             if (!$contents) {
  1301.                 return $this->_generateNewPackageXML();
  1302.             } else {
  1303.                 $PEAR_Common = $this->_options['pearcommonclass'];
  1304.                 if (!class_exists($PEAR_Common)) {
  1305.                     return $this->raiseError(PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS);
  1306.                 }
  1307.                 $common = new $PEAR_Common;
  1308.                 $this->_oldPackageXml =
  1309.                 $this->_packageXml = $common->infoFromString($contents);
  1310.                 if (PEAR::isError($this->_packageXml)) {
  1311.                     return $this->_packageXml;
  1312.                 }
  1313.                 if ($this->_options['deps'] !== false) {
  1314.                     $this->_packageXml['release_deps'] = $this->_options['deps'];
  1315.                 } else {
  1316.                     if (isset($this->_packageXml['release_deps'])) {
  1317.                         $this->_options['deps'] = $this->_packageXml['release_deps'];
  1318.                     }
  1319.                 }
  1320.                 if ($this->_options['maintainers'] !== false) {
  1321.                     $this->_packageXml['maintainers'] = $this->_options['maintainers'];
  1322.                 } else {
  1323.                     $this->_options['maintainers'] = $this->_packageXml['maintainers'];
  1324.                 }
  1325.                 unset($this->_packageXml['filelist']);
  1326.                 unset($this->_packageXml['provides']);
  1327.             }
  1328.             return true;
  1329.         } else {
  1330.             if (!is_string($path)) {
  1331.                 $path = gettype($path);
  1332.             }
  1333.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_PATH_DOESNT_EXIST,
  1334.                 $path);
  1335.         }
  1336.     }
  1337.     
  1338.     /**
  1339.      * Create the structure for a new package.xml
  1340.      *
  1341.      * @uses $_packageXml emulates reading in a package.xml
  1342.      *       by using the package, summary and description
  1343.      *       options
  1344.      * @return true|PEAR_Error
  1345.      * @access private
  1346.      */
  1347.     function _generateNewPackageXML()
  1348.     {
  1349.         $this->_oldPackageXml = false;
  1350.         if (!isset($this->_options['package'])) {
  1351.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NOPACKAGE);
  1352.         }
  1353.         if (!isset($this->_options['summary'])) {
  1354.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NOSUMMARY);
  1355.         }
  1356.         if (!isset($this->_options['description'])) {
  1357.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NODESC);
  1358.         }
  1359.         $this->_packageXml = array();
  1360.         $this->_packageXml['package'] = $this->_options['package'];
  1361.         $this->_packageXml['summary'] = $this->_options['summary'];
  1362.         $this->_packageXml['description'] = $this->_options['description'];
  1363.         $this->_packageXml['changelog'] = array();
  1364.         if ($this->_options['deps'] !== false) {
  1365.             $this->_packageXml['release_deps'] = $this->_options['deps'];
  1366.         } else {
  1367.             $this->_packageXml['release_deps'] = $this->_options['deps'] = array();
  1368.         }
  1369.         if ($this->_options['maintainers'] !== false) {
  1370.             $this->_packageXml['maintainers'] = $this->_options['maintainers'];
  1371.         } else {
  1372.             $this->_packageXml['maintainers'] = $this->_options['maintainers'] = array();
  1373.         }
  1374.         return true;
  1375.     }
  1376.  
  1377.     /**
  1378.      * calls {@link file_exists()} for each value in include_path,
  1379.      * then calls {@link is_readable()} when it finds the file
  1380.      * @param string
  1381.      * @static
  1382.      * @return boolean
  1383.      */
  1384.     function isIncludeable($filename)
  1385.     {
  1386.         $ip = ini_get('include_path');
  1387.         $ip = explode(PATH_SEPARATOR, $ip);
  1388.         foreach($ip as $path)
  1389.         {
  1390.             if ($a = realpath($path . DIRECTORY_SEPARATOR . $filename))
  1391.             {
  1392.                 if (is_readable($a))
  1393.                 {
  1394.                     return true;
  1395.                 }
  1396.             }
  1397.         }
  1398.         return false;
  1399.     }
  1400. }
  1401.  
  1402. if (!function_exists('file_get_contents')) {
  1403. /**
  1404.  * @ignore
  1405.  */
  1406. function file_get_contents($path, $use_include_path = null, $context = null)
  1407. {
  1408.     $a = @file($path, $use_include_path, $context);
  1409.     if (is_array($a)) {
  1410.         return implode('', $a);
  1411.     } else {
  1412.         return false;
  1413.     }
  1414. }
  1415. }
  1416. ?>
  1417.