<?php // vim: ts=4 sw=4 et tw=78 fdm=marker
/*
  +----------------------------------------------------------------------+
  | Copyright (c) 1997-2023 The PHP Group                                |
  +----------------------------------------------------------------------+
  | This source file is subject to version 3.01 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:           |
  | https://www.php.net/license/3_01.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:    Hartmut Holzgraefe <hholzgra@php.net>                    |
  |             Gabor Hojtsy <goba@php.net>                              |
  +----------------------------------------------------------------------+
  
  $Id$
*/

/**
 *
 * Create phpdoc/entities/file-entities.ent with respect
 * to all the specialities needed:
 *
 *  . CHM only appendix integration
 *  . Special install part (temporary)
 *  . Reserved constant part (temporary)
 *  . Translated language files with English ones as fallbacks
 *  . Global function index
 *  . additional extension documentation from PECL
 *  . additional extension documentation from standalone extensions
 *
 * Also take in account, that if XSLT style sheets are used,
 * special file:/// prefixed path values are needed.
 *
 */


// Always flush output
ob_implicit_flush();
// This script runs for a long time
set_time_limit(0);

// ......:ARGUMENT PARSING:.....................................................

$not_windows = (0 < strcasecmp('WIN',PHP_OS));

// The dir for PHP. If the cygwin wasn't compiled on Cygwin, the path needs to be stripped.
$out_dir = ($not_windows  || !strcasecmp('CYGWIN',php_uname()))? '@WORKDIR@' : abs_path(strip_cygdrive('@WORKDIR@'));
$src_dir = ($not_windows  || !strcasecmp('CYGWIN',php_uname()))? '@SRCDIR@'  : abs_path(strip_cygdrive('@SRCDIR@'));
$base_dir = ($not_windows || !strcasecmp('CYGWIN',php_uname()))? '@BASEDIR@' : abs_path(strip_cygdrive('@BASEDIR@'));
$root_dir = ($not_windows || !strcasecmp('CYGWIN',php_uname()))? '@ROOTDIR@' : abs_path(strip_cygdrive('@ROOTDIR@'));
$only_dir = ($not_windows || !strcasecmp('CYGWIN',php_uname()))? '@ONLYDIR@' : abs_path(strip_cygdrive('@ONLYDIR@'));

// The language encoding to use
$encoding = '@ENCODING@';


// ......:ENTITY CREATION:......................................................

// Put all the file entities into $entities
$entities = array();
file_entities($only_dir ?: "$root_dir/en", "$root_dir/@LANGDIR@", "$root_dir/en", $entities);

// Open file for appending and write out all entitities
$fp = fopen("$base_dir/entities/file-entities.ent", "w");
if (!$fp) {
    die("ERROR: Failed to open $base_dir/entities/file-entities.ent for writing\n");
}

echo "Creating file {$base_dir}/entities/file-entities.ent... ";

// File header
fputs($fp, "<!-- DON'T TOUCH - AUTOGENERATED BY file-entities.php -->\n\n");

// The global function index page is special
fputs(
   $fp,
   "<!-- global function index file -->\n" .
   entstr("global.function-index", "$out_dir/funcindex.xml") . "\n"
);

// Write out all other entities
$entities = array_unique($entities);
foreach ($entities as $entity) {
    fputs($fp, $entity);
}
fclose($fp);

echo "done\n";
// Here is the end of the code
exit;

// ......:FUNCTION DECLARATIONS:................................................

/**
 * Generate absolute path from a relative path, taking accout
 * the current wokring directory.
 *
 * @param string $path Relative path
 * @return string Absolute path generated
 */
function abs_path($path) {

    // This is already an absolute path (begins with / or a drive letter)
    if (preg_match("!^(/|\\w:)!", $path)) { return $path; }

    // Get directory parts

    $absdir  = str_replace("\\", "/", getcwd());
    $absdirs = explode("/", preg_replace("!/scripts$!", "", $absdir));
    $dirs    = explode("/", $path);

    // Generate array representation of absolute path
    foreach ($dirs as $dir) {
        if (empty($dir) or $dir == ".") continue;
        else if ($dir == "..") array_pop($absdirs);
        else array_push($absdirs, $dir);
    }

    // Return with string
    return join("/", $absdirs);
}

/**
 * Create file entities, and put them into the array passed as the
 * last argument (passed by reference).
 *
 * @param string $work_dir English files' directory
 * @param string $trans_dir Translation's directory
 * @param string $orig_dir Original directory
 * @param array $entities Entities string array
 * @return boolean Success signal
 */
