<?php
/**
 * IBSE
 *
 * Information Bot Service Engine
 *
 * @package        IBSE
 * @author        HSDN Team
 * @copyright    Copyright (c) 2006-2010, Information Networks Ltd.
 * @link        http://www.hsdn.org
 * @since        Version 4.0
 */

/**
 * IBAPI
 *
 * Information Bot Application Program Interface
 *
 * @package        IBSE
 * @author        HSDN Team
 * @copyright    Copyright (c) 2006-2010, Information Networks Ltd.
 * @since        Version 4.0
 */

// ------------------------------------------------------------------------

if (defined('LIBRARIES_PATH'))
{
    require_once 
LIBRARIES_PATH.'Lua.inc.php';
}

// ------------------------------------------------------------------------

/**
 * Класс Urq
 *
 * @author        HSDN Team
 *
 * @original    instead-urq
 * @link        http://code.google.com/p/instead-games/
 */
class Urq
{
    public 
$urq = array
    (
        
'debug' => FALSE,
        
'memcached' => 'localhost'// FALSE -- отключить

        
'extension_strings' => TRUE,
        
'extension_furq' => FALSE,
        
'extension_global_else' => FALSE,
        
'extension_input' => TRUE,
        
'extension_useinv' => FALSE
        
'extension_loccnt' => TRUE,
        
'extension_loccnt_btn' => TRUE,
        
'extension_invproc' => FALSE,
        
'extension_phantoms' => FALSE,

        
'codepage' => 'cp866',
        
'filename' => '',
        
'cursor' => '>',
        
'last_com' => 'common',
        
'examine' => "Осмотреть",
        
'eol' => "\n",
        
'default_delim' => ' \t,"?!',
        
'ro' => FALSE,
        
'qs1' => FALSE,
        
'save' => FALSE,
        
'btn_exists' => FALSE,
        
'forgetprocs' => FALSE,
        
'ip' => 0,
        
'lastip' => 0,
        
'time' => NULL,
        
'output' => NULL,
        
'input' => NULL,
        
'last_output' => NULL,
        
'last_token' => NULL,
        
'cur_loc' => NULL,
        
'ibranch' => 0,
        
'invo',
        
// 'branches',
        
'stack',
        
'quest' => array(),
        
'strings' => array(),
        
'labels' => array(),
        
'buttons' => array(),
        
'orig_labels' => array(),
        
'use_labels' => array(),
        
'str' => array(),
        
'inv' => array(),
        
'vars' => array
        (
            
'fp_prec' => 2
        
),
    );

    public function 
__construct()
    {
        
// $this->urq['branches'] = new Table(array(), 1);
        
$this->urq['invo'] = new Table(array(), 1);
        
$this->urq['stack'] = new Table(array(), 1);
    }

