<?php
/**
 * Handling of Config- and SML-Files
 *
 * @package     yana
 * @subpackage  utilities
 * @licence http://creativecommons.org/licenses/by-nc-sa/2.5/
 */

if (!defined('CASE_MIXED')) {
    /**
     * @ignore
     */
    define('CASE_MIXED', -1);
}

/**
 * Read a config file and copy its contents to the global scope
 *
 * The argument $mode can be used to decide how keys should be treated.
 * 
 * Valid values for $mode are:
 * <ul>
 *     <li>  CASE_UPPER  upper-case all keys       </li>
 *     <li>  CASE_LOWER  lower-case all keys       </li>
 *     <li>  CASE_MIXED  leave keys in mixed case  </li>
 * </ul>
 *
 * @name   importconfigfile()
 * @param  string  $filename
 * @param  int     $mode
 *
 * @see    getconfigfile()
 * @see    makeconfig()
 */
function importConfigFile($filename, $mode=CASE_UPPER)
{
    assert('is_string($filename);');
    assert('$mode === CASE_MIXED || $mode === CASE_LOWER || $mode === CASE_UPPER;');
    $daten =& getConfigFile($filename, $mode);
    foreach ($daten as $key => $element)
    {
        if (isset($GLOBALS[$key])) {
            $GLOBALS[$key] += $element;
        } else {
            $GLOBALS[$key]  = $element;
        }
    }
}

/**
 * Read a config file and returns it contents
 *
 * The argument $input can wether be a filename or a numeric array
 * of strings created by file($filename).
 *
 * The argument $mode can be used to decide how keys should be treated.
 *
 * Valid values for $mode are:
 * <ul>
 *     <li>  CASE_UPPER  upper-case all keys       </li>
 *     <li>  CASE_LOWER  lower-case all keys       </li>
 *     <li>  CASE_MIXED  leave keys in mixed case  </li>
 * </ul>
 *
 * @name   getconfigfile()
 * @param  array|string  $input
 * @param  int           $mode
 *
 * @see    importconfigfile()
 * @see    makeconfig()
 */
function getConfigFile($input, $mode=CASE_UPPER)
{
    assert('$mode === CASE_MIXED || $mode === CASE_LOWER || $mode === CASE_UPPER;');

    $result       = array();
    $stack        = array(&$result);
    $stack_length = 0;
    $match1       = array();
    $match2       = array();
    $handle       = null;
    $buffer       = "";
    $is_file      = false;

    if (is_file("$input")) {
        $handle  = fopen("$input", "r");;
        $is_file = true;
    } else if (is_array($input)) {
        $is_file = false;
        $i = 0;
    } else {
        trigger_error("Invalid input ".gettype($input)."($input)", E_USER_WARNING);
        exit;
    }

    while (true) {
        if ($is_file === true) {
            if (feof($handle)) {
                break;
            }
            $buffer = fgets($handle);
        } else {
            if (!isset($input[$i])) {
                break;
            }
            $buffer = &$input[$i];
        }
        if (preg_match("/^\s*<([^\/][^>]*)>/", $buffer, $match1)) {
            /* START TAG */
            if ($mode === CASE_UPPER) {
                $match1[1] = strtoupper($match1[1]);
            } else if ($mode === CASE_LOWER) {
                $match1[1] = strtolower($match1[1]);
            }
            $stack[$stack_length +1] =& $stack[$stack_length++][$match1[1]];

            if (preg_match('/^'.preg_quote($match1[0]).'(.*)<\/'.preg_quote($match1[1]).'>/Ui', $buffer, $match2)) {
                /* CDATA */
                if ($match2[1] == "true") {
                    $stack[$stack_length] = true;
                } elseif ($match2[1] == "false") {
                    $stack[$stack_length] = false;
                } elseif ($match2[1]!="") {
                    $stack[$stack_length] = $match2[1];
                }
                array_pop($stack);
                $stack_length--;
            }

        } else if (preg_match("/<\/[^\/]*>/U", $buffer)) {
            /* END TAG */
            array_pop($stack);
            $stack_length--;
        } else {
            /* COMMENT */
        }
        if ($is_file === false) {
            $i++;
        }
    }

    if ($is_file) {
        fclose($handle);
    }
    return $result;

}

/**
 * Create a config file from scalar variable or array data.
 *
 * The argument $name can be used to specify the name of the root node.
 * If $name is omitted, no root node is created.
 *
 * The argument $mode can be used to decide how keys should be treated.
 *
 * Note that any tags from string inputs will be stripped.
 *
 * Valid values for $mode are:
 * <ul>
 *     <li>  CASE_UPPER  upper-case all keys       </li>
 *     <li>  CASE_LOWER  lower-case all keys       </li>
 *     <li>  CASE_MIXED  leave keys in mixed case  </li>
 * </ul>
 *
 * @name   makeconfig()
 * @param  scalar|array  $filename
 * @param  string  $filename
 * @param  int     $mode
 *
 * @see    getconfigfile()
 * @see    importconfigfile()
 */
function makeConfig($data, $name = null, $mode = CASE_UPPER, $indent = 0)
{
    assert('is_null($data) || is_scalar($data) || is_array($data);');
    assert('$mode === CASE_MIXED || $mode === CASE_LOWER || $mode === CASE_UPPER;');

    settype($name,   "string");
    settype($indent, "integer");
    settype($mode,   "int");

    /* string */ $txt = "";
    
    if (is_null($data)) {
        return "";
    }

    if ($mode === CASE_UPPER) {
        $name = strtoupper($name);
    } else if ($mode === CASE_LOWER) {
        $name = strtolower($name);
    }

    if ((is_null($name) || $name === "") && is_array($data)) {
        $ignore_name = true;
    } else {
        $ignore_name = false;
    }

    /* indent tag */
    $tab = "";
    for ($i=0;$i<$indent;$i++)
    {
        $tab .= "\t";
    }

    if (is_bool($data)) {
        return $tab."<$name>".( ($data) ? "true" : "false" )."</$name>"."\n";
    } else if (is_string($data)) {
        return $tab."<$name>".preg_replace("/\s/", " ", strip_tags($data))."</$name>"."\n";
    } else if (is_integer($data) || is_float($data)) {
        return $tab."<$name>".$data."</$name>"."\n";
    } else if (is_array($data)) {
        $ignore_name || $txt = $tab."<$name>"."\n";
        $ignore_name || $indent++;
        foreach ($data as $key=>$element)
        {
            if ($element !== $data) {
                $txt .= makeConfig($element, $key, $mode, $indent);
            } else {
                /* ignore recursion */
            }
        }
        $ignore_name || $txt .= $tab."</$name>"."\n";
        return $txt;
    } else if (is_object($data) && method_exists ($data, "serialize")) {
        return $tab."<$name>".$data->serialize()."</$name>"."\n";
    } else {
        return $tab."<$name>".$data."</$name>"."\n";
    }
}
?>