function file_entities($work_dir, $trans_dir, $orig_dir, &$entities, $prefix=false) {
    static $arBroken = null;
    if ($arBroken === null) {
        $brokenFilePath = $trans_dir . '/broken-files.txt';
        if ('@USE_BROKEN_TRANSLATION_FILENAME@' == 'yes' && file_exists($brokenFilePath)) {
            $arBroken = array_map('trim', file($brokenFilePath));
            for ($n = 0; $n < count($arBroken); $n++) {
                $arBroken[$n] = $trans_dir . '/' . $arBroken[$n];
            }
            $arBroken = array_flip($arBroken);
        } else {
            $arBroken = array();
        }
    }
    // Compute translated version's path
    $trans_path = str_replace($orig_dir, $trans_dir, $work_dir);
    
    // Try to open English working directory
    $dh = opendir($work_dir);
    if (!$dh) { 
        return FALSE; 
    }

    // If the working directory is a reference functions directory,
    // then start to generate a entities.<dirname>.xml file for that folder.
    if (strpos($work_dir, "reference") !== false || preg_match("!/macros$!", $work_dir)) {
        // Start new functions file with empty entity set
        $function_entities = array();

        // Create ..reference/<extname>/entities.<dirname>.xml where <dirname>
        // is "functions" for instance
        $functions_file = sprintf("%s/entities.%s.xml", dirname($work_dir), basename($work_dir));
        touch($functions_file);

        // Get relative file path to original directory, and form an entity
        $functions_file_entity = str_replace("$orig_dir/", "", dirname($work_dir). "/entities." .basename($work_dir));
        $functions_file_entity = fname2entname($functions_file_entity, $prefix);
        $entities[] = entstr($functions_file_entity, $functions_file);
		//var_dump($functions_file_entity);exit;
    }

    // While we can read that directory
    while (($file = readdir($dh)) !== FALSE) {
        // If file name begins with . skip it
        if ($file[0] == ".") { continue; }

        // If we found a directory, and it's name is not
        // .svn, recursively go into it, and generate entities
        if (is_dir($work_dir . "/" . $file)) {
            if ($file == ".svn") { continue; }
            file_entities($work_dir . "/" . $file, $trans_dir, $orig_dir, $entities, $prefix);
        }

        // If the file name ends in ".xml"
        if (preg_match("!\\.xml$!", $file)) {
            
            // Get relative file name and get entity name for it
            $name = str_replace(
                "$orig_dir/",
                "",
                $work_dir . "/" . preg_replace("!\\.xml$!", "", $file)
            );
            $name = fname2entname($name, $prefix);

            // If this is a functions directory, collect it into
            // the special $function_entities array
            if (isset($function_entities)) {
                $function_entities[] = "&$name";
            }
            
            // If we have a translated file, use it, otherwise fall back to English
            $transfile = "$trans_path/$file";
            if (!isset($arBroken[$transfile]) && file_exists($transfile)) {
                $path = $transfile;
            } else {
                $path = "$work_dir/$file";
            }

            // Append to entities array
            $entities[] = entstr($name, $path);

        } // end of "if xml file"
    } // end of readdir loop
    
    // Close directory
    closedir($dh);

    // If we created a function entities list, write it out
    if (isset($function_entities)) {
        
        // Sort by name
        sort($function_entities);
        
        // Write out all entities with newlines
        $fp = fopen($functions_file, "w");
        foreach ($function_entities as $entity) {
            fputs($fp, "$entity;\n");
        }
        fclose($fp);
    }

    // Find all files available in the translation but not in the original English tree
    if ($orig_dir != $trans_dir && file_exists($trans_path) && is_dir($trans_path)) {

        // Open translation path
        $dh = @opendir($trans_path);

        if ($dh) {

            while (($file = readdir($dh)) !== FALSE) {
                if ($file[0] =="." || $file == "CVS") { continue; }
                if (is_dir($trans_path."/".$file)) { continue; }
                
                // If this is an XML file
                if (preg_match("!\\.xml$!",$file)) {
                    
                    // Generate relative file path and entity name out of it
                    $name = str_replace(
                        "$orig_dir/",
                        "",
                        $work_dir . "/" . preg_replace("!\\.xml$!", "", $file)
                    );
                    $name = fname2entname($name, $prefix);
                    
                    // If the file found is not in the English dir, append to entities list
                    if (!file_exists("$work_dir/$file")) {
                        $path = "$trans_path/$file";
                        $entities[] = entstr($name, $path);
                    }

                } // if this is an XML file end

            } // readdir iteration end
            closedir($dh);
        }
    }
    
} // end of funciton file_entities()

/**
 * Convert a file name (with path) to an entity name.
 *
 * Converts: _ => - and / => .
 *
 * @param string $fname File name
 * @return string Entity name
 */
function fname2entname($fname, $prefix=false)
{
    $ent = str_replace("_", "-", str_replace("/", ".", $fname));
    if ($prefix && !strstr($ent, $prefix)) {
        $ent = "$prefix.$ent";
    }
    // If we are running out of a sparse checkout, don't put "trunk" in entity
    if (substr($ent, 0, 5) == 'trunk') {
        $ent = substr($ent, 6);
    }
    return $ent;
}

/**
 * Return entity string with the given entityname and filename.
 * 
 * @param string $entname Entity name
 * @param string $filename Name of file
 * @return string Entity declaration string
 */
function entstr($entname, $filename)
{
    // If we have no file, than this is not a system entity
    if ($filename == "") {
        return sprintf("<!ENTITY %-40s        ''>\n", $entname);
    } else {
        return sprintf("<!ENTITY %-40s SYSTEM 'file:///%s'>\n", $entname, str_replace(array("\\", ' '), array("/", '%20'), $filename));
   }
}

/**
 * Return windows style path for cygwin.
 * 
 * @param string $path Orginal path
 * @return string windows style path
 */
function strip_cygdrive($path){
    return preg_replace(array('!^/cygdrive/(\w)/!', '@^/home/.+$@'), array('\1:/', strtr(dirname(dirname(__FILE__)), '\\', '/')), $path);
}