    public function 
call($cmd$input NULL$real_input NULL)
    {
        
$r NULL;
        
$rmode FALSE;
        
$this->urq['fading'] = FALSE;
        
$this->urq['save'] = FALSE;
        
$this->urq['input'] = $input;

        
$input $this->lower($input);

        if (
$cmd == 'look'// first run
        
{
            if (isset(
$this->urq['last']) AND $this->urq['last'] != '')
            {
                
$r $this->urq['last'];
            }
            else
            {
                
$this->start_cmd();

                
$r $this->exec($this->urq['ip']);

                
$this->urq['last_loc'] = $r;
                
$this->urq['last_output'] = $this->urq['output'];
            }
        }
        else if (
$cmd == 'go'// external
        
{
            if (
$input == 'system')
            {
                if (isset(
$this->urq['vars']['sys']))
                {
                    
$input $this->lower($this->urq['vars']['sys']);
                }
                else if (isset(
$this->urq['str']['sys']))
                {
                    
$input $this->lower($this->urq['str']['sys']);
                }
            }

            if ((
$this->urq['last_token'] == 'input')
                OR (
$this->urq['last_token'] == 'pause' 
                    
AND $this->urq['time'] !== NULL 
                    
AND $this->urq['time'] < time())
                )
            {
                
$this->urq['forgetprocs'] = FALSE;

                
$r $this->exec($this->urq['labels'][$this->urq['cur_loc']]);

                
$this->urq['last_loc'] = $r;
                
$this->urq['last_output'] = $this->urq['output'];
                
$this->urq['last_act'] = NULL;
            }
            else if (isset(
$this->urq['labels'][$input]) 
                AND ((
$this->urq['debug'] === TRUE) ? TRUE : ($this->lower($real_input) != $input OR is_numeric($input))))
            {
                
$this->start_cmd();

                
$r $this->do_common();

                if (
$this->urq['forgetprocs'] === FALSE// not in common!
                
{
                    
$this->update_loc($inputTRUE);

                    if (
$this->urq['extension_loccnt_btn'] === TRUE)
                    {
                        
$this->update_count($input);
                    }

                    
$input $this->urqbtnargs($input);

                    
$this->urq['lastip'] = $this->urq['labels'][$input];

                    
$r $this->exec($this->urq['lastip'], $input);
                }

                
$this->urq['last_loc'] = $r;
                
$this->urq['last_output'] = $this->urq['output'];
                
$this->urq['last_act'] = NULL;
            }
            else if (isset(
$this->urq['last_loc']))
            {
                
$r $this->urq['last_loc'];
            }
        }
        if (
$cmd == 'act'// internal
        
{
            
$this->start_cmd();

            if (
$input == 'system')
            {
                if (isset(
$this->urq['vars']['sys']))
                {
                    
$input $this->urq['vars']['sys'];
                }
                else if (isset(
$this->urq['str']['sys']))
                {
                    
$input $this->urq['str']['sys'];
                }
            }

            
$this->urq['lastip'] = $this->urq['labels'][$input];

            
$r $this->exec($this->urq['lastip']);

            
$last_act = isset($this->urq['last_act']) ? $this->urq['last_act'] : '';

            if (
$this->urq['buttons'])
            {
                
$this->last_act $last_act;
            }

            
$this->reenter();

            
$this->urq['last_act'] = $last_act;
            
$this->urq['last_output'] = $this->urq['output'];
        }
        else if (
$cmd == 'use'// use inv
        
{
            if (
preg_match("/^use_/"$input) AND isset($this->urq['labels'][$input]))
            {
                
$this->start_cmd();

                return 
$this->call('act'$input);
            }

            if (isset(
$this->urq['use_labels']['use_'.$input])) // use
            
{
                
$this->start_cmd();

                return 
$this->call('act''use_'.$input);
            }

            
$r $this->urq['last'];
        }

        if (
$cmd == 'go' 
            
OR $cmd == 'look' 
            
OR $cmd == 'use'
            
OR $cmd == 'act')
        {
            
$this->urq['last'] = $r;
        }

        if (
$this->urq['debug'] === TRUE)
        {
            
$r .= $this->debug($cmd$input);
        }

        return array
        (
            
'text' => $r,
            
'buttons' => $this->urq_buttons(),
            
'inv' => $this->inv(),
            
'save' => $this->urq['save'],
            
'state' => $this->save_state()
        );
    }

    public function 
error($msg)
    {
        
$this->urq['output'] = 'Error: '.$msg;
    }

    public function 
lower($str)
    {
        return 
mb_strtolower($str$this->urq['codepage']);
    }

    public function 
debug($cmd$input)
    {
        return 
"\n"
            
.'[Line: '.$this->urq['ip']
            .
'; token: '.$this->urq['last_token']
            .
'; location: '.$this->urq['cur_loc']
            .(isset(
$this->urq['str']['previous_loc']) ? '; prev location: '.$this->urq['str']['previous_loc'] : '')
            .
'; cmd: '.$cmd
            
.'; input: '.$input.']';
    }

    public function 
take_label($line)
    {
        
$line =  preg_replace("/^:([^&]*)$/i""\\1"trim($line));

        list(
$loc) = $this->label($line);
        
        return 
$loc;
    }

    public function 
decode_qs1($l)
    {
        
$r '';

        for (
$i 0$i strlen($l); $i++)
        {
            
$b $l{$i};

            if (
$b >= chr(32))
            {
                
$b chr(287 ord($b));
            }

            
$r .= $b;
        }

        return 
$r;
    }

    function 
load($name)
    {
        
$this->urq['filename'] = $name;

        
$file file($name);

        if (!
$file)
        {
            return 
FALSE;
        }

        if (
end(explode('.'$name)) == 'qs1')
        {
            
$this->urq['qs1'] = TRUE;
        }

        
$incomment '';

        
$s 0;

        foreach (
$file as $line)
        {
            if (
$this->urq['qs1'])
            {
                
$line $this->decode_qs1($line);
            }

            
$line str_replace(array("\r""\n"), ''$line);

            if (
$this->urq['codepage'] == 'cp866' OR $this->urq['codepage'] == 'CP866')
            {
                
$line mb_convert_encoding($line'CP866''CP1251');
            }

            
array_push($this->urq['quest'], $line);

            
//$this->urq['quest']->insert($line);

            
$cline $line ltrim($line);

            list(
$line$incomment) = $this->strip_comments($line$incomment);

            if (
preg_match("/^:/"$line))
            {
                
$line $this->take_label($line);

                if (!isset(
$this->urq['labels'][$line]) OR $this->urq['labels'][$line] == NULL)
                {
                    
$this->urq['labels'][$line] = end(array_keys($this->urq['quest']));
                    
$this->urq['orig_labels'][$line] = $cline;

                    if (
strpos($line"use_"))
                    {
                        
$this->urq['use_labels'][$line] = $this->urq['labels'][$line];
                    }
                }
            }
            else if (
preg_match("/^[ \t]*include[ \t]/i"$line))
            {
                list(
$s$e) = find("include"$line);

                
$f $this->strip(sub($e 1));

                
$this->load($f);
            }
        }

        if (
$this->urq['codepage'] == 'cp866' OR $this->urq['codepage'] == 'CP866')
        {
            
$this->urq['examine'] = 'Ћб¬®ваҐвм';
        }
    }

    public function 
label($s)
    {
        if (
$s == NULL)
        {
            return 
NULL;
        }

        
$s $this->strip($s);

        return array(
$this->lower($s), $s);
    }

    public function 
urqprint($str$nl FALSE)
    {
        if (
$str != '')
        {
            
$this->urq['output'] .= $str.' ';
        }

        if (
$nl != FALSE)
        {
            
$this->urq['output'] .= $this->urq['eol'];
        }
    }

    public function 
urqbtnargs($l)
    {
        
$n 0;
        
$a '';

        if (
$this->urq['extension_furq'] === TRUE)
        {
            
$num preg_replace("/^[^;]+;?([0-9]*)$/""\\1"$l);

            if (
$num != '')
            {
                
$l preg_replace("/;[0-9]*$/"''$l);
                
$a $this->urq['str'][($l.'__urq_btn_arguments__'.((float) $num))];
            }

            
$this->clearbtnargs($l);
        }

        if (
$a == '' OR $this->urq['ro'] === TRUE)
        {
            return 
$l;
        }

        
$this->urq['str'][($l.'__urq_btn_arguments__'.((float) $num))] = NULL;


        if (
$this->urq['extension_strings'] === TRUE)
        {
            
$a preg_replace('/("[^"]*")/ex'"self::args_reps('\\1')"$a);
        }

        
preg_replace("/([^,]+)/ex""self::args_subst('\\1', '$l', '$n')"$a);

        return array(
$l$n);
    }

    public function 
args_subst($s$l, &$n)
    {
        
$s $this->strip($s);
        
$s str_replace('\003'','$s);
        
        
$r $this->urqexpr($sFALSETRUE);

        
$n++;

        if (
is_numeric($r))
        {
            
$this->urq['vars'][$l.'_'.$n] = $r;
        }
        else
        {
            
$this->urq['str'][$l.'_'.$n] = $r;
        }
    }

    public function 
args_reps($s)
    {
        return 
str_replace(',''\003'$s);
    }

    public function 
clearbtnargs($loc)
    {
        if (
$this->urq['ro'] === TRUE)
        { 
            return;
        }

        foreach (
$this->urq['vars'] as $k => $v)
        {
            if (
preg_match("/^".$loc."_"."[0-9]+/"$k))
            {
                unset(
$this->urq['vars'][$k]);
            }
        }

        foreach (
$this->urq['str'] as $k => $v)
        {
            if (
preg_match("/^".$loc."_"."[0-9]+/"$k))
            {
                unset(
$this->urq['str'][$k]);
            }
        }
    }

    public function 
urqargs($loc)
    {
        list(
$loc$lloc) = $this->label($loc);

        if (
$this->urq['extension_furq'] === TRUE AND (!isset($this->urq['labels'][$loc]) OR $this->urq['labels'][$loc] == 0))
        {
            
$a preg_replace("/^[^((]+[((](.*)[))]$/""\\1"$lloc);
            
$loc preg_replace("/^([^(]+)[((].*[))]$/""\\1"$loc);

            
$loc $this->strip($loc);

            
$this->clearbtnargs($loc);

            
$loca = (string) $this->urq['btn_args'];

            if (
$a != '' AND $this->urq['ro'] === FALSE)
            {
                
$this->urq['str'][$loc.'__urq_btn_arguments__'.$loca] = $a;
            }

            
$this->urq['btn_args']++;

            return array(
$loc$loc.';'.$loca);
        }

        return array(
$loc$loc);
    }

    public function 
urqbtn($loc$str)
    {
        if (
$loc == NULL OR $str == NULL)
        {
            
$this->error("Error in btn at line: ".$this->urq['ip']);
        }

        if (
preg_match('/^[ \t]*$/'$str))
        {
            
$str '...';
        }

        list(
$loc$lloc) = $this->urqargs($loc);

        return 
$this->strip($str);
    }

    public function 
urqend()
    {
        return 
$this->urq['output'];
    }

    public function 
urqif($s)
    {
        
$this->urq['ibranch']++;

        
$s $this->strip($s);

        
$r = (bool) $this->urqexpr($sTRUE);

        
// $this->urq['branches']->insert($r);

        
return $r;
    }

    public function 
invoidx($a)
    {
        foreach (
$this->urq['invo']->as $k => $v)
        {
            
$k $this->lower($k);
            
$v $this->lower($v);

            if (
$v == $this->lower($this->strip($a)))
            {
                return 
$k;
            }
        }

        return 
NULL;
    }

    public function 
invodel($a)
    {
        if (!
$i $this->invoidx($a))
        {
            return;
        }

        
$this->urq['invo']->remove($i);
    }

    public function 
invoadd($a)
    {
        
$this->urq['invo']->insert($this->strip($a));
    }

    public function 
urqinvkill($a)
    {
        if (
$this->urq['ro'] === TRUE)
        {
            return;
        }

        if (
$a != '')
        {
            
$a $this->lower($this->strip($a));

            unset(
$this->inv[$a]);
            unset(
$this->urq['vars']['inv_'.$a]);
            
            
$this->invodel($a);

            return;
        }

        foreach (
$this->urq['inv'] as $k => $v)
        {
            unset(
$this->inv[$k]);
            unset(
$this->urq['vars']['inv_'.$k]);
            
            
$this->invodel($k);
        }
    }

    public function 
urqperkill()
    {
        if (
$this->urq['ro'] === TRUE)
        {
            return;
        }

        foreach (
$this->urq['vars'] as $k => $v)
        {
            if (
preg_match("/^inv_/"$k))
            {
                unset(
$this->urq['vars'][$k]);
            }
        }

        foreach (
$this->urq['str'] as $k => $v)
        {
            if (
preg_match("/^current_loc$/"$k) AND !preg_match("/^previous_loc$/"$k) AND !preg_match("/^last_btn_caption$/"$k))
            {
                unset(
$this->urq['str'][$k]);
            }
        }

        
$this->urq['str']['tokens_delim'] = $this->urq['default_delim'];
        
$this->urq['vars']["fp_prec"] = 2;
    }

    public function 
urqinv($o$n)
    {
        if (
$this->urq['ro'] === TRUE)
        {
            return;
        }

        if (
$n == NULL OR $o == NULL)
        {
            
$this->error("Error in line: ".$this->urq['ip']);
        }

        
$ob $this->strip($o);
        
$o $this->lower($ob);

        if (!isset(
$this->urq['inv'][$o])) // -- make new
        
{
            if (
$n <= 0)
            {
                return;
            }

            
$this->urq['inv'][$o] = array('n' => $n'name' => $ob);
            
$this->urq['vars']['inv_'.$o] = $n;

            
$this->invoadd($o);

            return;
        }

        if (isset(
$this->urq['inv'][$o]['n']))
        {
            
$this->urq['inv'][$o]['n'] += $n;
        }

        if (isset(
$this->urq['vars']['inv_'.$o]))
        {
            
$this->urq['vars']['inv_'.$o] += $n;
        }

        if (isset(
$this->urq['inv'][$o]['n']) AND $this->urq['inv'][$o]['n'] <= 0)
        {
            unset(
$this->urq['inv'][$o]);
            unset(
$this->urq['vars']['inv_'.$o]);
            
            
$this->invodel($o);
        }
    }

    public function 
get2args($a)
    {
        if (
strpos($a',') !== FALSE)
        {
            list(
$l$t) = explode(','$a2);

            
$t trim($t);
        }
        else
        {
            
$l $a;
            
$t NULL;
        }

        return array(
trim($l), $t);
    }

    public function 
is_string_pattern($s)
    {
        if (
$s == '')
        {
            return 
FALSE;
        }
        
        if (
preg_match("/^__urq_string__[0-9]+/"$s))
        {
            return 
TRUE;
        }

        return 
FALSE;
    }

    public function 
fprec($n)
    {
        
$prec $this->urq['vars']["fp_prec"];

        if (!
is_numeric($n))
        {
            
$this->error("Error in expr at line: ".$this->urq['ip']);
        }

        
$n sprintf('%.'.$prec.'f'$n); // ?

        
return (float) $n;
    }

    public function 
check_inv($s)
    {
        if (isset(
$this->urq['inv'][$s]) AND $this->urq['inv'][$s] != '')
        {
            return 
1;
        }

        
$n preg_match("/^([^ \t]+)[ \t][^ \t]+$/""\\1"$s);
        
$i preg_match("/^[^ \t]+[ \t]([^ \t]+)$/""\\1"$s);

        if (
is_numeric($n) AND $i)
        {
            if (isset(
$this->urq['inv'][$i]) AND $this->urq['inv'][$i] != '')
            {
                
$i $this->urq['inv'][$i][$n];
            }
            else
            {
                
$i 0;
            }

            return 
$n.'<='.$i;
        }

        return 
NULL;
    }

    public function 
urqvar($v$inif// only numeric!
    
{
        
$n NULL;

        
$s $this->lower($this->strip($v));

        
$rnd = (float) mt_rand(010000) / 10000;

        if (
$s == 'rnd')
        {
            return 
$this->fprec($rnd);
        }
        if (
preg_match("/^rnd([0-9]+)$/"$s$r))
        {
            
$n = ($r[1] > 0) ? intval(mt_rand(0$r[1])) : $rnd;

            return 
$this->fprec($n);
        }
        else if (
$s == 'time')
        {
            return 
$this->fprec(time());
        }
        else
        {
            if (isset(
$this->urq['vars'][$s]) AND $this->urq['vars'][$s] != NULL// numeric
            
{
                
$n $this->urq['vars'][$s];
            }
            else if (isset(
$this->urq['str'][$s]) AND $this->urq['str'][$s] != NULL// string
            
{
                
$n $this->urq['str'][$s];
            }
            else if (isset(
$this->urq['vars']['inv_'.$s]) AND $this->urq['vars']['inv_'.$s] != NULL// inv
            
{
                
$n $this->urq['vars']['inv_'.$s];
            }
            else if (
$inif === TRUE)
            {
                
$r $this->check_inv($s);

                if (
$r != NULL)
                {
                    return 
$r;
                }
            }
        }

        if (
$n == NULL)
        {
            return 
0;
        }
    
        if (!isset(
$this->urq['vars'][$s]) OR $this->urq['vars'][$s] == NULL)
        {
            
$n = (string) strlen($n);
        }

        return 
$this->fprec($n);
    }

    public function 
urqstr($s)
    {
        if (
$s == '')
        {
            
$this->error("Error in line: ".$this->urq['ip']);
        }

        
$s $this->lower($this->strip($s));

        if (!isset(
$this->urq['str'][$s]) OR $this->urq['str'][$s] == '')
        {
            return 
'';
        }

        return (string) 
$this->urq['str'][$s];
    }

    public function 
add_string($r)
    {
        if (
$p in_array($r$this->urq['strings']))
        {
            return 
'__urq_string__'.$p;
        }

        
array_push($this->urq['strings'], $r);

        return 
'__urq_string__'.end(array_keys($this->urq['strings']));
    }

    public function 
urqexpr($str$inif FALSE// already substed
    
{
        
$str $this->strip($str);

        if (
$this->urq['extension_strings'] === TRUE)
        {
            
$str preg_replace('/"([^\"]*)\"/ex'"self::expr_reps('\\1')"$str);
        }

        
$str 
            
preg_replace("/^([ \t]*)not([ \t((])/""\\1(!)\\2",
            
preg_replace("/([ \t))((])not([ \t(())])/""\\1(!)\\2",
            
preg_replace("/([ \t))((])or([ \t(())])/""\\1(||)\\2"
            
preg_replace("/([ \t))((])and([ \t(())])/""\\1(&&)\\2"
            
$this->lower($str)))));

        
$str preg_replace("#([^+\^\=/\*/>><<))((-]+)#ex""self::expr_rep('\\1', '$inif')"$str);

        
$str 
            
preg_replace("/[\(\(]![\)\)]/""!",
            
preg_replace("/[\(\(]&&[\)\)]/""&&"
            
preg_replace("/[\(\(]\|\|[\)\)]/""||"$str)));

        
$expr = new Expr($this->urq['strings']);

        
$r $expr->call($str);

        if (
$r === NULL)
        {
            
$this->error("Error in expression at line: ".$this->urq['ip']);
        }

        return 
$r;
    }

    public function 
expr_reps($s)
    {
        
$s =  preg_replace('/"[ \t]*$/'''ltrim($s));
        
        return 
$this->add_string($s);
    }

    public function 
expr_rep($s$inif)
    {
        
$s $this->strip($s);

        if (
$this->is_string_pattern($s) OR preg_match("/^[ \t]*$/"$s))
        {
            return 
$s;
        }

        if (
$s == '&&' OR $s == '||' OR $s == '!')
        {
            return 
$s;
        }

        if (
is_numeric($s))
        {
            return 
$s// number as is
        
}

        if (isset(
$this->urq['str'][$this->lower($s)]) AND $this->urq['str'][$this->lower($s)] != NULL)
        {
            return 
$this->add_string($this->urq['str'][$this->lower($s)]);
        }

        return 
$this->urqvar($s$inif); // last
    
}

    public function 
urqlet($l$r$str FALSE)
    {
        if (
$this->urq['ro'] === TRUE)
        {
            return;
        }

        
$l $this->strip($l);

        if (
$str == FALSE 
            
OR !isset($this->urq['vars']['instr_leave_spc']) 
            OR 
$this->urq['vars']['instr_leave_spc'] == NULL)
        {
            
$r $this->strip($r);
        }

        if (
$r === NULL)
        {
            
$this->error("No right value at line: ".$this->urq['ip']);
        }

        
$v $this->lower($l);

        if (
$str !== FALSE)
        {
            
$this->urq['str'][$v] = $r;
            unset(
$this->urq['vars'][$v]);
            
            return;
        }

        
$res $this->urqexpr($r);

        if (
is_numeric($res))
        {
            
$this->urq['vars'][$v] = $res;
            unset(
$this->urq['str'][$v]);
        }
        else
        {
            
$this->urq['str'][$v] = $res;
            unset(
$this->urq['vars'][$v]);
        }

        if (
preg_match("/^inv_/"$v))
        {
            
$o sub($l5);

            if (isset(
$this->urq['inv'][$o]))
            {
                
$this->urq['inv'][$o]['n'] = (float) $this->urq['vars'][$v];
            }
            else
            {
                
$this->urqinv($o, ((float) $this->urq['vars'][$v]));
            }
        }
    }

    public function 
subst($str$eval FALSE)
    {
        
$l $i $k 0;
        
$depth 0;
        
$s '';

        if (
$str === '')
        {
            return;
        }

        list(
$i) = find($str'[#$$]');

        while (
$i)
        {
            if ((
sub($str$i$i) == '#') AND ($i == OR sub($str$i 1$i 1) != '#'))
            {
                
$depth++;

                if (
$depth == 1)
                {
                    
$s $i 1;
                }
            }
            else if (
sub($str$i$i) == '$')
            {
                
$depth--;

                if (
$depth 0)
                {
                    return 
$str// no pairs
                
}

                if (
$depth == 0)
                {

                    
$r $this->subst(sub($str$s$i 1), TRUE);

                    
$str sub($str1$s 2).$r.sub($str$i 1);

                    
$i $s strlen($r);
                }
            }

            list(
$i) = find($str'[#$$]'$i 1);
        }

        if (
$eval == TRUE)
        {
            if (
preg_match("/^[ \t]*$/"$str))
            {
                
$str ' ';
            }
            else if (
preg_match("#^[ \t]*/[ \t]*#"$str))
            {
                
$str $this->urq['eol'];
            }
            else if (
preg_match("/^%/"$str))
            {
                
$str $this->urqstr(sub($str2));
            }
            else if (
preg_match("/^#/"$str))
            {
                
$c = (int) $this->strip(sub($str2));

                if (
$c <= 255)
                {
                    
$str chr($c);
                }
            }
            else
            {
                
$str $this->urqexpr($str);

                if (!
is_numeric($str))
                {
                    
$str strlen($str);
                }
            }
        }

        return 
$str;
    }

    public function 
strip($s)
    {
        return 
preg_replace("/[ \t]+$/"''ltrim($s));
    }

    public function 
strip_comments($line$incomment)
    {
        if (
$incomment != '')
        {
            list(
$s$e) = find($line"*/"1TRUE);
            
            if (
$e !== NULL)
            {
                
$incomment FALSE;

                
$line sub($line$e 1);
            }
            else
            {
                
$line '';
            }
        }

        while (
TRUE)
        {
            list(
$s$e) = find($line"/*"1TRUE);

            if (
$s === NULL)
            {
                break;
            }

            list(
$ss$ee) = find($line"*/"$e 1TRUE);

            if (
$ss === NULL)
            {
                
$incomment TRUE;
                
$line sub($line1$s 1);

                break;
            }

            
$line sub($line1$s 1).sub($line$ee 1);
        }

        
$line preg_replace("/^([^;]*);.*$/""\\1"$line);

        return array(
$line$incomment);
    }

    public function 
update_count($l)
    {
        if (
$this->urq['ro'] === TRUE)
        {
            return;
        }

        if (
$this->urq['extension_loccnt'] === TRUE)
        {
            
$l 'count_'.$l;
        }

        if (!isset(
$this->urq['vars'][$l]))
        {
            
$this->urq['vars'][$l] = 0;
        }

        
$this->urq['vars'][$l]++;
    }

    public function 
token_add($r$n)
    {
        if (
preg_match("/^[ \t]*$/"$r))
        {
            return 
$n;
        }

        
$n++;

        
$this->urqlet('token'.$n$rTRUE);

        return 
$n;
    }

    public function 
find_delim($a$d)
    {
        
$se NULL;
        
$i 1;

        while (
$i <= strlen($d))
        {
            if (
$d == 'char')
            {
                return array(
11);
            }

            
$c sub($d$i$i);
            
$s find($a$c1TRUE);

            if (
$se === NULL OR ($s !== NULL and $s $se))
            {
                
$se $s;
            }

            
$i++;
        }

        return array(
$se$se);
    }

    public function 
destroy_tokens()
    {
        foreach (
$this->urq['str'] as $k => $v)
        {
            if (
preg_match("/^token[0-9]+$/"$k))
            {
                
$v NULL;
            }
        }
    }

    public function 
urqtokens($a)
    {
        if (
$this->urq['ro'] === TRUE)
        {
            return;
        }

        if (
$a == '')
        {
            
$this->error("Tokens without argument in line: ".$this->urq['ip']);
        }

        
$delim $this->urq['str']['tokens_delim'];

        if (
$delim == '')
        {
            
$delim $this->urq['default_delim'];
        }

        
$n 0;
        
        
$this->destroy_tokens();

        do
        {
            list(
$s) = $this->find_delim($a$delim);

            if (
$s === NULL)
            {
                
$r sub($a1);
                
$n $this->token_add($r$n);
                
                break;
            }

            if (
!= $s)
            {
                
$r sub($a1, (int) $s 1);
                
$n $this->token_add($r$n);
            }
            else if (
$delim == 'char')
            {
                
$r sub($a11);
                
$n $this->token_add($r$n);
            }

            
$a sub($a, (int) $s 1);
        }
        while ((list(
$u) = find($a"^[ \t]*$")) AND $u !== NULL); // ???

        
$this->urq['vars']["tokens_num"] = (int) $n;
    }

    function 
update_loc($l$btn '')
    {
        if (
$this->urq['ro'] === TRUE)
        {
            return;
        }

        if (
$l == '')
        {
            return;
        }

        
$c = isset($this->urq['str']['current_loc']) ? $this->urq['str']['current_loc'] : '';

        if (
$btn != '' AND $c != $l)
        {
            
$this->urq['str']['previous_loc'] = $c;
            
$this->urq['str']['current_loc'] = $l;
        }

        
$this->urq['cur_loc'] = $l;
    }

    public function 
findif($line)
    {
        list(
$s$e) = find($this->lower($line), "[))& \t]if[ \t((]");

        if (
$s !== NULL)
        {
            list(
$s$e) = find($this->lower($line), "if"$s);
        }

        return array(
$s$e);
    }

    public function 
findthen($line)
    {
        list(
$s$e) = find($this->lower($line), "[))& \t]then[ \t((]");

        if (
$s !== NULL)
        {
            list(
$s$e) = find($this->lower($line), "then"$s);
        }

        return array(
$s$e);
    }

    public function 
findifelse($line)
    {
        list(
$s$e) = $this->findif($line);

        if (
$this->urq['extension_global_else'] === TRUE)
        {
            list(
$ss$ee) = find($this->lower($line), "else");
        }
        else
        {
            list(
$ss$ee) = find($this->lower($line), "[$$))& \t]else[ \t((]");
        }

        if (
$ss !== NULL)
        {
            list(
$ss$ee) = find($this->lower($line), "else"$ss);
        }

        if (
$s !== NULL AND $ss !== NULL)
        {
            if (
$s <= $ss)
            {
                return array(
$s$eTRUE);
            }

            return array(
$ss$eeNULL);
        }

        if (
$s === NULL)
        {
            return array(
$ss$eeNULL);
        }
        
        return array(
$s$eTRUE);
    }

    public function 
findelse($line)
    {
        
$pos 0;
        
$inif 0;

        while (
TRUE)
        {
            list(
$s$e$i) = $this->findifelse($line);

            if (
$i !== NULL)
            {
                
$inif++; 
            }
            else
            {
                if (
$s === NULL)
                {
                    return array(
NULLNULL);
                }

                if (
$inif == 0)
                {
                    return array(
$pos $s$pos $e);
                }

                
$inif--;
            }

            
$line sub($line$e 1);

            
$pos += $e;
        }
    }

    public function 
pushcall()
    {
        
$this->urq['stack']->insert($this->urq['ip']);
        
$this->urq['stack']->insert($this->urq['depth']);
    }

    public function 
popcall()
    {
        
$this->urq['stack']->remove($this->urq['stack']->maxn());
        
$this->urq['stack']->remove($this->urq['stack']->maxn());
    }

    public function 
exec($ip$tolabel '')
    {
        
$ret NULL;

        if (
$ip sizeof($this->urq['quest'])) 
        {
            
$this->error("Error while exec wrong line. ");
        }

        
$this->update_loc($tolabel);

        
$incomment '';

        while (
$ip <= sizeof($this->urq['quest']) AND $ret == '')
        {
            
$this->urq['ip'] = $ip;
            
$line $this->urq['quest'][$ip];
            
$line ltrim($line);

            list(
$line$incomment) = $this->strip_comments($line$incomment);

            while (
$ip sizeof($this->urq['quest']) AND isset($this->urq['quest'][$ip 1]) AND preg_match("/^[ \t]*_+/"$this->urq['quest'][$ip 1])) // concat lines
            
{
                
$ip++;

                
$line $line.preg_replace("/^[ \t]*_+/"''$this->urq['quest'][$ip]);

                list(
$line$incomment) = $this->strip_comments($line$incomment);
            }

            
$this->urq['ip'] = $ip;
            
$this->urq['depth'] = 1;

            list(
$line$incomment) = $this->strip_comments($line$incomment);

            while (
$line !== '')
            {
                
$line ltrim(preg_replace("/^&+/"""$line));

                list(
$s$e) = find($line"&");


                if (
$s !== NULL// get line before &
                
{
                    
$sline $this->subst(sub($line1$e 1));
                }
                else
                {
                    
$sline $this->sline $this->subst($line);
                }

                list(
$i$k) = find($sline"[ \t]");

                if (
$i !== NULL)
                {
                    
$cmd ltrim(sub($sline1$i 1));
                    
$a sub($sline$i 1);
                }
                else
                {
                    
$cmd $this->strip($sline);
                    
$a NULL;
                }

                
$cmd $this->lower($cmd);

                
//echo $cmd.' ';

                
$this->urq['last_token'] = $cmd;

                if (
preg_match("/^:/"$line))
                {
                    
// noting todo
                    
$l $this->take_label($line);

                    if (
$this->urq['extension_loccnt_btn'] === FALSE)
                    {
                        
$this->update_count($l);
                    }

                    if (!isset(
$this->urq['str']['current_loc']) 
                        OR !isset(
$this->urq['str']['previous_loc']) 
                        OR 
$this->urq['str']['current_loc'] == NULL 
                        
OR $this->urq['str']['previous_loc'] == NULL)
                    {
                        
$this->update_loc($lTRUE);
                    }
                }
                else if (
$cmd == 'include')
                {
                    
// nothing todo
                
}
                else if (
$cmd == 'inv_visible' OR $cmd == 'html')
                {
                    
// nothing todo
                
}
                else if (
$cmd == 'clsb')
                {
                    if (
$this->urq['extension_input'] === TRUE)
                    {
                        
$this->urq['buttons'] = NULL;
                    }
                }
                else if (
$cmd == 'cls' OR $cmd == 'clst')
                {
                    if (
$this->urq['extension_input'])
                    {
                        
$this->urq['output'] = '';
                        
$this->urq['buttons'] = NULL;
                    }
                }
                else if (
$cmd == 'input')
                {
                    
$a $this->lower($a);

                    if (
$this->urq['ro'] === FALSE 
                        
AND $a != ''
                        
AND $this->urq['extension_input'] === TRUE)
                    {
                        if (
$this->urq['input'] === NULL)
                        {
                            
$this->urq['forgetprocs'] = TRUE;
                            
$this->urq['output'] .= "\n".$this->urq['cursor'];

                            
$ret TRUE;

                            break;
                        }
                        else
                        {
                            if (isset(
$this->urq['str'][$a]) OR !is_numeric($this->urq['input']))
                            {
                                
$this->urq['str'][$a] = $this->urq['input'];
                            }
                            else
                            {
                                
$this->urq['vars'][$a] = $this->urq['input'];
                            }

                            
$this->urq['input'] = NULL;
                            
$this->urq['output'] = '';
                        }
                    }
                }
                else if (
$cmd == 'p' OR $cmd == 'print')
                {
                    
$this->urqprint($a);
                }
                else if (
$cmd == 'pln' OR $cmd == 'println')
                {
                    
$this->urqprint($aTRUE);
                }
                else if (
$cmd == 'quit')
                {
                    
$this->urq['forgetprocs'] = TRUE// todo?
                    
$ret TRUE;
                    
                    break;
                }
                else if (
$cmd == 'end')
                {
                    
$this->urq['btn_exists'] = FALSE;
                    
$ret TRUE;

                    break;
                }
                else if (
$cmd == 'invkill')
                {
                    
$this->urqinvkill($a);
                }
                elseif (
$cmd == 'perkill')
                {
                    
$this->urqperkill();
                }
                else if (
$cmd == 'inv+')
                {
                    list(
$l$t)  = $this->get2args($a);

                    if (
$t === NULL)
                    {
                        
$t 1;
                    }
                    else
                    {
                        list(
$l$t) = array($t$l);
                    }
                    
                    if (
$this->urq['extension_furq'])
                    {
                        
$t $this->urqexpr($t);
                    }

                    
$this->urqinv($l$t);
                }
                else if (
$cmd == 'inv-')
                {
                    list(
$l$t)  = $this->get2args($a);

                    if (
$t === NULL)
                    {
                        
$t 1;
                    }
                    else
                    {
                        list(
$l$t) = array($t$l);
                    }

                    if (
$this->urq['extension_furq'])
                    {
                        
$t $this->urqexpr($t);
                    }

                    if (
intval($t) == 0)
                    {
                        
$t 1;
                        
                        
// print ("Warning: wrong number in inv-.");
                    
}

                    
$this->urqinv($l, -$t);
                }
                else if (
$cmd == 'btn')
                {
                    
$l NULL;
                    
$t 0;
                    
                    if (
$this->urq['extension_furq'] === TRUE)
                    {
                        list(
$s$e) = find("^[^((]+[((].*[))],"$a);

                        if (
$e !== NULL)
                        {
                            
$l sub($a1$e 1); 
                            
$t sub($a$e 1);
                        }
                    }

                    if (
$l == NULL)
                    {
                        list(
$l$t) = $this->get2args($a);
                    }

                    
$this->urq['btn_exists'] = TRUE;

                    if (
$l == 'system')
                    {
                        if (isset(
$this->urq['vars']['sys']))
                        {
                            
$l $this->urq['vars']['sys'];
                        }
                        else if (isset(
$this->urq['str']['sys']))
                        {
                            
$l $this->urq['str']['sys'];
                        }
                    }

                    if (isset(
$this->urq['labels'][$this->lower($l)]))
                    {
                        
$this->urq['buttons'][$l] = $this->urqbtn($l$t);
                    }
                    else if (
$this->urq['extension_phantoms'] === TRUE)
                    {
                        
$this->urq['buttons'][$l] = $this->urqbtn($l$t).' //phantom';
                    }
                }
                else if (
$cmd == 'forget_procs')
                {
                    
$this->urq['forgetprocs'] = TRUE;
                }
                else if (
$cmd == 'proc')
                {
                    
$a preg_replace("/[ \t]+$/"''$a);

                    list(
$a$lloc) = $this->urqargs($a);

                    if (!isset(
$this->urq['labels'][$a]) OR $this->urq['labels'][$a] == NULL)
                    {
                        
//print ("proc on non existing label '".$a."' at line: ".$ip);
                    
}
                    else if (
gettype($this->urq['labels'][$a]) == 'object'// ???
                    
{
                        
$a $this->urqbtnargs($lloc);

                        
$func $this->urq['labels'][$a];

                        
$this->$func();
                    }
                    else
                    {
                        
$a $this->urqbtnargs($lloc);

                        
$this->pushcall();

                        if (
$tolabel)
                        {
                            
$this->exec($this->urq['labels'][$a], $a);
                        }
                        else
                        {
                            
$this->exec($this->urq['labels'][$a]);
                        }

                        
$this->popcall();

                        if (
$this->urq['forgetprocs'] === TRUE)
                        {
                            
$ret TRUE;
                        }
                        else
                        {
                            
$this->update_loc($tolabel);
                        }
                    }
                }
                else if (
$cmd == 'tokens')
                {
                    if (
$a != '')
                    {
                        
$a $this->urq['str'][$this->strip($this->lower($a))];
                    }

                    
$this->urqtokens($a);
                }
                else if (
$cmd == 'goto' OR $cmd == 'save')
                {
                    
$this->urq['time'] = NULL;

                    if (
$cmd == 'save')
                    {
                        if (
$a != '')
                        {
                            
$this->autosave($a);
                        }
                        else
                        {
                            
$this->autosave($this->urq['cur_loc']);
                        }
                    }

                    if (
$a != '')
                    {
                        
$a preg_replace("/[ \t]+$/"''$a);
                        
                        list(
$a$lloc) = $this->urqargs($a);

                        if (
$a != $this->urq['cur_loc']) // защита от зацикливания (loop-методы работать не будут!)
                        
{
                            if (!isset(
$this->urq['labels'][$a]) OR $this->urq['labels'][$a] == NULL)
                            {
                                
//print ("goto on non existing label '".$a."' at line: ".$ip);
                            
}
                            else
                            {
                                
$a $this->urqbtnargs($lloc);

                                if (
$tolabel != '')
                                {
                                    
$tolabel $a;
                                }

                                
$this->exec($this->urq['labels'][$a], $tolabel);

                                
$ret TRUE;
                            }
                        }
                    }
                }
                else if (
$cmd == 'music' OR $cmd == 'play' OR $cmd == 'image' OR $cmd == 'anykey')
                {
                    
// skip
                
}
                else if (
$cmd == 'pause')
                {
                    if (
$this->urq['btn_exists'] === TRUE AND $a != 0)
                    {
                        if (
$this->urq['time'] === NULL)
                        {
                            
$this->urq['time'] = time() + floor($a 1000);
                        }

                        if (
$this->urq['time'] >= time())
                        {
                            if (
$this->urq['input'] !== NULL)
                            {
                                
$this->urq['output'] = $this->urq['last'];
                            }

                            
$ret TRUE;

                            break;
                        }

                        
$this->urq['btn_exists'] = FALSE;
                    }

                    
$this->urq['time'] = NULL;
                }
                else if (
$cmd == 'else')
                {
                    break;
                }
                else if (
$cmd == 'if')
                {
                    list(
$i$k) = $this->findthen($a);

                    if (
$i === NULL)
                    {
                        
$this->error('if without then at line: '.$ip.' ('.$a.')');
                    }

                    
$a sub($a1$i 1);

                    if (!
$this->urqif($a))
                    {
                        list(
$i$k) = $this->findelse($line);

                        if (
$i !== NULL)
                        {
                            list(
$s$e) = array($i$k);
                        }
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        list(
$i$k) = $this->findelse($line);

                        if (
$i !== NULL)
                        {
                            
$line sub($line1$i 1);
                        }

                        list(
$s$e) = $this->findthen($line);
                    }
                }
                else if (
$cmd  == 'instr')
                {
                    list(
$l$r) = explode('='$a2);

                    
$this->urqlet($l$rTRUE);
                }
                else if (
strpos($sline'=') !== FALSE)
                {
                    list(
$l$r) = explode('='$sline2);

                    
$this->urqlet($l$r);
                }
                else
                {
                    if (
$this->urq['extension_furq'] AND 
                    (
                           
preg_match("/^decor_/"$cmd)
                        OR 
preg_match("/^decoradd/"$cmd)
                        OR 
preg_match("/^decordel/"$cmd)
                        OR 
preg_match("/^decorcol/"$cmd)
                        OR 
preg_match("/^decorrot/"$cmd)
                        OR 
preg_match("/^decormov/"$cmd)
                    ))
                    {
                        
// skip
                    
}
                    else if (
$cmd != '')
                    {
                        
//print ("Unknown cmd in line: ".$this->urq['ip']." cmd: ".$sline."\n");
                    
}
                }

                if (
$e != '')
                {
                    
$line ltrim(sub($line$e 1));
                }
                else
                {
                    
$line '';
                }

                
$this->urq['depth']++;
            }

            
$ip++;
        }

        
$ret $this->urqend();

        
$this->urq['ip'] = $ip;

        
$ret preg_replace("/[\n]+$/""\n"preg_replace("/^[ \t]*[\n]+/"''$ret));

        return 
$ret;
    }

    public function 
idisp($o)
    {
        if (isset(
$this->urq['str']['idisp_'.$this->lower($o)]) 
            AND 
$this->urq['str']['idisp_'.$this->lower($o)] != NULL 
            
AND $this->urq['extension_furq'] === TRUE)
        {
            
$o $this->urq['str']["idisp_".$this->lower($o)];
        }

        
$o str_replace("_"" "$o);

        return 
$o;
    }

    public function 
inv()
    {
        
$r = array();

        if (
$this->urq['extension_useinv'] === TRUE)
        {
            foreach (
$this->urq['use_labels'] as $k => $v)
            {
                if (
preg_match("/^use_inv$/"$k))
                {
                    
$d $this->urq['orig_labels'][$k];

                    if (
$d !='')
                    {
                        
$this->error("Fatal error in: ".$this->urq['ip']);
                    }

                    
$r[$k] = $this->urq['examine'];
                }
            }
        }

        foreach (
$this->urq['use_labels'] as $k => $v)
        {
            list(
$s$e) = find("^use_inv_.*$"$k);

            if (
$s !== NULL)
            {
                if (!isset(
$this->urq['orig_labels'][$k]) OR $this->urq['orig_labels'][$k] == '')
                {
                    
$this->error("Fatal error in: ".$this->urq['ip']);
                }

                
$d $this->urq['orig_labels'][$k];

                
$r[$k] = $this->idisp(sub($d9));
            }
        }

        foreach (
$this->urq['invo']->as $kk => $vv)
        {
            
$k $this->lower($this->strip($vv));
            
$v $this->urq['inv'][$k];

            if (
is_array($v))
            {
                
$pre '';

                if (
$v['n'] > 1)
                {
                    
$pre $this->fprec($v['n']).' ';
                }

                
$r[$k] = $pre.$this->idisp($v['name']);
            }
        }

        return 
$r;
    }

    public function 
urq_buttons()
    {
        
$ret $this->urq['buttons'];

        if (
$ret == NULL)
        {
            return;
        }
        
        
$ret preg_replace("/[\n]+$/""\n"preg_replace("/^[ \t]*[\n]+/""\n"$ret));

        return 
$ret;
    }

    public function 
start_cmd()
    {
        
$this->urq['input'] = NULL;
        
$this->urq['output'] = '';
        
$this->urq['buttons'] = NULL;
        
$this->urq['forgetprocs'] = FALSE;
        
$this->urq['btn_args'] = 1;
    }

    public function 
reenter()
    {
        
$cur_loc $this->urq['cur_loc'];

        if (
$this->urq['extension_invproc'] === FALSE OR $cur_loc == '')
        {
            return;
        }

        
$ip $this->urq['labels'][$cur_loc];

        
$this->start_cmd();

        
$this->urq['ro'] = TRUE;
        
$this->urq['last_loc'] = $this->exec($ip);
        
$this->urq['ro'] = FALSE;
        
$this->urq['output'] = NULL;
        
$this->urq['input'] = NULL;
    }

    public function 
do_common()
    {
        
$r '';

        if (isset(
$this->urq['vars']['common']) AND $this->urq['vars']['common'] != '')
        {
            
$com 'common_'.$this->urq['vars']['common'];
        }
        else
        {
            
$com 'common';
        }

        if (isset(
$this->urq['labels'][$com]) 
            AND isset(
$this->urq['str']['last_btn_caption'])
            AND 
$this->urq['labels'][$com] != NULL 
            
AND $this->urq['str']['last_btn_caption'] != ''// not init
        
{
            
$r $this->exec(preg_replace("/[\n]+$/"''$this->urq['labels'][$com]));
        }

        
$this->urq['last_com'] = $com;

        return 
$r;
    }

    public function 
autosave($loc)
    {
        
$this->urq['save'] = $this->lower($loc);
    }

    public function 
save_state()
    {
        if (
$this->urq['memcached'] !== FALSE)
        {
            
$m = new Memcached();

            
$m->addServer($this->urq['memcached'], 11211);
            
$m->set(md5($this->urq['filename']), $this->urq['quest']);

            unset(
$this->urq['quest']); // не заливать в БД
        
}

        
$urq $this->urq;

        
/*if ($this->urq['save'] != '' AND isset($this->urq['labels'][$this->urq['save']]))
        {
            $urq['ip'] = $this->urq['labels'][$this->urq['save']];
            $urq['cur_loc'] = $this->urq['save'];
            $urq['last'] = '';
            //var_dump($urq['ip'] );
        }*/

        
return array
        (
            
'urq' => $urq
        
);
    }

    public function 
load_state($state_array)
    {
        if (
is_array($state_array) AND sizeof($state_array) > 0)
        {
            if (
$this->urq['memcached'] !== FALSE)
            {
                
$m = new Memcached();

                
$m->addServer($this->urq['memcached'], 11211);

                
$state_array['urq']['quest'] = $m->get(md5($state_array['urq']['filename']));
            }

            
$this->urq $state_array['urq'];
        }
    }
}

/* EOF Urq.class.php */