<?php
final class SimpleTemplate_SyntaxError extends \Exception {
function __construct($message) {
$this->message = 'SimpleTemplate - Syntax Error: ' . $message;
}
}
final class SimpleTemplate_FN_Invalid extends \Exception {
function __construct($message) {
$this->message = 'SimpleTemplate_FN - Invalid function: ' . $message;
}
}
// contains all functions needed
final class SimpleTemplate_FN {
private static function fn_array_flat(){
$return[] = $value;
});
return $return;
}
private static function fn_inc($_, $by = 1){
// if there's no increment value
if(!$by)
{
return $_;
}
// if there's no value
if(!$_)
{
return $by;
}
$fn = function($_, $by){
{
case 'NULL':
case 'null':
return $by;
case 'integer':
case 'double':
case 'float':
return $_ + $by;
case 'string':
if($_ === '')
{
return '';
}
for($i = 0; $i < $_by; $i++)
{
if($by > 0)
{
++$_;
}
else
{
if($_[$last] === 'a' || $_[$last] === 'A')
{
// handles aaaa -> zzz
}, $_);
}
else
{
$_[$last] = chr(ord($_[$last]) - 1); }
}
}
return $_;
default:
return $by;
}
};
{
$value = $fn($value, $by);
});
}
else
{
$_ = $fn($_, $by);
}
return $_;
}
private static function fn_len($args){
{
}
else
{
}
foreach($args as $arg)
{
{
case 'array':
break;
case 'string':
break;
case 'integer':
case 'double':
case 'float':
$result[] = 0;
break;
default:
$result[] = null;
}
}
return $result;
}
private static function fn_repeat($_, $times = 1){
if($times < 1)
{
return '';
}
});
return $_;
}
// *******************************************************************************
static function call(){
{
throw new SimpleTemplate_FN_Invalid($fn);
}
}
static function name_list(){
if(!$list)
{
{
if(strpos($method, 'fn_') === 0) {
}
}
}
return $list;
}
return isset($exist[$method]) ? $exist[$method]
: (
: $exist[$method]
);
}
}
// compiler class
class SimpleTemplate_Compiler {
private $uuid = null;
private static $var_name = 'DATA';
private static $default_var_name = '_';
private static
$regex = array( 'var' => '(?:(?:(?:U|unsafe|R|ref|reference)\:)?[_a-zA-Z]\w*(?:\.(?:\[[_a-zA-Z]\w*(?:\.\w*)*\]|\w*))*)',
'var_name' => '(?:[_a-zA-Z]\w*)',
'var_simple' => '(?:(?:(?:U|unsafe|R|ref|reference)\:)?[_a-zA-Z]\w*(?:\.\w*)*)',
'value' => '(?:(?:"[^"\\\\]*(?:\\\\.[^"\\\\]*)*")|[\-+]?\d*(?:\.\d*)?|true|false|null)',
'var_value' => '(?:(?:"[^"\\\\]*(?:\\\\.[^"\\\\]*)*")|[\-+]?[\d\W]\d*(?:\.\d*)?|true|false|null|(?:(?:(?:U|unsafe|R|ref|reference)\:)?[_a-zA-Z]\w*(?:\.(?:\[[_a-zA-Z]\w*(?:\.\w*)*\]|\w*))*))'
);
private $options = array(); private $template = null;
private $fn = null;
private $php = '$_ = &$DATA;' . PHP_EOL;
private static function render_var($name = null, $safe = true, $allow_ref = false){
// preg_match('@^\s*(?:(?<unsafe>U|unsafe)\s+)?(?<var>.*)$@', $name ?: self::$default_var_name, $bits);
preg_match('@^\s*(?:(?:(?<unsafe>U|unsafe)|(?<ref>R|ref|reference))\:)?(?<var>.*)$@', $name ?
: self::$default_var_name, $bits);
{
return '[' . self::render_var($matches[1], false) . ']';
}, $bits['var']);
$var = '$' . self::$var_name . ($bits['var'] ?
'[\'' . implode('\'][\'', explode('.', $var)) . '\']' : '');
array('@\'(\w+)\[@', '@\'\]\]\'\]@'), array('\'$1\'][', '\']]'), $var
);
}
else
{
$var = '$' . self::$var_name . ($bits['var'] ?
'[\'' . implode('\'][\'', explode('.', $bits['var'])) . '\']' : ''); }
return $allow_ref && isset($bits['ref']) && $bits['ref'] ? '&' . $var
: (
$safe && !$bits['unsafe']
? '(isset(' . $var . ')?' . $var . ':null)'
: $var
);
}
private static function split_values($values, $delimiter = '\s*,\s*'){
// http://stackoverflow.com/a/5696141/ --> regex quoted string
// http://stackoverflow.com/a/632552/ --> regex to match $delimiter outside quotes
return preg_split('@(' . ($delimiter ?
: '\s*,\s*') . ')(?=(?:[^"]|"[^"\\\\]*(?:\\\\.[^"\\\\]*)*")*$)@', $values); }
private static function parse_values($values, $delimiter = '\s*,\s*', $safe = true, $allow_ref = true){
$value_bits = self::split_values($values, $delimiter);
foreach($value_bits as $k => $value)
{
$value_bits[$k] = self::parse_value($value, $safe, $allow_ref);
}
return $value_bits;
}
private static function parse_boolean($data){
if(
'@(' . self::$regex['var_value'] . ')\s*(?:(isset|is(?:(?:\s*not|n\'?t)?\s*(?:(?:greater|lower)(?:\s*than)?|equal(?:\s*to)?|equal|a|(?:(?:instance|multiple|mod)(?:\s*of)?)|matches))?|has(?:\s*not)?|(?:not\s*)?matches)\s*(' . self::$regex['var_value'] . ')?)?(?:\s*(.+))?@',
$data, $bits
)
)
{
return '';
}
'is' => function($data, $var1, $var2){
'' => '%s === %s',
'a' => 'gettype(%s) === %s',
'instance' => 'is_a(%s, %s)',
'equal' => '%s == %s',
'lower' => '%s < %s',
'greater' => '%s > %s',
'multiple' => '!(%s %% %s)',
'mod' => '%s %% %s',
'matches' => 'preg_match(%2$s, %1$s)'
);
preg_match('@(?<not>not)?\s*(?<operation>equal|lower|greater|a|instance|multiple|mod|matches)?\s*(?:of|to|than)?\s*@', $data, $bits);
return (isset($bits['not']) && $bits['not'] !== '' ?
'!': '') . '(' . sprintf($symbols[isset($bits['operation']) ?
$bits['operation']: ''], self::parse_value($var1), self::parse_value($var2)) . ')'; },
'has' => function($data, $var1, $var2){
return ($data === 'not' ? '!': '') . 'array_key_exists(' . self::parse_value($var2) . ', (array)' . self::parse_value($var1) . ')';
},
'isset' => function($data, $var1){
return ($data === 'not' ? '!': '') . 'isset(' . self::render_var($var1, false) . ')';
},
'matches' => function($data, $var1, $var2, $extra){
if($extra && self::is_var($extra = self::parse_value($extra, false)))
{
return ($data === 'not' ? '!': '') . 'preg_match(' . self::parse_value($var2) . ', ' . self::parse_value($var1) . ', ' . $extra . ')';
}
else
{
return ($data === 'not' ? '!': '') . 'preg_match(' . self::parse_value($var2) . ', ' . self::parse_value($var1) . ')';
}
}
);
{
return $fn[$ops[0]](
isset($ops[1]) ?
$ops[1]: '', $bits[1],
isset($bits[3]) ?
$bits[3] : self::$default_var_name, isset($bits[4]) ?
$bits[4] : '' );
}
else
{
return self::parse_value($bits[1]);
}
}
private static function parse_value($value, $safe = true, $allow_ref = false){
if($value === '' || $value === '""')
{
return $value;
}
else if(preg_match('@^' . self::$regex['value'] . '$@', $value)) {
return $value[0] === '"'
? self::parse_value_string($value)
: $value;
}
else if(preg_match('@^' . self::$regex['var'] . '$@', $value)) {
return self::render_var($value, $safe, $allow_ref);
}
else
{
throw new SimpleTemplate_SyntaxError('Invalid value syntax: ' . $value);
}
}
private static function parse_value_string($value){
'@#{([_a-zA-Z]\w*(?:\.(?:\[[_a-zA-Z]\w*(?:\.\w*)*\]|\w*))*)}@',
function($matches){
return '{' . self::render_var($matches[1], false) . '}';
},
);
}
private static function is_value($value){
&& $value[0] !== '$'
&& $value[0] !== '('
&& $value[0] !== '&'
/*&& (
$value[0] !== '"'
|| strpos($value, '{$' . self::$var_name) === false
)*/;
}
private static function is_var($value){
&& (
$value[0] === '$'
|| $value[0] === '('
|| $value[0] === '&'
);
}
private function format_code($code, $tabs, $skip_first = false, $skip_last = false){
$heredoc_closing = self::$var_name . $this->uuid;
$last = $skip_last ? PHP_EOL
. array_pop($lines): '';
foreach($lines as $line)
{
if($return)
{
$return .= PHP_EOL;
}
if($line === $heredoc_closing)
{
$return .= $heredoc_closing;
}
else
{
$return .= (
: $tabs
}
}
return $return . $last;
}
private function compile($str){
$UUID = $this->uuid;
$brackets = 0;
$tabs = '';
'/' => function($data)use(&$replacement, &$brackets, &$tabs){
if($brackets > 0)
{
--$brackets;
return $tabs . '};';
}
else
{
return $tabs . ';';
}
},
'//' => function(){
return '';
},
'echo' => function($data)use(&$replacement, &$brackets, &$tabs){
preg_match('@^(?:separator\s+(?<separator>' . self::$regex['var_value'] . ')\s*)?(?<data>.*)$@', $data, $bits);
$separator = $bits['separator'] ? self::parse_value($bits['separator']): '\'\'';
return $tabs . 'echo implode(' . $separator . ', SimpleTemplate_FN::call(\'array_flat\', ' . implode(', ', self::parse_values($bits['data'])) . '));'; },
'echol' => function($data)use(&$replacement, &$brackets, &$tabs){
return $replacement['echo']($data) . 'echo PHP_EOL;';
},
'echoj' => function($data)use(&$replacement, &$brackets, &$tabs){
return $replacement['echo']('separator ' . $data);
},
'echojl' => function($data)use(&$replacement, &$brackets, &$tabs){
return $replacement['echol']('separator ' . $data);
},
'echof' => function($data)use(&$replacement, &$brackets, &$tabs){
return $replacement['echol']('separator ' . $data);
},
'print' => function($data)use(&$replacement, &$brackets, &$tabs){
return $replacement['call']((strpos('into', $data) === 0 ?
's' : '') . 'printf ' . $data); },
'if' => function($data)use(&$replacement, &$brackets, &$tabs){
++$brackets;
return $tabs . 'if(' . self::parse_boolean($data) . ') {';
},
'else' => function($data)use(&$replacement, &$brackets, &$tabs){
preg_match('@(?<if>if)(?<data>.*)@', $data, $bits);
$return = substr($tabs, 1) . '} else';
{
--$brackets;
$return .= ltrim($replacement['if'](trim($bits['data']))); }
else
{
$return .= ' {';
}
return $return;
},
'each' => function($data)use(&$replacement, &$brackets, &$tabs){
++$brackets;
preg_match('@^(?<var>' . self::$regex['var_value'] . ')\s*(?:as\s*(?<as>' . self::$regex['var'] . ')(?:\s*key\s*(?<key>' . self::$regex['var'] . ')\s*)?)?$@', $data, $bits);
static $count = 0;
$var_name = self::$var_name;
$tmp_name = 'tmp_' . (++$count) . '_';
$vars_var = self::parse_value(isset($bits['var']) ?
$bits['var'] : '_', false); $vars_as = self::render_var(isset($bits['as']) ?
$bits['as'] : '', false); $vars_key = self::render_var(isset($bits['key']) ?
$bits['key'] : '__', false);
if(self::is_var($vars_var))
{
return <<<PHP
{$tabs}// ~ optimization unfeasable ~ variable used in the first argument
{$tabs}// loop variables - variable
{$tabs}\${$tmp_name}val = isset({$vars_var}) ? \${$tmp_name}val = &{$vars_var} : null;
{$tabs}\${$tmp_name}keys = gettype({$vars_var}) == 'array'
{$tabs} ? array_keys({$vars_var})
{$tabs} : ((\${$tmp_name}val = \${$tmp_name}val . '')
{$tabs} ? array_keys(range(0, strlen(\${$tmp_name}val = \${$tmp_name}val . '') - 1))
{$tabs} : array()
{$tabs} );
{$tabs}\${$tmp_name}key_last = end(\${$tmp_name}keys);
{$tabs}
{$tabs}// loop
{$tabs}foreach(\${$tmp_name}keys as \${$tmp_name}index => \${$tmp_name}key){
{$tabs} \${$var_name}['loop'] = array(
{$tabs} 'index' => \${$tmp_name}index,
{$tabs} 'i' => \${$tmp_name}index,
{$tabs} 'key' => \${$tmp_name}key,
{$tabs} 'k' => \${$tmp_name}key,
{$tabs} 'value' => \${$tmp_name}val[\${$tmp_name}key],
{$tabs} 'v' => \${$tmp_name}val[\${$tmp_name}key],
{$tabs} 'first' => \${$tmp_name}key === \${$tmp_name}keys[0],
{$tabs} 'last' => \${$tmp_name}key === \${$tmp_name}key_last,
{$tabs} );
{$tabs} {$vars_key} = \${$tmp_name}key;
{$tabs} {$vars_as} = \${$tmp_name}val[\${$tmp_name}key];
PHP;
}
else if($this->options['optimize'])
{
$keys = '';
if($vars_var !== '""')
{
',',
0,
strlen($vars_var) - ($vars_var[0] === '"' ?
3 : 1) )
);
}
return <<<PHP
{$tabs}// ~ optimization ENABLED ~ \${$tmp_name}keys was inlined
{$tabs}// loop variables - inline value
{$tabs}\${$tmp_name}val = ({$vars_var}) . ''; // convert to string
{$tabs}\${$tmp_name}keys = array({$keys});
{$tabs}\${$tmp_name}key_last = end(\${$tmp_name}keys);
{$tabs}
{$tabs}// loop
{$tabs}foreach(\${$tmp_name}keys as \${$tmp_name}index => \${$tmp_name}key){
{$tabs} \${$var_name}['loop'] = array(
{$tabs} 'index' => \${$tmp_name}index,
{$tabs} 'i' => \${$tmp_name}index,
{$tabs} 'key' => \${$tmp_name}key,
{$tabs} 'k' => \${$tmp_name}key,
{$tabs} 'value' => \${$tmp_name}val[\${$tmp_name}key],
{$tabs} 'v' => \${$tmp_name}val[\${$tmp_name}key],
{$tabs} 'first' => \${$tmp_name}key === \${$tmp_name}keys[0],
{$tabs} 'last' => \${$tmp_name}key === \${$tmp_name}key_last,
{$tabs} );
{$tabs} {$vars_key} = \${$tmp_name}key;
{$tabs} {$vars_as} = \${$tmp_name}val[\${$tmp_name}key];
PHP;
}
else
{
return <<<PHP
{$tabs}// ~ optimization DISABLED ~ \${$tmp_name}keys could be inlined
{$tabs}// loop variables - inline value
{$tabs}\${$tmp_name}val = ({$vars_var}) . ''; // convert to string
{$tabs}\${$tmp_name}keys = \${$tmp_name}val
{$tabs} ? array_keys(range(0, strlen(\${$tmp_name}val) - 1))
{$tabs} : array();
{$tabs}\${$tmp_name}key_last = end(\${$tmp_name}keys);
{$tabs}
{$tabs}// loop
{$tabs}foreach(\${$tmp_name}keys as \${$tmp_name}index => \${$tmp_name}key){
{$tabs} \${$var_name}['loop'] = array(
{$tabs} 'index' => \${$tmp_name}index,
{$tabs} 'i' => \${$tmp_name}index,
{$tabs} 'key' => \${$tmp_name}key,
{$tabs} 'k' => \${$tmp_name}key,
{$tabs} 'value' => \${$tmp_name}val[\${$tmp_name}key],
{$tabs} 'v' => \${$tmp_name}val[\${$tmp_name}key],
{$tabs} 'first' => \${$tmp_name}key === \${$tmp_name}keys[0],
{$tabs} 'last' => \${$tmp_name}key === \${$tmp_name}key_last,
{$tabs} );
{$tabs} {$vars_key} = \${$tmp_name}key;
{$tabs} {$vars_as} = \${$tmp_name}val[\${$tmp_name}key];
PHP;
}
},
'while' => function($data)use(&$replacement, &$brackets, &$tabs){
++$brackets;
return $tabs . 'while(' . self::parse_boolean($data) . '){';
},
'for' => function($data)use(&$replacement, &$brackets, &$tabs){
++$brackets;
'@(?<var>' . self::$regex['var'] . ')?\s*(?:from\s*(?<start>' . self::$regex['var_value'] . '))?(?:\s*to\s*(?<end>' . self::$regex['var_value'] . '))(?:\s*step\s*(?<step>' . self::$regex['var_value'] . '))?@',
function($matches)use(&$replacement, &$brackets, &$tabs){
'start' => isset($matches['start']) && $matches['start'] !== '' ?
self::parse_value($matches['start']) : '0', 'end' => isset($matches['end']) ?
self::parse_value($matches['end']) : self::parse_value($matches['start']), 'step' => isset($matches['step']) ?
self::parse_value($matches['step']) : '1' );
$return = $tabs . 'foreach(';
if(self::is_value($values['start']) && self::is_value($values['end']) && self::is_value($values['step']))
{
if($this->options['optimize'])
{
$return = "{$tabs}// ~ optimization enabled ~ inlining the results\r\n{$return}" . self::format_code(
),
true
),
$tabs . "\t",
true
);
}
else
{
$return = "{$tabs}// ~ optimization DISABLED ~ results could be inlined\r\n{$return}range({$values['start']}, {$values['end']}, abs({$values['step']}))";
}
}
else
{
$return .= 'range(' . $values['start'] . ', ' . $values['end'] . ', abs(' . $values['step'] . '))';
}
return $return . ' as ' . self::render_var(isset($matches['var']) ?
$matches['var'] : '', false) . '){'; },
$data
);
},
'do' => function($data)use(&$replacement, &$brackets, &$tabs){
++$brackets;
return $tabs . 'do{';
},
'until' => function($data)use(&$replacement, &$brackets, &$tabs){
--$brackets;
return substr($tabs, 1) . '}while(!(' . self::parse_boolean($data) . '));'; },
'set' => function($data)use(&$replacement, &$brackets, &$tabs){
preg_match('@^\s*(?<op>[\+\-\*\\\/\%])?\s*(?<var>' . self::$regex['var'] . ')\s*(?:(?<op_val>' . self::$regex['var_value'] . ')\s)?\s*(?<values>.*)$@', $data, $bits);
$values = self::parse_values($bits['values']);
$return = $tabs . self::render_var($bits['var'], false) . ' = ';
$close = 0;
{
switch($bits['op'])
{
case '-':
$return .= <<<PHP
call_user_func_array(function(){
{$tabs} \$args = func_get_args();
{$tabs} \$initial = array_shift(\$args);
{$tabs} return array_reduce(\$args, function(\$carry, \$value){
{$tabs} return \$carry - \$value;
{$tabs} }, \$initial);
{$tabs}}, SimpleTemplate_FN::call('array_flat',
PHP;
$close = 2;
break;
case '+':
$return .= 'array_sum(SimpleTemplate_FN::call(\'array_flat\', ';
$close = 2;
break;
case '*':
$return .= 'array_product(SimpleTemplate_FN::call(\'array_flat\', ';
$close = 2;
break;
case '\\':
case '/':
case '%':
'\\' => 'round(%s / $value)',
'/' => '(%s / $value)',
'%' => '(%s %% $value)'
);
$return .= 'array_map(function($value)use(&$' . self::$var_name . '){'
$ops[$bits['op']],
? self::parse_value($bits['op_val'])
: self::render_var($bits['var'], false)
)
. ';}, SimpleTemplate_FN::call(\'array_flat\', ';
$close = 2;
break;
}
}
if($count > 1)
{
$return .= 'array(' . implode(',', $values) . ')'; }
else
{
$return .= ($count && strlen($values[0]) ?
$values[0] : 'null'); }
},
'unset' => function($data)use(&$replacement, &$brackets, &$tabs){
$values = self::parse_values($data, null, false);
return !self::is_value($var);
});
$return = $tabs . (
? '// Warning: invalid values were passed' . PHP_EOL . $tabs
: ''
);
return $return . (
$vars
?
'unset(' . implode(',', $vars) . ');' : '// Warning: no values were passed or all were filtered out'
);
},
'global' => function($data)use(&$replacement, &$brackets, &$tabs){
$data = self::split_values($data, ' ');
return $tabs . self::render_var(array_shift($data), false) . ' = $GLOBALS[\'' . join('\'][\'', $data) . '\'];'; },
'call' => function($data)use(&$replacement, &$brackets, &$tabs){
preg_match('@^\s*(?<fn>' . self::$regex['var'] . ')\s*(?:into\s*(?<into>' . self::$regex['var'] . ')\s*)?(?<args>.*?)$@', $data, $bits);
$var = self::render_var($bits['fn'], false);
$into = $bits['into'] ? self::render_var($bits['into'], false) . ' = ' : '';
$args = implode(', ', self::parse_values($bits['args']));
return <<<PHP
{$tabs}{$into}call_user_func_array(
{$tabs} (isset({$var}) && is_callable({$var})
{$tabs} ? {$var}
{$tabs} : (SimpleTemplate_FN::method_exists('{$bits['fn']}')
{$tabs} ? SimpleTemplate_FN::call('{$bits['fn']}')
{$tabs} : '{$alt_name}'
{$tabs} )
{$tabs} ),
{$tabs} array({$args})
{$tabs});
PHP;
},
'php' => function($data)use(&$replacement, &$brackets, &$tabs){
return $tabs . 'call_user_func_array(function(&$' . self::$var_name . '){' . PHP_EOL
. "{$tabs}\t{$data};" . PHP_EOL
. $tabs . '}, array(&$' . self::$var_name . '));';
},
'return' => function($data)use(&$replacement, &$brackets, &$tabs){
return $tabs . 'return' . ($data || $data === '0' ? ' ' . self::parse_value($data) : '') . ';';
},
'inc' => function($data)use(&$replacement, &$brackets, &$tabs){
preg_match('@^(?:\s*by\s*(?<by>' . self::$regex['var_value'] . ')\s*)?(?<values>.*?)$@', $data, $bits); $values = self::parse_values($bits['values'], '\s*,\s*', false);
$inc = isset($bits['by']) && $bits['by'] !== '' ?
self::parse_value($bits['by']): '1';
$return = '';
if(!$inc || $inc === '"0"' || $inc === 'null' || $inc === 'false')
{
if($this->options['optimize'])
{
return "{$tabs}// ~ optimization enabled ~ increment by {$inc} removed";
}
else
{
$return .= "{$tabs}// ~ optimization DISABLED ~ increment by {$inc} could be removed" . PHP_EOL;
}
}
$var_name = self::$var_name;
foreach($values as $value)
{
if(!isset($value[0]) && !self::is_value($value[0])) {
continue;
}
$return .= "{$tabs}{$value} = SimpleTemplate_FN::call('inc', isset({$value})?{$value}:0, {$inc});" . PHP_EOL;
}
return $return;
},
'fn' => function($data)use(&$replacement, &$brackets, &$tabs){
if(
'@^\s*(' . self::$regex['var_simple'] . ')(?:\s+(' . self::$regex['var_name'] . '(?:,\s*' . self::$regex['var_name'] . ')*))?$@',
$data, $bits
) === false
)
{
return '';
}
++$brackets;
$version = SimpleTemplate::version();
$var_name = self::$var_name;
$return = $tabs . self::render_var($bits ? $bits[1] : null, false) . <<<PHP
= function()use(&\$_){
{$tabs} \${$var_name} = array(
{$tabs} 'argv' => func_get_args(),
{$tabs} 'argc' => func_num_args(),
{$tabs} 'VERSION' => '{$version}',
{$tabs} 'EOL' => PHP_EOL,
{$tabs} 'PARENT' => &\$_
{$tabs} );
{$tabs} \$_ = &\${$var_name};
PHP;
if(isset($bits[2]) && $bits[2]) {
foreach(self::split_values($bits[2]) as $value)
{
if(!self::is_value(self::parse_value($value)))
{
$args[] = $value;
}
}
foreach($args as $k => $arg)
{
$return .= "{$tabs} \${$var_name}[\"{$arg}\"] = &\${$var_name}[\"argv\"][{$k}];" . PHP_EOL;
}
}
return $return;
},
'eval' => function($data)use(&$replacement, &$brackets, &$tabs){
$return = '';
$value = self::parse_value($data);
if($this->options['optimize'] && self::is_value($value))
{
$return = $tabs . '// ~ optimization enabled ~ trying to avoid compiling in runtime' . PHP_EOL;
static
$cached = array();
if(isset($cached[$sha1])) {
$return .= $tabs . '// {@eval} cached code found: cache entry ';
}
else
{
$return .= $tabs . '// {@eval} no cached code found: creating entry ';
$compiler = new SimpleTemplate_Compiler
($this->template, stripslashes(trim($value, '"')), $this->options);
$cached[$sha1] = self::format_code($compiler->getPHP() . '// {@eval} ended', $tabs);
}
$return .= $sha1 . PHP_EOL . $cached[$sha1];
}
else
{
$options = self::format_code(var_export($this->options, true), $tabs . "\t\t", true);
$return = <<<PHP
{$tabs}// ~ optimization DISABLED or unfeasable ~ compilation in runtime is required
{$tabs}call_user_func_array(function()use(&\$DATA){
{$tabs} \$compiler = new SimpleTemplate_Compiler(\$this, {$value}, {$options});
{$tabs} \$fn = \$compiler->getFN();
{$tabs} return \$fn(\$DATA);
{$tabs}}, array());
PHP;
}
return $return;
}
);
$trim_fn = $this->options['trim'] ? 'trim' : '';
$this->php .= "\r\necho {$trim_fn}(<<<'" . self::$var_name . "{$UUID}'\r\n"
// http://stackoverflow.com/a/6464500
'~{@(eval|echoj?l?|print|if|else|for|while|each|do|until|(?:un)?set|call|global|php|return|inc|fn|//?)(?:\\s*(.*?))?}(?=(?:[^"\\\\]*(?:\\\\.|"(?:[^"\\\\]*\\\\.)*[^"\\\\]*"))*[^"]*$)~',
function($matches)use(&$replacement, &$brackets, &$tabs, &$UUID, &$trim_fn){
$tabs = $brackets
?
str_repeat("\t", $brackets - ($matches[1] === '/')) : '';
$var_name = self::$var_name;
$php = $replacement[$matches[1]](isset($matches[2]) ?
$matches[2] : null);
return "\r\n{$var_name}{$UUID}\r\n);\r\n{$tabs}// {$matches[0]}\r\n{$php}\r\n\r\n{$tabs}echo {$trim_fn}(<<<'{$var_name}{$UUID}'\r\n";
},
$str . ''
)
. "\r\n" . self::$var_name . "{$UUID}\r\n);\r\n";
'@\r\n\t*echo\s*' . $trim_fn . '\(<<<\'' . self::$var_name . $UUID . '\'(?:\s*\r\n)?' . self::$var_name . $UUID . '\r\n\);@',
'@\r\n' . self::$var_name . $UUID . '\r\n\);(\r\n)*\t*echo\s*' . $trim_fn . '\(<<<\'' . self::$var_name . $UUID . '\'@'
),
'', ''
),
$this->php
);
if($brackets)
{
$this->php .= "\r\n// AUTO-CLOSE\r\n" . str_repeat('};', $brackets); }
}
function getPHP(){
return $this->php;
}
function getFN(){
if(!$this->fn)
{
$this->fn = eval('return function(&$' . self::$var_name . '){' . PHP_EOL . $this->php . PHP_EOL
. '};'
);
$this->fn = $this->fn->bindTo($this->template);
}
return $this->fn;
}
function __construct
(SimpleTemplate
$template, $code, array $options = array()){ $this->options = $options;
$this->template = $template;
// ALMOST unguessable name, to avoid syntax errors
$this->compile($code);
}
}
// base class
class SimpleTemplate {
private static $version = '0.84';
private $settings = array( 'optimize' => true,
'trim' => false
);
private $compiler = null;
function __construct
($code, array $options = array()){ if(!$code)
{
throw new Exception('No code was provided');
}
if($options)
{
$this->settings = array_merge($this->settings, $options); }
$this->compiler = new SimpleTemplate_Compiler($this, $code, $this->settings);
}
function setData($key, $value){
$this->data[$key] = $value;
}
function getData($key, $value){
return isset($this->data[$key]) ?
$this->data[$key] : null; }
function unsetData($key){
unset($this->data[$key]); }
function loadData($data){
foreach($data as $k => $value)
{
$this->data[$k] = $value;
}
}
function clearData(){
}
function getPHP(){
return $this->compiler->getPHP();
}
function render(){
$this->data['VERSION'] = self::$version;
$this->data['EOL'] = PHP_EOL;
$fn = $this->compiler->getFN();
return $fn($this->data);
}
static
function fromFile
($path, array $options = array()){ }
static
function fromString
($string, array $options = array()){ return new self($string, $options);
}
static function version(){
return self::$version;
}
}
$code = <<<'CODE'
{@fori to51}{@forto77}<b{@ifi is lowerthan26} x{@/}>@{@/}<br>{@/}<style>b{color:#0057B7}[x]{color:gold
CODE;
$key = 'ungolfed'; // golfed - ungolfed
$x = new SimpleTemplate($c = $code);
echo $x->render(), PHP_EOL, PHP_EOL, '----------------- v PHP v -----------------', PHP_EOL, $x->getPHP();
echo PHP_EOL
, PHP_EOL
, '----------------- v CODE (', strlen($c), 'b - ', $key, ') v -----------------', PHP_EOL
, $c;
PD9waHAKCmZpbmFsIGNsYXNzIFNpbXBsZVRlbXBsYXRlX1N5bnRheEVycm9yIGV4dGVuZHMgXEV4Y2VwdGlvbiB7CglmdW5jdGlvbiBfX2NvbnN0cnVjdCgkbWVzc2FnZSkgewoJCSR0aGlzLT5tZXNzYWdlID0gJ1NpbXBsZVRlbXBsYXRlIC0gU3ludGF4IEVycm9yOiAnIC4gJG1lc3NhZ2U7Cgl9Cn0KCmZpbmFsIGNsYXNzIFNpbXBsZVRlbXBsYXRlX0ZOX0ludmFsaWQgZXh0ZW5kcyBcRXhjZXB0aW9uIHsKCWZ1bmN0aW9uIF9fY29uc3RydWN0KCRtZXNzYWdlKSB7CgkJJHRoaXMtPm1lc3NhZ2UgPSAnU2ltcGxlVGVtcGxhdGVfRk4gLSBJbnZhbGlkIGZ1bmN0aW9uOiAnIC4gJG1lc3NhZ2U7Cgl9Cn0KCi8vIGNvbnRhaW5zIGFsbCBmdW5jdGlvbnMgbmVlZGVkCmZpbmFsIGNsYXNzIFNpbXBsZVRlbXBsYXRlX0ZOIHsKCXByaXZhdGUgc3RhdGljIGZ1bmN0aW9uIGZuX2FycmF5X2ZsYXQoKXsKCQkkcmV0dXJuID0gYXJyYXkoKTsKCQkkYXJyYXkgPSBmdW5jX2dldF9hcmdzKCk7CgkJYXJyYXlfd2Fsa19yZWN1cnNpdmUoJGFycmF5LCBmdW5jdGlvbigkdmFsdWUpdXNlKCYkcmV0dXJuKXsKCQkJJHJldHVybltdID0gJHZhbHVlOwoJCX0pOwoJCXJldHVybiAkcmV0dXJuOwoJfQoJCglwcml2YXRlIHN0YXRpYyBmdW5jdGlvbiBmbl9pbmMoJF8sICRieSA9IDEpewoJCS8vIGlmIHRoZXJlJ3Mgbm8gaW5jcmVtZW50IHZhbHVlCgkJaWYoISRieSkKCQl7CgkJCXJldHVybiAkXzsKCQl9CgkJCgkJLy8gaWYgdGhlcmUncyBubyB2YWx1ZQoJCWlmKCEkXykKCQl7CgkJCXJldHVybiAkYnk7CgkJfQoJCQoJCSRmbiA9IGZ1bmN0aW9uKCRfLCAkYnkpewoJCQlzd2l0Y2goZ2V0dHlwZSgkXykpCgkJCXsKCQkJCWNhc2UgJ05VTEwnOgoJCQkJY2FzZSAnbnVsbCc6CgkJCQkJcmV0dXJuICRieTsKCQkJCWNhc2UgJ2ludGVnZXInOgoJCQkJY2FzZSAnZG91YmxlJzoKCQkJCWNhc2UgJ2Zsb2F0JzoKCQkJCQlyZXR1cm4gJF8gKyAkYnk7CgkJCQljYXNlICdzdHJpbmcnOgoJCQkJCWlmKCRfID09PSAnJykKCQkJCQl7CgkJCQkJCXJldHVybiAnJzsKCQkJCQl9CgkJCQkJCgkJCQkJJF9ieSA9IGFicygkYnkpOwoJCQkJCQoJCQkJCWZvcigkaSA9IDA7ICRpIDwgJF9ieTsgJGkrKykKCQkJCQl7CgkJCQkJCWlmKCRieSA+IDApCgkJCQkJCXsKCQkJCQkJCSsrJF87CgkJCQkJCX0KCQkJCQkJZWxzZQoJCQkJCQl7CgkJCQkJCQkkbGFzdCA9IHN0cmxlbigkXykgLSAxOwoJCQkJCQkJCgkJCQkJCQlpZigkX1skbGFzdF0gPT09ICdhJyB8fCAkX1skbGFzdF0gPT09ICdBJykKCQkJCQkJCXsKCQkJCQkJCQkvLyBoYW5kbGVzIGFhYWEgLT4genp6CgkJCQkJCQkJJF8gPSBwcmVnX3JlcGxhY2VfY2FsbGJhY2soJ0BbYUFdKyRAJywgZnVuY3Rpb24oJHN0cil7CgkJCQkJCQkJCXJldHVybiBzdHJfcmVwZWF0KCRzdHJbMF1bMF0gPT09ICdhJyA/ICd6JzogJ1onLCBzdHJsZW4oJHN0clswXSkgLSAxKTsKCQkJCQkJCQl9LCAkXyk7CgkJCQkJCQl9CgkJCQkJCQllbHNlCgkJCQkJCQl7CgkJCQkJCQkJJF9bJGxhc3RdID0gY2hyKG9yZCgkX1skbGFzdF0pIC0gMSk7CgkJCQkJCQl9CgkJCQkJCX0KCQkJCQl9CgkJCQkJCgkJCQkJcmV0dXJuICRfOwoJCQkJZGVmYXVsdDoKCQkJCQlyZXR1cm4gJGJ5OwoJCQl9CgkJfTsKCgkJCgkJaWYoZ2V0dHlwZSgkXykgPT09ICdhcnJheScpCgkJewoJCQlhcnJheV93YWxrX3JlY3Vyc2l2ZSgkXywgZnVuY3Rpb24oJiR2YWx1ZSl1c2UoJiRmbiwgJiRieSl7CgkJCQkkdmFsdWUgPSAkZm4oJHZhbHVlLCAkYnkpOwoJCQl9KTsKCQl9CgkJZWxzZQoJCXsKCQkJJF8gPSAkZm4oJF8sICRieSk7CgkJfQoJCQoJCXJldHVybiAkXzsKCX0KCQoJcHJpdmF0ZSBzdGF0aWMgZnVuY3Rpb24gZm5fbGVuKCRhcmdzKXsKCQkkcmVzdWx0ID0gYXJyYXkoKTsKCQkKCQlpZihmdW5jX251bV9hcmdzKCkgPiAxKQoJCXsKCQkJJGFyZ3MgPSBmdW5jX2dldF9hcmdzKCk7CgkJfQoJCWVsc2UKCQl7CgkJCSRhcmdzID0gYXJyYXkoJGFyZ3MpOwoJCX0KCQkKCQlmb3JlYWNoKCRhcmdzIGFzICRhcmcpCgkJewoJCQlzd2l0Y2goZ2V0dHlwZSgkYXJnKSkKCQkJewoJCQkJY2FzZSAnYXJyYXknOgoJCQkJCSRyZXN1bHRbXSA9IGNvdW50KCRhcmcpOwoJCQkJCWJyZWFrOwoJCQkJY2FzZSAnc3RyaW5nJzoKCQkJCQkkcmVzdWx0W10gPSBzdHJsZW4oJGFyZyk7CgkJCQkJYnJlYWs7CgkJCQljYXNlICdpbnRlZ2VyJzoKCQkJCWNhc2UgJ2RvdWJsZSc6CgkJCQljYXNlICdmbG9hdCc6CgkJCQkJJHJlc3VsdFtdID0gMDsKCQkJCQlicmVhazsKCQkJCWRlZmF1bHQ6CgkJCQkJJHJlc3VsdFtdID0gbnVsbDsKCQkJfQoJCX0KCQkKCQlyZXR1cm4gJHJlc3VsdDsKCX0KCQoJcHJpdmF0ZSBzdGF0aWMgZnVuY3Rpb24gZm5fcmVwZWF0KCRfLCAkdGltZXMgPSAxKXsKCQlpZigkdGltZXMgPCAxKQoJCXsKCQkJcmV0dXJuICcnOwoJCX0KCQkKCQlhcnJheV93YWxrX3JlY3Vyc2l2ZSgkXywgZnVuY3Rpb24oJiR2YWx1ZSl1c2UoJiR0aW1lcyl7CgkJCSR2YWx1ZSA9IHN0cl9yZXBlYXQoJHZhbHVlLCAkdGltZXMpOwoJCX0pOwoJCQoJCXJldHVybiAkXzsKCX0KCQoJLy8gKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKgoJCglzdGF0aWMgZnVuY3Rpb24gY2FsbCgpewoJCSRhcmdzID0gZnVuY19nZXRfYXJncygpOwoJCSRmbiA9IGFycmF5X3NoaWZ0KCRhcmdzKTsKCQkKCQlpZighc2VsZjo6bWV0aG9kX2V4aXN0cygkZm4pKQoJCXsKCQkJdGhyb3cgbmV3IFNpbXBsZVRlbXBsYXRlX0ZOX0ludmFsaWQoJGZuKTsKCQl9CgkJCgkJcmV0dXJuIGNhbGxfdXNlcl9mdW5jX2FycmF5KGFycmF5KF9fQ0xBU1NfXywgJ2ZuXycgLiAkZm4pLCAkYXJncyk7Cgl9CgkKCXN0YXRpYyBmdW5jdGlvbiBuYW1lX2xpc3QoKXsKCQlzdGF0aWMgJGxpc3QgPSBhcnJheSgpOwoJCQoJCWlmKCEkbGlzdCkKCQl7CgkJCWZvcmVhY2goZ2V0X2NsYXNzX21ldGhvZHMoX19DTEFTU19fKSBhcyAkbWV0aG9kKQoJCQl7CgkJCQlpZihzdHJwb3MoJG1ldGhvZCwgJ2ZuXycpID09PSAwKQoJCQkJewoJCQkJCSRsaXN0W10gPSBwcmVnX3JlcGxhY2UoJ0BeZm5fQCcsICcnLCAkbWV0aG9kKTsKCQkJCX0KCQkJfQoJCX0KCQkKCQlyZXR1cm4gJGxpc3Q7Cgl9CgkKCXN0YXRpYyBmdW5jdGlvbiBtZXRob2RfZXhpc3RzKCRtZXRob2QpewoJCXN0YXRpYyAkZXhpc3QgPSBhcnJheSgpOwoJCQoJCXJldHVybiBpc3NldCgkZXhpc3RbJG1ldGhvZF0pCgkJCT8gJGV4aXN0WyRtZXRob2RdCgkJCTogKAoJCQkJaXNfbnVsbCgkZXhpc3RbJG1ldGhvZF0gPSBAbWV0aG9kX2V4aXN0cyhfX0NMQVNTX18sICdmbl8nIC4gJG1ldGhvZCkpCgkJCQkJPyBpbl9hcnJheSgkbWV0aG9kLCBzZWxmOjpuYW1lX2xpc3QoKSkKCQkJCQk6ICRleGlzdFskbWV0aG9kXQoJCQkpOwoJfQp9CgovLyBjb21waWxlciBjbGFzcwpjbGFzcyBTaW1wbGVUZW1wbGF0ZV9Db21waWxlciB7Cglwcml2YXRlICR1dWlkID0gbnVsbDsKCQoJcHJpdmF0ZSBzdGF0aWMgJHZhcl9uYW1lID0gJ0RBVEEnOwoJcHJpdmF0ZSBzdGF0aWMgJGRlZmF1bHRfdmFyX25hbWUgPSAnXyc7CgkKCXByaXZhdGUgc3RhdGljICRyZWdleCA9IGFycmF5KAoJCSd2YXInID0+ICcoPzooPzooPzpVfHVuc2FmZXxSfHJlZnxyZWZlcmVuY2UpXDopP1tfYS16QS1aXVx3Kig/OlwuKD86XFtbX2EtekEtWl1cdyooPzpcLlx3KikqXF18XHcqKSkqKScsCgkJJ3Zhcl9uYW1lJyA9PiAnKD86W19hLXpBLVpdXHcqKScsCgkJJ3Zhcl9zaW1wbGUnID0+ICcoPzooPzooPzpVfHVuc2FmZXxSfHJlZnxyZWZlcmVuY2UpXDopP1tfYS16QS1aXVx3Kig/OlwuXHcqKSopJywKCQkndmFsdWUnID0+ICcoPzooPzoiW14iXFxcXF0qKD86XFxcXC5bXiJcXFxcXSopKiIpfFtcLStdP1xkKig/OlwuXGQqKT98dHJ1ZXxmYWxzZXxudWxsKScsCgkJJ3Zhcl92YWx1ZScgPT4gJyg/Oig/OiJbXiJcXFxcXSooPzpcXFxcLlteIlxcXFxdKikqIil8W1wtK10/W1xkXFddXGQqKD86XC5cZCopP3x0cnVlfGZhbHNlfG51bGx8KD86KD86KD86VXx1bnNhZmV8UnxyZWZ8cmVmZXJlbmNlKVw6KT9bX2EtekEtWl1cdyooPzpcLig/OlxbW19hLXpBLVpdXHcqKD86XC5cdyopKlxdfFx3KikpKikpJwoJKTsKCQoJcHJpdmF0ZSAkb3B0aW9ucyA9IGFycmF5KCk7Cglwcml2YXRlICR0ZW1wbGF0ZSA9IG51bGw7CgkKCXByaXZhdGUgJGZuID0gbnVsbDsKCXByaXZhdGUgJHBocCA9ICckXyA9ICYkREFUQTsnIC4gUEhQX0VPTDsKCQoJcHJpdmF0ZSBzdGF0aWMgZnVuY3Rpb24gcmVuZGVyX3ZhcigkbmFtZSA9IG51bGwsICRzYWZlID0gdHJ1ZSwgJGFsbG93X3JlZiA9IGZhbHNlKXsKCQkvLyBwcmVnX21hdGNoKCdAXlxzKig/Oig/PHVuc2FmZT5VfHVuc2FmZSlccyspPyg/PHZhcj4uKikkQCcsICRuYW1lID86IHNlbGY6OiRkZWZhdWx0X3Zhcl9uYW1lLCAkYml0cyk7CgkJcHJlZ19tYXRjaCgnQF5ccyooPzooPzooPzx1bnNhZmU+VXx1bnNhZmUpfCg/PHJlZj5SfHJlZnxyZWZlcmVuY2UpKVw6KT8oPzx2YXI+LiopJEAnLCAkbmFtZSA/OiBzZWxmOjokZGVmYXVsdF92YXJfbmFtZSwgJGJpdHMpOwoJCQoJCWlmKHN0cnBvcygkYml0c1sndmFyJ10sICdbJykpCgkJewoJCQkkdmFyID0gcHJlZ19yZXBsYWNlX2NhbGxiYWNrKCdAXC5cWyhbX2EtekEtWl1cdyooPzpcLlx3Kik/KVxdQCcsIGZ1bmN0aW9uKCRtYXRjaGVzKXsKCQkJCXJldHVybiAnWycgLiBzZWxmOjpyZW5kZXJfdmFyKCRtYXRjaGVzWzFdLCBmYWxzZSkgLiAnXSc7CgkJCX0sICRiaXRzWyd2YXInXSk7CgkJCQoJCQkkdmFyID0gJyQnIC4gc2VsZjo6JHZhcl9uYW1lIC4gKCRiaXRzWyd2YXInXSA/ICdbXCcnIC4gaW1wbG9kZSgnXCddW1wnJywgZXhwbG9kZSgnLicsICR2YXIpKSAuICdcJ10nIDogJycpOwoJCQkKCQkJJHZhciA9IHByZWdfcmVwbGFjZSgKCQkJCWFycmF5KCdAXCcoXHcrKVxbQCcsICdAXCdcXVxdXCdcXUAnKSwKCQkJCWFycmF5KCdcJyQxXCddWycsICdcJ11dJyksCgkJCQkkdmFyCgkJCSk7CgkJfQoJCWVsc2UKCQl7CgkJCSR2YXIgPSAnJCcgLiBzZWxmOjokdmFyX25hbWUgLiAoJGJpdHNbJ3ZhciddID8gJ1tcJycgLiBpbXBsb2RlKCdcJ11bXCcnLCBleHBsb2RlKCcuJywgJGJpdHNbJ3ZhciddKSkgLiAnXCddJyA6ICcnKTsKCQl9CgkJCgkJcmV0dXJuICRhbGxvd19yZWYgJiYgaXNzZXQoJGJpdHNbJ3JlZiddKSAmJiAkYml0c1sncmVmJ10KCQkJPyAnJicgLiAkdmFyCgkJCTogKAoJCQkJJHNhZmUgJiYgISRiaXRzWyd1bnNhZmUnXQoJCQkJCT8gJyhpc3NldCgnIC4gJHZhciAuICcpPycgLiAkdmFyIC4gJzpudWxsKScKCQkJCQk6ICR2YXIKCQkJKTsKCX0KCQoJcHJpdmF0ZSBzdGF0aWMgZnVuY3Rpb24gc3BsaXRfdmFsdWVzKCR2YWx1ZXMsICRkZWxpbWl0ZXIgPSAnXHMqLFxzKicpewoJCS8vIGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS9hLzU2OTYxNDEvIC0tPiByZWdleCBxdW90ZWQgc3RyaW5nCgkJLy8gaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL2EvNjMyNTUyLyAtLT4gcmVnZXggdG8gbWF0Y2ggJGRlbGltaXRlciBvdXRzaWRlIHF1b3RlcwoJCXJldHVybiBwcmVnX3NwbGl0KCdAKCcgLiAoJGRlbGltaXRlciA/OiAnXHMqLFxzKicpIC4gJykoPz0oPzpbXiJdfCJbXiJcXFxcXSooPzpcXFxcLlteIlxcXFxdKikqIikqJClAJywgJHZhbHVlcyk7Cgl9CgkKCXByaXZhdGUgc3RhdGljIGZ1bmN0aW9uIHBhcnNlX3ZhbHVlcygkdmFsdWVzLCAkZGVsaW1pdGVyID0gJ1xzKixccyonLCAkc2FmZSA9IHRydWUsICRhbGxvd19yZWYgPSB0cnVlKXsKCQkkdmFsdWVfYml0cyA9IHNlbGY6OnNwbGl0X3ZhbHVlcygkdmFsdWVzLCAkZGVsaW1pdGVyKTsKCQkKCQlmb3JlYWNoKCR2YWx1ZV9iaXRzIGFzICRrID0+ICR2YWx1ZSkKCQl7CgkJCSR2YWx1ZV9iaXRzWyRrXSA9IHNlbGY6OnBhcnNlX3ZhbHVlKCR2YWx1ZSwgJHNhZmUsICRhbGxvd19yZWYpOwoJCX0KCQkKCQlyZXR1cm4gJHZhbHVlX2JpdHM7Cgl9CgkKCXByaXZhdGUgc3RhdGljIGZ1bmN0aW9uIHBhcnNlX2Jvb2xlYW4oJGRhdGEpewoJCWlmKAoJCQkhcHJlZ19tYXRjaCgKCQkJCSdAKCcgLiBzZWxmOjokcmVnZXhbJ3Zhcl92YWx1ZSddIC4gJylccyooPzooaXNzZXR8aXMoPzooPzpccypub3R8blwnP3QpP1xzKig/Oig/OmdyZWF0ZXJ8bG93ZXIpKD86XHMqdGhhbik/fGVxdWFsKD86XHMqdG8pP3xlcXVhbHxhfCg/Oig/Omluc3RhbmNlfG11bHRpcGxlfG1vZCkoPzpccypvZik/KXxtYXRjaGVzKSk/fGhhcyg/OlxzKm5vdCk/fCg/Om5vdFxzKik/bWF0Y2hlcylccyooJyAuIHNlbGY6OiRyZWdleFsndmFyX3ZhbHVlJ10gLiAnKT8pPyg/OlxzKiguKykpP0AnLAoJCQkJJGRhdGEsICRiaXRzCgkJCSkKCQkpCgkJewoJCQlyZXR1cm4gJyc7CgkJfQoJCQoJCSRmbiA9IGFycmF5KAoJCQknaXMnID0+IGZ1bmN0aW9uKCRkYXRhLCAkdmFyMSwgJHZhcjIpewoJCQkJJHN5bWJvbHMgPSBhcnJheSgKCQkJCQknJyA9PiAnJXMgPT09ICVzJywKCQkJCQknYScgPT4gJ2dldHR5cGUoJXMpID09PSAlcycsCgkJCQkJJ2luc3RhbmNlJyA9PiAnaXNfYSglcywgJXMpJywKCQkJCQknZXF1YWwnID0+ICclcyA9PSAlcycsCgkJCQkJJ2xvd2VyJyA9PiAnJXMgPCAlcycsCgkJCQkJJ2dyZWF0ZXInID0+ICclcyA+ICVzJywKCQkJCQknbXVsdGlwbGUnID0+ICchKCVzICUlICVzKScsCgkJCQkJJ21vZCcgPT4gJyVzICUlICVzJywKCQkJCQknbWF0Y2hlcycgPT4gJ3ByZWdfbWF0Y2goJTIkcywgJTEkcyknCgkJCQkpOwoJCQkJCgkJCQlwcmVnX21hdGNoKCdAKD88bm90Pm5vdCk/XHMqKD88b3BlcmF0aW9uPmVxdWFsfGxvd2VyfGdyZWF0ZXJ8YXxpbnN0YW5jZXxtdWx0aXBsZXxtb2R8bWF0Y2hlcyk/XHMqKD86b2Z8dG98dGhhbik/XHMqQCcsICRkYXRhLCAkYml0cyk7CgkJCQkKCQkJCXJldHVybiAoaXNzZXQoJGJpdHNbJ25vdCddKSAmJiAkYml0c1snbm90J10gIT09ICcnID8gJyEnOiAnJykgLiAnKCcgLiBzcHJpbnRmKCRzeW1ib2xzW2lzc2V0KCRiaXRzWydvcGVyYXRpb24nXSkgPyAkYml0c1snb3BlcmF0aW9uJ106ICcnXSwgc2VsZjo6cGFyc2VfdmFsdWUoJHZhcjEpLCBzZWxmOjpwYXJzZV92YWx1ZSgkdmFyMikpIC4gJyknOwoJCQl9LAoJCQknaGFzJyA9PiBmdW5jdGlvbigkZGF0YSwgJHZhcjEsICR2YXIyKXsKCQkJCXJldHVybiAoJGRhdGEgPT09ICdub3QnID8gJyEnOiAnJykgLiAnYXJyYXlfa2V5X2V4aXN0cygnIC4gc2VsZjo6cGFyc2VfdmFsdWUoJHZhcjIpIC4gJywgKGFycmF5KScgLiBzZWxmOjpwYXJzZV92YWx1ZSgkdmFyMSkgLiAnKSc7CgkJCX0sCgkJCSdpc3NldCcgPT4gZnVuY3Rpb24oJGRhdGEsICR2YXIxKXsKCQkJCXJldHVybiAoJGRhdGEgPT09ICdub3QnID8gJyEnOiAnJykgLiAnaXNzZXQoJyAuIHNlbGY6OnJlbmRlcl92YXIoJHZhcjEsIGZhbHNlKSAuICcpJzsKCQkJfSwKCQkJJ21hdGNoZXMnID0+IGZ1bmN0aW9uKCRkYXRhLCAkdmFyMSwgJHZhcjIsICRleHRyYSl7CgkJCQlpZigkZXh0cmEgJiYgc2VsZjo6aXNfdmFyKCRleHRyYSA9IHNlbGY6OnBhcnNlX3ZhbHVlKCRleHRyYSwgZmFsc2UpKSkKCQkJCXsKCQkJCQlyZXR1cm4gKCRkYXRhID09PSAnbm90JyA/ICchJzogJycpIC4gJ3ByZWdfbWF0Y2goJyAuIHNlbGY6OnBhcnNlX3ZhbHVlKCR2YXIyKSAuICcsICcgLiBzZWxmOjpwYXJzZV92YWx1ZSgkdmFyMSkgLiAnLCAnIC4gJGV4dHJhIC4gJyknOwoJCQkJfQoJCQkJZWxzZQoJCQkJewoJCQkJCXJldHVybiAoJGRhdGEgPT09ICdub3QnID8gJyEnOiAnJykgLiAncHJlZ19tYXRjaCgnIC4gc2VsZjo6cGFyc2VfdmFsdWUoJHZhcjIpIC4gJywgJyAuIHNlbGY6OnBhcnNlX3ZhbHVlKCR2YXIxKSAuICcpJzsKCQkJCX0KCQkJfQoJCSk7CgkJCgkJaWYoaXNzZXQoJGJpdHNbMl0pKQoJCXsKCQkJJG9wcyA9IGV4cGxvZGUoJyAnLCAkYml0c1syXSwgMik7CgkJCQoJCQlyZXR1cm4gJGZuWyRvcHNbMF1dKAoJCQkJaXNzZXQoJG9wc1sxXSkgPyAkb3BzWzFdOiAnJywKCQkJCSRiaXRzWzFdLAoJCQkJaXNzZXQoJGJpdHNbM10pID8gJGJpdHNbM10gOiBzZWxmOjokZGVmYXVsdF92YXJfbmFtZSwKCQkJCWlzc2V0KCRiaXRzWzRdKSA/ICRiaXRzWzRdIDogJycKCQkJKTsKCQl9CgkJZWxzZQoJCXsKCQkJcmV0dXJuIHNlbGY6OnBhcnNlX3ZhbHVlKCRiaXRzWzFdKTsKCQl9Cgl9CgkKCXByaXZhdGUgc3RhdGljIGZ1bmN0aW9uIHBhcnNlX3ZhbHVlKCR2YWx1ZSwgJHNhZmUgPSB0cnVlLCAkYWxsb3dfcmVmID0gZmFsc2UpewoJCWlmKCR2YWx1ZSA9PT0gJycgfHwgJHZhbHVlID09PSAnIiInKQoJCXsKCQkJcmV0dXJuICR2YWx1ZTsKCQl9CgkJZWxzZSBpZihwcmVnX21hdGNoKCdAXicgLiBzZWxmOjokcmVnZXhbJ3ZhbHVlJ10gLiAnJEAnLCAkdmFsdWUpKQoJCXsKCQkJcmV0dXJuICR2YWx1ZVswXSA9PT0gJyInCgkJCQk/IHNlbGY6OnBhcnNlX3ZhbHVlX3N0cmluZygkdmFsdWUpCgkJCQk6ICR2YWx1ZTsKCQl9CgkJZWxzZSBpZihwcmVnX21hdGNoKCdAXicgLiBzZWxmOjokcmVnZXhbJ3ZhciddIC4gJyRAJywgJHZhbHVlKSkKCQl7CgkJCXJldHVybiBzZWxmOjpyZW5kZXJfdmFyKCR2YWx1ZSwgJHNhZmUsICRhbGxvd19yZWYpOwoJCX0KCQllbHNlCgkJewoJCQl0aHJvdyBuZXcgU2ltcGxlVGVtcGxhdGVfU3ludGF4RXJyb3IoJ0ludmFsaWQgdmFsdWUgc3ludGF4OiAnIC4gJHZhbHVlKTsKCQl9Cgl9CgkKCXByaXZhdGUgc3RhdGljIGZ1bmN0aW9uIHBhcnNlX3ZhbHVlX3N0cmluZygkdmFsdWUpewoJCXJldHVybiBwcmVnX3JlcGxhY2VfY2FsbGJhY2soCgkJCSdAI3soW19hLXpBLVpdXHcqKD86XC4oPzpcW1tfYS16QS1aXVx3Kig/OlwuXHcqKSpcXXxcdyopKSopfUAnLAoJCQlmdW5jdGlvbigkbWF0Y2hlcyl7CgkJCQlyZXR1cm4gJ3snIC4gc2VsZjo6cmVuZGVyX3ZhcigkbWF0Y2hlc1sxXSwgZmFsc2UpIC4gJ30nOwoJCQl9LAoJCQlzdHJfcmVwbGFjZSgnJCcsICdcXCQnLCAkdmFsdWUpCgkJKTsKCX0KCQoJcHJpdmF0ZSBzdGF0aWMgZnVuY3Rpb24gaXNfdmFsdWUoJHZhbHVlKXsKCQlyZXR1cm4gc3RybGVuKCR2YWx1ZSkKCQkJJiYgJHZhbHVlWzBdICE9PSAnJCcKCQkJJiYgJHZhbHVlWzBdICE9PSAnKCcKCQkJJiYgJHZhbHVlWzBdICE9PSAnJicKCQkJLyomJiAoCgkJCQkkdmFsdWVbMF0gIT09ICciJwoJCQkJfHwgc3RycG9zKCR2YWx1ZSwgJ3skJyAuIHNlbGY6OiR2YXJfbmFtZSkgPT09IGZhbHNlCgkJCSkqLzsKCX0KCQoJcHJpdmF0ZSBzdGF0aWMgZnVuY3Rpb24gaXNfdmFyKCR2YWx1ZSl7CgkJcmV0dXJuIHN0cmxlbigkdmFsdWUpCgkJCSYmICgKCQkJCSR2YWx1ZVswXSA9PT0gJyQnCgkJCQl8fCAkdmFsdWVbMF0gPT09ICcoJwoJCQkJfHwgJHZhbHVlWzBdID09PSAnJicKCQkJKTsKCX0KCQoJcHJpdmF0ZSBmdW5jdGlvbiBmb3JtYXRfY29kZSgkY29kZSwgJHRhYnMsICRza2lwX2ZpcnN0ID0gZmFsc2UsICRza2lwX2xhc3QgPSBmYWxzZSl7CgkJJGxpbmVzID0gcHJlZ19zcGxpdCgiQCg/OlxyP1xufFxyKStAIiwgJGNvZGUpOwoJCSRoZXJlZG9jX2Nsb3NpbmcgPSBzZWxmOjokdmFyX25hbWUgLiAkdGhpcy0+dXVpZDsKCQkKCQkkcmV0dXJuID0gJHNraXBfZmlyc3QgPyBhcnJheV9zaGlmdCgkbGluZXMpIDogJyc7CgkJJGxhc3QgPSAkc2tpcF9sYXN0ID8gUEhQX0VPTCAuIGFycmF5X3BvcCgkbGluZXMpOiAnJzsKCQkKCQlmb3JlYWNoKCRsaW5lcyBhcyAkbGluZSkKCQl7CgkJCWlmKCRyZXR1cm4pCgkJCXsKCQkJCSRyZXR1cm4gLj0gUEhQX0VPTDsKCQkJfQoJCQkKCQkJaWYoJGxpbmUgPT09ICRoZXJlZG9jX2Nsb3NpbmcpCgkJCXsKCQkJCSRyZXR1cm4gLj0gJGhlcmVkb2NfY2xvc2luZzsKCQkJfQoJCQllbHNlCgkJCXsKCQkJCSRyZXR1cm4gLj0gKAoJCQkJCXByZWdfbWF0Y2goJ0BeXHMqXCkrOz9ccyokQCcsICRsaW5lKQoJCQkJCQk/IHN1YnN0cigkdGFicywgMSkKCQkJCQkJOiAkdGFicwoJCQkJKS4gbHRyaW0oJGxpbmUpOwoJCQl9CgkJfQoJCQoJCXJldHVybiAkcmV0dXJuIC4gJGxhc3Q7Cgl9CgkKCXByaXZhdGUgZnVuY3Rpb24gY29tcGlsZSgkc3RyKXsKCQkkVVVJRCA9ICR0aGlzLT51dWlkOwoJCQoJCSRicmFja2V0cyA9IDA7CgkJJHRhYnMgPSAnJzsKCQkKCQkkcmVwbGFjZW1lbnQgPSBhcnJheSgKCQkJJy8nID0+IGZ1bmN0aW9uKCRkYXRhKXVzZSgmJHJlcGxhY2VtZW50LCAmJGJyYWNrZXRzLCAmJHRhYnMpewoJCQkJaWYoJGJyYWNrZXRzID4gMCkKCQkJCXsKCQkJCQktLSRicmFja2V0czsKCQkJCQkKCQkJCQlyZXR1cm4gJHRhYnMgLiAnfTsnOwoJCQkJfQoJCQkJZWxzZQoJCQkJewoJCQkJCXJldHVybiAkdGFicyAuICc7JzsKCQkJCX0KCQkJfSwKCQkJJy8vJyA9PiBmdW5jdGlvbigpewoJCQkJcmV0dXJuICcnOwoJCQl9LAoJCQknZWNobycgPT4gZnVuY3Rpb24oJGRhdGEpdXNlKCYkcmVwbGFjZW1lbnQsICYkYnJhY2tldHMsICYkdGFicyl7CgkJCQlwcmVnX21hdGNoKCdAXig/OnNlcGFyYXRvclxzKyg/PHNlcGFyYXRvcj4nIC4gc2VsZjo6JHJlZ2V4Wyd2YXJfdmFsdWUnXSAuICcpXHMqKT8oPzxkYXRhPi4qKSRAJywgJGRhdGEsICRiaXRzKTsKCQkJCQoJCQkJJHNlcGFyYXRvciA9ICRiaXRzWydzZXBhcmF0b3InXSA/IHNlbGY6OnBhcnNlX3ZhbHVlKCRiaXRzWydzZXBhcmF0b3InXSk6ICdcJ1wnJzsKCQkJCQoJCQkJcmV0dXJuICR0YWJzIC4gJ2VjaG8gaW1wbG9kZSgnIC4gJHNlcGFyYXRvciAuICcsIFNpbXBsZVRlbXBsYXRlX0ZOOjpjYWxsKFwnYXJyYXlfZmxhdFwnLCAnIC4gaW1wbG9kZSgnLCAnLCBzZWxmOjpwYXJzZV92YWx1ZXMoJGJpdHNbJ2RhdGEnXSkpIC4gJykpOyc7CgkJCX0sCgkJCSdlY2hvbCcgPT4gZnVuY3Rpb24oJGRhdGEpdXNlKCYkcmVwbGFjZW1lbnQsICYkYnJhY2tldHMsICYkdGFicyl7CgkJCQlyZXR1cm4gJHJlcGxhY2VtZW50WydlY2hvJ10oJGRhdGEpIC4gJ2VjaG8gUEhQX0VPTDsnOwoJCQl9LAoJCQknZWNob2onID0+IGZ1bmN0aW9uKCRkYXRhKXVzZSgmJHJlcGxhY2VtZW50LCAmJGJyYWNrZXRzLCAmJHRhYnMpewoJCQkJcmV0dXJuICRyZXBsYWNlbWVudFsnZWNobyddKCdzZXBhcmF0b3IgJyAuICRkYXRhKTsKCQkJfSwKCQkJJ2VjaG9qbCcgPT4gZnVuY3Rpb24oJGRhdGEpdXNlKCYkcmVwbGFjZW1lbnQsICYkYnJhY2tldHMsICYkdGFicyl7CgkJCQlyZXR1cm4gJHJlcGxhY2VtZW50WydlY2hvbCddKCdzZXBhcmF0b3IgJyAuICRkYXRhKTsKCQkJfSwKCQkJJ2VjaG9mJyA9PiBmdW5jdGlvbigkZGF0YSl1c2UoJiRyZXBsYWNlbWVudCwgJiRicmFja2V0cywgJiR0YWJzKXsKCQkJCXJldHVybiAkcmVwbGFjZW1lbnRbJ2VjaG9sJ10oJ3NlcGFyYXRvciAnIC4gJGRhdGEpOwoJCQl9LAoJCQkncHJpbnQnID0+IGZ1bmN0aW9uKCRkYXRhKXVzZSgmJHJlcGxhY2VtZW50LCAmJGJyYWNrZXRzLCAmJHRhYnMpewoJCQkJcmV0dXJuICRyZXBsYWNlbWVudFsnY2FsbCddKChzdHJwb3MoJ2ludG8nLCAkZGF0YSkgPT09IDAgPyAncycgOiAnJykgLiAncHJpbnRmICcgLiAkZGF0YSk7CgkJCX0sCgkJCSdpZicgPT4gZnVuY3Rpb24oJGRhdGEpdXNlKCYkcmVwbGFjZW1lbnQsICYkYnJhY2tldHMsICYkdGFicyl7CgkJCQkrKyRicmFja2V0czsKCQkJCQoJCQkJcmV0dXJuICR0YWJzIC4gJ2lmKCcgLiBzZWxmOjpwYXJzZV9ib29sZWFuKCRkYXRhKSAuICcpIHsnOwoJCQl9LAoJCQknZWxzZScgPT4gZnVuY3Rpb24oJGRhdGEpdXNlKCYkcmVwbGFjZW1lbnQsICYkYnJhY2tldHMsICYkdGFicyl7CgkJCQlwcmVnX21hdGNoKCdAKD88aWY+aWYpKD88ZGF0YT4uKilAJywgJGRhdGEsICRiaXRzKTsKCQkJCQoJCQkJJHJldHVybiA9IHN1YnN0cigkdGFicywgMSkgLiAnfSBlbHNlJzsKCQkJCQoJCQkJaWYoaXNzZXQoJGJpdHNbJ2lmJ10pICYmIGlzc2V0KCRiaXRzWydkYXRhJ10pKQoJCQkJewoJCQkJCS0tJGJyYWNrZXRzOwoJCQkJCgkJCQkJJHJldHVybiAuPSBsdHJpbSgkcmVwbGFjZW1lbnRbJ2lmJ10odHJpbSgkYml0c1snZGF0YSddKSkpOwoJCQkJfQoJCQkJZWxzZQoJCQkJewoJCQkJCSRyZXR1cm4gLj0gJyB7JzsKCQkJCX0KCQkJCQoJCQkJcmV0dXJuICRyZXR1cm47CgkJCX0sCgkJCSdlYWNoJyA9PiBmdW5jdGlvbigkZGF0YSl1c2UoJiRyZXBsYWNlbWVudCwgJiRicmFja2V0cywgJiR0YWJzKXsKCQkJCSsrJGJyYWNrZXRzOwoJCQkJCgkJCQlwcmVnX21hdGNoKCdAXig/PHZhcj4nIC4gc2VsZjo6JHJlZ2V4Wyd2YXJfdmFsdWUnXSAuICcpXHMqKD86YXNccyooPzxhcz4nIC4gc2VsZjo6JHJlZ2V4Wyd2YXInXSAuICcpKD86XHMqa2V5XHMqKD88a2V5PicgLiBzZWxmOjokcmVnZXhbJ3ZhciddIC4gJylccyopPyk/JEAnLCAkZGF0YSwgJGJpdHMpOwoJCQkJCgkJCQlzdGF0aWMgJGNvdW50ID0gMDsKCQkJCQoJCQkJJHZhcl9uYW1lID0gc2VsZjo6JHZhcl9uYW1lOwoJCQkJJHRtcF9uYW1lID0gJ3RtcF8nIC4gKCsrJGNvdW50KSAuICdfJzsKCQkJCQoJCQkJJHZhcnNfdmFyID0gc2VsZjo6cGFyc2VfdmFsdWUoaXNzZXQoJGJpdHNbJ3ZhciddKSA/ICRiaXRzWyd2YXInXSA6ICdfJywgZmFsc2UpOwoJCQkJJHZhcnNfYXMgPSBzZWxmOjpyZW5kZXJfdmFyKGlzc2V0KCRiaXRzWydhcyddKSA/ICRiaXRzWydhcyddIDogJycsIGZhbHNlKTsKCQkJCSR2YXJzX2tleSA9IHNlbGY6OnJlbmRlcl92YXIoaXNzZXQoJGJpdHNbJ2tleSddKSA/ICRiaXRzWydrZXknXSA6ICdfXycsIGZhbHNlKTsKCQkJCQoJCQkJaWYoc2VsZjo6aXNfdmFyKCR2YXJzX3ZhcikpCgkJCQl7CgkJCQkJcmV0dXJuIDw8PFBIUAp7JHRhYnN9Ly8gfiBvcHRpbWl6YXRpb24gdW5mZWFzYWJsZSB+IHZhcmlhYmxlIHVzZWQgaW4gdGhlIGZpcnN0IGFyZ3VtZW50CnskdGFic30vLyBsb29wIHZhcmlhYmxlcyAtIHZhcmlhYmxlCnskdGFic31cJHskdG1wX25hbWV9dmFsID0gaXNzZXQoeyR2YXJzX3Zhcn0pID8gXCR7JHRtcF9uYW1lfXZhbCA9ICZ7JHZhcnNfdmFyfSA6IG51bGw7CnskdGFic31cJHskdG1wX25hbWV9a2V5cyA9IGdldHR5cGUoeyR2YXJzX3Zhcn0pID09ICdhcnJheScKeyR0YWJzfQk/IGFycmF5X2tleXMoeyR2YXJzX3Zhcn0pCnskdGFic30JOiAoKFwkeyR0bXBfbmFtZX12YWwgPSBcJHskdG1wX25hbWV9dmFsIC4gJycpCnskdGFic30JCT8gYXJyYXlfa2V5cyhyYW5nZSgwLCBzdHJsZW4oXCR7JHRtcF9uYW1lfXZhbCA9IFwkeyR0bXBfbmFtZX12YWwgLiAnJykgLSAxKSkKeyR0YWJzfQkJOiBhcnJheSgpCnskdGFic30JKTsKeyR0YWJzfVwkeyR0bXBfbmFtZX1rZXlfbGFzdCA9IGVuZChcJHskdG1wX25hbWV9a2V5cyk7CnskdGFic30KeyR0YWJzfS8vIGxvb3AKeyR0YWJzfWZvcmVhY2goXCR7JHRtcF9uYW1lfWtleXMgYXMgXCR7JHRtcF9uYW1lfWluZGV4ID0+IFwkeyR0bXBfbmFtZX1rZXkpewp7JHRhYnN9CVwkeyR2YXJfbmFtZX1bJ2xvb3AnXSA9IGFycmF5KAp7JHRhYnN9CQknaW5kZXgnID0+IFwkeyR0bXBfbmFtZX1pbmRleCwKeyR0YWJzfQkJJ2knID0+IFwkeyR0bXBfbmFtZX1pbmRleCwKeyR0YWJzfQkJJ2tleScgPT4gXCR7JHRtcF9uYW1lfWtleSwKeyR0YWJzfQkJJ2snID0+IFwkeyR0bXBfbmFtZX1rZXksCnskdGFic30JCSd2YWx1ZScgPT4gXCR7JHRtcF9uYW1lfXZhbFtcJHskdG1wX25hbWV9a2V5XSwKeyR0YWJzfQkJJ3YnID0+IFwkeyR0bXBfbmFtZX12YWxbXCR7JHRtcF9uYW1lfWtleV0sCnskdGFic30JCSdmaXJzdCcgPT4gXCR7JHRtcF9uYW1lfWtleSA9PT0gXCR7JHRtcF9uYW1lfWtleXNbMF0sCnskdGFic30JCSdsYXN0JyA9PiBcJHskdG1wX25hbWV9a2V5ID09PSBcJHskdG1wX25hbWV9a2V5X2xhc3QsCnskdGFic30JKTsKeyR0YWJzfQl7JHZhcnNfa2V5fSA9IFwkeyR0bXBfbmFtZX1rZXk7CnskdGFic30JeyR2YXJzX2FzfSA9IFwkeyR0bXBfbmFtZX12YWxbXCR7JHRtcF9uYW1lfWtleV07ClBIUDsKCQkJCX0KCQkJCWVsc2UgaWYoJHRoaXMtPm9wdGlvbnNbJ29wdGltaXplJ10pCgkJCQl7CgkJCQkJJGtleXMgPSAnJzsKCQkJCQkKCQkJCQlpZigkdmFyc192YXIgIT09ICciIicpCgkJCQkJewoJCQkJCQkka2V5cyA9IGltcGxvZGUoCgkJCQkJCQknLCcsCgkJCQkJCQlyYW5nZSgKCQkJCQkJCQkwLAoJCQkJCQkJCXN0cmxlbigkdmFyc192YXIpIC0gKCR2YXJzX3ZhclswXSA9PT0gJyInID8gMyA6IDEpCgkJCQkJCQkpCgkJCQkJCSk7CgkJCQkJfQoJCQkJCQoJCQkJCXJldHVybiA8PDxQSFAKeyR0YWJzfS8vIH4gb3B0aW1pemF0aW9uIEVOQUJMRUQgfiBcJHskdG1wX25hbWV9a2V5cyB3YXMgaW5saW5lZAp7JHRhYnN9Ly8gbG9vcCB2YXJpYWJsZXMgLSBpbmxpbmUgdmFsdWUKeyR0YWJzfVwkeyR0bXBfbmFtZX12YWwgPSAoeyR2YXJzX3Zhcn0pIC4gJyc7IC8vIGNvbnZlcnQgdG8gc3RyaW5nCnskdGFic31cJHskdG1wX25hbWV9a2V5cyA9IGFycmF5KHska2V5c30pOwp7JHRhYnN9XCR7JHRtcF9uYW1lfWtleV9sYXN0ID0gZW5kKFwkeyR0bXBfbmFtZX1rZXlzKTsKeyR0YWJzfQp7JHRhYnN9Ly8gbG9vcAp7JHRhYnN9Zm9yZWFjaChcJHskdG1wX25hbWV9a2V5cyBhcyBcJHskdG1wX25hbWV9aW5kZXggPT4gXCR7JHRtcF9uYW1lfWtleSl7CnskdGFic30JXCR7JHZhcl9uYW1lfVsnbG9vcCddID0gYXJyYXkoCnskdGFic30JCSdpbmRleCcgPT4gXCR7JHRtcF9uYW1lfWluZGV4LAp7JHRhYnN9CQknaScgPT4gXCR7JHRtcF9uYW1lfWluZGV4LAp7JHRhYnN9CQkna2V5JyA9PiBcJHskdG1wX25hbWV9a2V5LAp7JHRhYnN9CQknaycgPT4gXCR7JHRtcF9uYW1lfWtleSwKeyR0YWJzfQkJJ3ZhbHVlJyA9PiBcJHskdG1wX25hbWV9dmFsW1wkeyR0bXBfbmFtZX1rZXldLAp7JHRhYnN9CQkndicgPT4gXCR7JHRtcF9uYW1lfXZhbFtcJHskdG1wX25hbWV9a2V5XSwKeyR0YWJzfQkJJ2ZpcnN0JyA9PiBcJHskdG1wX25hbWV9a2V5ID09PSBcJHskdG1wX25hbWV9a2V5c1swXSwKeyR0YWJzfQkJJ2xhc3QnID0+IFwkeyR0bXBfbmFtZX1rZXkgPT09IFwkeyR0bXBfbmFtZX1rZXlfbGFzdCwKeyR0YWJzfQkpOwp7JHRhYnN9CXskdmFyc19rZXl9ID0gXCR7JHRtcF9uYW1lfWtleTsKeyR0YWJzfQl7JHZhcnNfYXN9ID0gXCR7JHRtcF9uYW1lfXZhbFtcJHskdG1wX25hbWV9a2V5XTsKUEhQOwoJCQkJfQoJCQkJZWxzZQoJCQkJewoJCQkJCXJldHVybiA8PDxQSFAKeyR0YWJzfS8vIH4gb3B0aW1pemF0aW9uIERJU0FCTEVEIH4gXCR7JHRtcF9uYW1lfWtleXMgY291bGQgYmUgaW5saW5lZAp7JHRhYnN9Ly8gbG9vcCB2YXJpYWJsZXMgLSBpbmxpbmUgdmFsdWUKeyR0YWJzfVwkeyR0bXBfbmFtZX12YWwgPSAoeyR2YXJzX3Zhcn0pIC4gJyc7IC8vIGNvbnZlcnQgdG8gc3RyaW5nCnskdGFic31cJHskdG1wX25hbWV9a2V5cyA9IFwkeyR0bXBfbmFtZX12YWwKeyR0YWJzfQk/IGFycmF5X2tleXMocmFuZ2UoMCwgc3RybGVuKFwkeyR0bXBfbmFtZX12YWwpIC0gMSkpCnskdGFic30JOiBhcnJheSgpOwp7JHRhYnN9XCR7JHRtcF9uYW1lfWtleV9sYXN0ID0gZW5kKFwkeyR0bXBfbmFtZX1rZXlzKTsKeyR0YWJzfQp7JHRhYnN9Ly8gbG9vcAp7JHRhYnN9Zm9yZWFjaChcJHskdG1wX25hbWV9a2V5cyBhcyBcJHskdG1wX25hbWV9aW5kZXggPT4gXCR7JHRtcF9uYW1lfWtleSl7CnskdGFic30JXCR7JHZhcl9uYW1lfVsnbG9vcCddID0gYXJyYXkoCnskdGFic30JCSdpbmRleCcgPT4gXCR7JHRtcF9uYW1lfWluZGV4LAp7JHRhYnN9CQknaScgPT4gXCR7JHRtcF9uYW1lfWluZGV4LAp7JHRhYnN9CQkna2V5JyA9PiBcJHskdG1wX25hbWV9a2V5LAp7JHRhYnN9CQknaycgPT4gXCR7JHRtcF9uYW1lfWtleSwKeyR0YWJzfQkJJ3ZhbHVlJyA9PiBcJHskdG1wX25hbWV9dmFsW1wkeyR0bXBfbmFtZX1rZXldLAp7JHRhYnN9CQkndicgPT4gXCR7JHRtcF9uYW1lfXZhbFtcJHskdG1wX25hbWV9a2V5XSwKeyR0YWJzfQkJJ2ZpcnN0JyA9PiBcJHskdG1wX25hbWV9a2V5ID09PSBcJHskdG1wX25hbWV9a2V5c1swXSwKeyR0YWJzfQkJJ2xhc3QnID0+IFwkeyR0bXBfbmFtZX1rZXkgPT09IFwkeyR0bXBfbmFtZX1rZXlfbGFzdCwKeyR0YWJzfQkpOwp7JHRhYnN9CXskdmFyc19rZXl9ID0gXCR7JHRtcF9uYW1lfWtleTsKeyR0YWJzfQl7JHZhcnNfYXN9ID0gXCR7JHRtcF9uYW1lfXZhbFtcJHskdG1wX25hbWV9a2V5XTsKUEhQOwoJCQkJfQoJCQl9LAoJCQknd2hpbGUnID0+IGZ1bmN0aW9uKCRkYXRhKXVzZSgmJHJlcGxhY2VtZW50LCAmJGJyYWNrZXRzLCAmJHRhYnMpewoJCQkJKyskYnJhY2tldHM7CgkJCQkKCQkJCXJldHVybiAkdGFicyAuICd3aGlsZSgnIC4gc2VsZjo6cGFyc2VfYm9vbGVhbigkZGF0YSkgLiAnKXsnOwoJCQl9LAoJCQknZm9yJyA9PiBmdW5jdGlvbigkZGF0YSl1c2UoJiRyZXBsYWNlbWVudCwgJiRicmFja2V0cywgJiR0YWJzKXsKCQkJCSsrJGJyYWNrZXRzOwoJCQkJCgkJCQlyZXR1cm4gcHJlZ19yZXBsYWNlX2NhbGxiYWNrKAoJCQkJCSdAKD88dmFyPicgLiBzZWxmOjokcmVnZXhbJ3ZhciddIC4gJyk/XHMqKD86ZnJvbVxzKig/PHN0YXJ0PicgLiBzZWxmOjokcmVnZXhbJ3Zhcl92YWx1ZSddIC4gJykpPyg/OlxzKnRvXHMqKD88ZW5kPicgLiBzZWxmOjokcmVnZXhbJ3Zhcl92YWx1ZSddIC4gJykpKD86XHMqc3RlcFxzKig/PHN0ZXA+JyAuIHNlbGY6OiRyZWdleFsndmFyX3ZhbHVlJ10gLiAnKSk/QCcsCgkJCQkJZnVuY3Rpb24oJG1hdGNoZXMpdXNlKCYkcmVwbGFjZW1lbnQsICYkYnJhY2tldHMsICYkdGFicyl7CgkJCQkJCgkJCQkJCSR2YWx1ZXMgPSBhcnJheSgKCQkJCQkJCSdzdGFydCcgPT4gaXNzZXQoJG1hdGNoZXNbJ3N0YXJ0J10pICYmICRtYXRjaGVzWydzdGFydCddICE9PSAnJyA/IHNlbGY6OnBhcnNlX3ZhbHVlKCRtYXRjaGVzWydzdGFydCddKSA6ICcwJywKCQkJCQkJCSdlbmQnID0+IGlzc2V0KCRtYXRjaGVzWydlbmQnXSkgPyBzZWxmOjpwYXJzZV92YWx1ZSgkbWF0Y2hlc1snZW5kJ10pIDogc2VsZjo6cGFyc2VfdmFsdWUoJG1hdGNoZXNbJ3N0YXJ0J10pLAoJCQkJCQkJJ3N0ZXAnID0+IGlzc2V0KCRtYXRjaGVzWydzdGVwJ10pID8gc2VsZjo6cGFyc2VfdmFsdWUoJG1hdGNoZXNbJ3N0ZXAnXSkgOiAnMScKCQkJCQkJKTsKCQkJCQkJCgkJCQkJCSRyZXR1cm4gPSAkdGFicyAuICdmb3JlYWNoKCc7CgkJCQkJCQoJCQkJCQlpZihzZWxmOjppc192YWx1ZSgkdmFsdWVzWydzdGFydCddKSAmJiBzZWxmOjppc192YWx1ZSgkdmFsdWVzWydlbmQnXSkgJiYgc2VsZjo6aXNfdmFsdWUoJHZhbHVlc1snc3RlcCddKSkKCQkJCQkJewoJCQkJCQkJaWYoJHRoaXMtPm9wdGlvbnNbJ29wdGltaXplJ10pCgkJCQkJCQl7CgkJCQkJCQkJJHJldHVybiA9ICJ7JHRhYnN9Ly8gfiBvcHRpbWl6YXRpb24gZW5hYmxlZCB+IGlubGluaW5nIHRoZSByZXN1bHRzXHJcbnskcmV0dXJufSIgLiBzZWxmOjpmb3JtYXRfY29kZSgKCQkJCQkJCQkJdmFyX2V4cG9ydCgKCQkJCQkJCQkJCXJhbmdlKAoJCQkJCQkJCQkJCXByZWdfcmVwbGFjZSgnQF4ifCIkQCcsICcnLCAkdmFsdWVzWydzdGFydCddKSwKCQkJCQkJCQkJCQlwcmVnX3JlcGxhY2UoJ0BeInwiJEAnLCAnJywgJHZhbHVlc1snZW5kJ10pLAoJCQkJCQkJCQkJCWFicygkdmFsdWVzWydzdGVwJ10pCgkJCQkJCQkJCQkpLAoJCQkJCQkJCQkJdHJ1ZQoJCQkJCQkJCQkpLAoJCQkJCQkJCQkkdGFicyAuICJcdCIsCgkJCQkJCQkJCXRydWUKCQkJCQkJCQkpOwoJCQkJCQkJfQoJCQkJCQkJZWxzZQoJCQkJCQkJewoJCQkJCQkJCSRyZXR1cm4gPSAieyR0YWJzfS8vIH4gb3B0aW1pemF0aW9uIERJU0FCTEVEIH4gcmVzdWx0cyBjb3VsZCBiZSBpbmxpbmVkXHJcbnskcmV0dXJufXJhbmdlKHskdmFsdWVzWydzdGFydCddfSwgeyR2YWx1ZXNbJ2VuZCddfSwgYWJzKHskdmFsdWVzWydzdGVwJ119KSkiOwoJCQkJCQkJfQoJCQkJCQl9CgkJCQkJCWVsc2UKCQkJCQkJewoJCQkJCQkJJHJldHVybiAuPSAncmFuZ2UoJyAuICR2YWx1ZXNbJ3N0YXJ0J10gLiAnLCAnIC4gJHZhbHVlc1snZW5kJ10gLiAnLCBhYnMoJyAuICR2YWx1ZXNbJ3N0ZXAnXSAuICcpKSc7CgkJCQkJCX0KCQkJCQkJCgkJCQkJCXJldHVybiAkcmV0dXJuIC4gJyBhcyAnIC4gc2VsZjo6cmVuZGVyX3Zhcihpc3NldCgkbWF0Y2hlc1sndmFyJ10pID8gJG1hdGNoZXNbJ3ZhciddIDogJycsIGZhbHNlKSAuICcpeyc7CgkJCQkJfSwKCQkJCQkkZGF0YQoJCQkJKTsKCQkJfSwKCQkJJ2RvJyA9PiBmdW5jdGlvbigkZGF0YSl1c2UoJiRyZXBsYWNlbWVudCwgJiRicmFja2V0cywgJiR0YWJzKXsKCQkJCSsrJGJyYWNrZXRzOwoJCQkJCgkJCQlyZXR1cm4gJHRhYnMgLiAnZG97JzsKCQkJfSwKCQkJJ3VudGlsJyA9PiBmdW5jdGlvbigkZGF0YSl1c2UoJiRyZXBsYWNlbWVudCwgJiRicmFja2V0cywgJiR0YWJzKXsKCQkJCS0tJGJyYWNrZXRzOwoJCQkJCgkJCQlyZXR1cm4gc3Vic3RyKCR0YWJzLCAxKSAuICd9d2hpbGUoISgnIC4gc2VsZjo6cGFyc2VfYm9vbGVhbigkZGF0YSkgLiAnKSk7JzsKCQkJfSwKCQkJJ3NldCcgPT4gZnVuY3Rpb24oJGRhdGEpdXNlKCYkcmVwbGFjZW1lbnQsICYkYnJhY2tldHMsICYkdGFicyl7CgkJCQlwcmVnX21hdGNoKCdAXlxzKig/PG9wPltcK1wtXCpcXFwvXCVdKT9ccyooPzx2YXI+JyAuIHNlbGY6OiRyZWdleFsndmFyJ10gLiAnKVxzKig/Oig/PG9wX3ZhbD4nIC4gc2VsZjo6JHJlZ2V4Wyd2YXJfdmFsdWUnXSAuICcpXHMpP1xzKig/PHZhbHVlcz4uKikkQCcsICRkYXRhLCAkYml0cyk7CgkJCQkKCQkJCSR2YWx1ZXMgPSBzZWxmOjpwYXJzZV92YWx1ZXMoJGJpdHNbJ3ZhbHVlcyddKTsKCQkJCSRjb3VudCA9IGNvdW50KCR2YWx1ZXMpOwoJCQkJCgkJCQkkcmV0dXJuID0gJHRhYnMgLiBzZWxmOjpyZW5kZXJfdmFyKCRiaXRzWyd2YXInXSwgZmFsc2UpIC4gJyA9ICc7CgkJCQkKCQkJCSRjbG9zZSA9IDA7CgkJCQkKCQkJCWlmKGlzc2V0KCRiaXRzWydvcCddKSkKCQkJCXsKCQkJCQlzd2l0Y2goJGJpdHNbJ29wJ10pCgkJCQkJewoJCQkJCQljYXNlICctJzoKCQkJCQkJCSRyZXR1cm4gLj0gPDw8UEhQCmNhbGxfdXNlcl9mdW5jX2FycmF5KGZ1bmN0aW9uKCl7CnskdGFic30JXCRhcmdzID0gZnVuY19nZXRfYXJncygpOwp7JHRhYnN9CVwkaW5pdGlhbCA9IGFycmF5X3NoaWZ0KFwkYXJncyk7CnskdGFic30JcmV0dXJuIGFycmF5X3JlZHVjZShcJGFyZ3MsIGZ1bmN0aW9uKFwkY2FycnksIFwkdmFsdWUpewp7JHRhYnN9CQlyZXR1cm4gXCRjYXJyeSAtIFwkdmFsdWU7CnskdGFic30JfSwgXCRpbml0aWFsKTsKeyR0YWJzfX0sIFNpbXBsZVRlbXBsYXRlX0ZOOjpjYWxsKCdhcnJheV9mbGF0JywgClBIUDsKCQkJCQkJCSRjbG9zZSA9IDI7CgkJCQkJCQlicmVhazsKCQkJCQkJY2FzZSAnKyc6CgkJCQkJCQkkcmV0dXJuIC49ICdhcnJheV9zdW0oU2ltcGxlVGVtcGxhdGVfRk46OmNhbGwoXCdhcnJheV9mbGF0XCcsICc7CgkJCQkJCQkkY2xvc2UgPSAyOwoJCQkJCQkJYnJlYWs7CgkJCQkJCWNhc2UgJyonOgoJCQkJCQkJJHJldHVybiAuPSAnYXJyYXlfcHJvZHVjdChTaW1wbGVUZW1wbGF0ZV9GTjo6Y2FsbChcJ2FycmF5X2ZsYXRcJywgJzsKCQkJCQkJCSRjbG9zZSA9IDI7CgkJCQkJCQlicmVhazsKCQkJCQkJY2FzZSAnXFwnOgoJCQkJCQljYXNlICcvJzoKCQkJCQkJY2FzZSAnJSc6CgkJCQkJCQkkb3BzID0gYXJyYXkoCgkJCQkJCQkJJ1xcJyA9PiAncm91bmQoJXMgLyAkdmFsdWUpJywKCQkJCQkJCQknLycgPT4gJyglcyAvICR2YWx1ZSknLAoJCQkJCQkJCSclJyA9PiAnKCVzICUlICR2YWx1ZSknCgkJCQkJCQkpOwoJCQkJCQkJCgkJCQkJCQkkcmV0dXJuIC49ICdhcnJheV9tYXAoZnVuY3Rpb24oJHZhbHVlKXVzZSgmJCcgLiBzZWxmOjokdmFyX25hbWUgLiAnKXsnCgkJCQkJCQkJLidyZXR1cm4gJyAuIHNwcmludGYoCgkJCQkJCQkJCSRvcHNbJGJpdHNbJ29wJ11dLAoJCQkJCQkJCQlpc3NldCgkYml0c1snb3BfdmFsJ10pCgkJCQkJCQkJCQk/IHNlbGY6OnBhcnNlX3ZhbHVlKCRiaXRzWydvcF92YWwnXSkKCQkJCQkJCQkJCTogc2VsZjo6cmVuZGVyX3ZhcigkYml0c1sndmFyJ10sIGZhbHNlKQoJCQkJCQkJCSkKCQkJCQkJCQkuICc7fSwgU2ltcGxlVGVtcGxhdGVfRk46OmNhbGwoXCdhcnJheV9mbGF0XCcsICc7CgkJCQkJCQkkY2xvc2UgPSAyOwoJCQkJCQkJYnJlYWs7CgkJCQkJfQoJCQkJfQoJCQkJCgkJCQlpZigkY291bnQgPiAxKQoJCQkJewoJCQkJCSRyZXR1cm4gLj0gJ2FycmF5KCcgLiBpbXBsb2RlKCcsJywgJHZhbHVlcykgLiAnKSc7CgkJCQl9CgkJCQllbHNlCgkJCQl7CgkJCQkJJHJldHVybiAuPSAoJGNvdW50ICYmIHN0cmxlbigkdmFsdWVzWzBdKSA/ICR2YWx1ZXNbMF0gOiAnbnVsbCcpOwoJCQkJfQoJCQkJCgkJCQlyZXR1cm4gJHJldHVybiAuIHN0cl9yZXBlYXQoJyknLCAkY2xvc2UpIC4gJzsnOwoJCQl9LAoJCQkndW5zZXQnID0+IGZ1bmN0aW9uKCRkYXRhKXVzZSgmJHJlcGxhY2VtZW50LCAmJGJyYWNrZXRzLCAmJHRhYnMpewoJCQkJJHZhbHVlcyA9IHNlbGY6OnBhcnNlX3ZhbHVlcygkZGF0YSwgbnVsbCwgZmFsc2UpOwoJCQkJJHZhcnMgPSBhcnJheV9maWx0ZXIoJHZhbHVlcywgZnVuY3Rpb24oJHZhcil7CgkJCQkJcmV0dXJuICFzZWxmOjppc192YWx1ZSgkdmFyKTsKCQkJCX0pOwoJCQkJCgkJCQkkcmV0dXJuID0gJHRhYnMgLiAoCgkJCQkJY291bnQoJHZhbHVlcykgIT09IGNvdW50KCR2YXJzKQoJCQkJCQk/ICcvLyBXYXJuaW5nOiBpbnZhbGlkIHZhbHVlcyB3ZXJlIHBhc3NlZCcgLiBQSFBfRU9MIC4gJHRhYnMKCQkJCQkJOiAnJwoJCQkJKTsKCQkJCQoJCQkJcmV0dXJuICRyZXR1cm4gLiAoCgkJCQkJJHZhcnMKCQkJCQkJPyAndW5zZXQoJyAuIGltcGxvZGUoJywnLCAkdmFycykgLiAnKTsnCgkJCQkJCTogJy8vIFdhcm5pbmc6IG5vIHZhbHVlcyB3ZXJlIHBhc3NlZCBvciBhbGwgd2VyZSBmaWx0ZXJlZCBvdXQnCgkJCQkpOwoJCQl9LAoJCQknZ2xvYmFsJyA9PiBmdW5jdGlvbigkZGF0YSl1c2UoJiRyZXBsYWNlbWVudCwgJiRicmFja2V0cywgJiR0YWJzKXsKCQkJCSRkYXRhID0gc2VsZjo6c3BsaXRfdmFsdWVzKCRkYXRhLCAnICcpOwoJCQkJCgkJCQlyZXR1cm4gJHRhYnMgLiBzZWxmOjpyZW5kZXJfdmFyKGFycmF5X3NoaWZ0KCRkYXRhKSwgZmFsc2UpIC4gJyA9ICRHTE9CQUxTW1wnJyAuIGpvaW4oJ1wnXVtcJycsICRkYXRhKSAuICdcJ107JzsKCQkJfSwKCQkJJ2NhbGwnID0+IGZ1bmN0aW9uKCRkYXRhKXVzZSgmJHJlcGxhY2VtZW50LCAmJGJyYWNrZXRzLCAmJHRhYnMpewoJCQkJcHJlZ19tYXRjaCgnQF5ccyooPzxmbj4nIC4gc2VsZjo6JHJlZ2V4Wyd2YXInXSAuICcpXHMqKD86aW50b1xzKig/PGludG8+JyAuIHNlbGY6OiRyZWdleFsndmFyJ10gLiAnKVxzKik/KD88YXJncz4uKj8pJEAnLCAkZGF0YSwgJGJpdHMpOwoJCQkJCgkJCQkkdmFyID0gc2VsZjo6cmVuZGVyX3ZhcigkYml0c1snZm4nXSwgZmFsc2UpOwoJCQkJCgkJCQkkaW50byA9ICRiaXRzWydpbnRvJ10gPyBzZWxmOjpyZW5kZXJfdmFyKCRiaXRzWydpbnRvJ10sIGZhbHNlKSAuICcgPSAnIDogJyc7CgkJCQkkYXJncyA9IGltcGxvZGUoJywgJywgc2VsZjo6cGFyc2VfdmFsdWVzKCRiaXRzWydhcmdzJ10pKTsKCQkJCSRhbHRfbmFtZSA9IHN0cl9yZXBsYWNlKCcuJywgJ18nLCAkYml0c1snZm4nXSk7CgkJCQkKCQkJCXJldHVybiA8PDxQSFAKeyR0YWJzfXskaW50b31jYWxsX3VzZXJfZnVuY19hcnJheSgKeyR0YWJzfQkoaXNzZXQoeyR2YXJ9KSAmJiBpc19jYWxsYWJsZSh7JHZhcn0pCnskdGFic30JCT8geyR2YXJ9CnskdGFic30JCTogKFNpbXBsZVRlbXBsYXRlX0ZOOjptZXRob2RfZXhpc3RzKCd7JGJpdHNbJ2ZuJ119JykKeyR0YWJzfQkJCT8gU2ltcGxlVGVtcGxhdGVfRk46OmNhbGwoJ3skYml0c1snZm4nXX0nKQp7JHRhYnN9CQkJOiAneyRhbHRfbmFtZX0nCnskdGFic30JCSkKeyR0YWJzfQkpLAp7JHRhYnN9CWFycmF5KHskYXJnc30pCnskdGFic30pOwpQSFA7CgkJCX0sCgkJCSdwaHAnID0+IGZ1bmN0aW9uKCRkYXRhKXVzZSgmJHJlcGxhY2VtZW50LCAmJGJyYWNrZXRzLCAmJHRhYnMpewoJCQkJcmV0dXJuICR0YWJzIC4gJ2NhbGxfdXNlcl9mdW5jX2FycmF5KGZ1bmN0aW9uKCYkJyAuIHNlbGY6OiR2YXJfbmFtZSAuICcpeycgLiBQSFBfRU9MCgkJCQkJICAgLiAieyR0YWJzfVx0eyRkYXRhfTsiIC4gUEhQX0VPTAoJCQkJCSAgIC4gJHRhYnMgLiAnfSwgYXJyYXkoJiQnIC4gc2VsZjo6JHZhcl9uYW1lIC4gJykpOyc7CgkJCX0sCgkJCSdyZXR1cm4nID0+IGZ1bmN0aW9uKCRkYXRhKXVzZSgmJHJlcGxhY2VtZW50LCAmJGJyYWNrZXRzLCAmJHRhYnMpewoJCQkJcmV0dXJuICR0YWJzIC4gJ3JldHVybicgLiAoJGRhdGEgfHwgJGRhdGEgPT09ICcwJyA/ICcgJyAuIHNlbGY6OnBhcnNlX3ZhbHVlKCRkYXRhKSA6ICcnKSAuICc7JzsKCQkJfSwKCQkJJ2luYycgPT4gZnVuY3Rpb24oJGRhdGEpdXNlKCYkcmVwbGFjZW1lbnQsICYkYnJhY2tldHMsICYkdGFicyl7CgkJCQlwcmVnX21hdGNoKCdAXig/OlxzKmJ5XHMqKD88Ynk+JyAuIHNlbGY6OiRyZWdleFsndmFyX3ZhbHVlJ10gLiAnKVxzKik/KD88dmFsdWVzPi4qPykkQCcsICRkYXRhLCAkYml0cyk7CgkJCQkkdmFsdWVzID0gc2VsZjo6cGFyc2VfdmFsdWVzKCRiaXRzWyd2YWx1ZXMnXSwgJ1xzKixccyonLCBmYWxzZSk7CgkJCQkkaW5jID0gaXNzZXQoJGJpdHNbJ2J5J10pICYmICRiaXRzWydieSddICE9PSAnJyA/IHNlbGY6OnBhcnNlX3ZhbHVlKCRiaXRzWydieSddKTogJzEnOwoJCQkJCgkJCQkkcmV0dXJuID0gJyc7CgkJCQkKCQkJCWlmKCEkaW5jIHx8ICRpbmMgPT09ICciMCInIHx8ICRpbmMgPT09ICdudWxsJyB8fCAkaW5jID09PSAnZmFsc2UnKQoJCQkJewoJCQkJCWlmKCR0aGlzLT5vcHRpb25zWydvcHRpbWl6ZSddKQoJCQkJCXsKCQkJCQkJcmV0dXJuICJ7JHRhYnN9Ly8gfiBvcHRpbWl6YXRpb24gZW5hYmxlZCB+IGluY3JlbWVudCBieSB7JGluY30gcmVtb3ZlZCI7CgkJCQkJfQoJCQkJCWVsc2UKCQkJCQl7CgkJCQkJCSRyZXR1cm4gLj0gInskdGFic30vLyB+IG9wdGltaXphdGlvbiBESVNBQkxFRCB+IGluY3JlbWVudCBieSB7JGluY30gY291bGQgYmUgcmVtb3ZlZCIgLiBQSFBfRU9MOwoJCQkJCX0KCQkJCX0KCQkJCQoJCQkJJHZhcl9uYW1lID0gc2VsZjo6JHZhcl9uYW1lOwoJCQkJCgkJCQlmb3JlYWNoKCR2YWx1ZXMgYXMgJHZhbHVlKQoJCQkJewoJCQkJCWlmKCFpc3NldCgkdmFsdWVbMF0pICYmICFzZWxmOjppc192YWx1ZSgkdmFsdWVbMF0pKQoJCQkJCXsKCQkJCQkJY29udGludWU7CgkJCQkJfQoJCQkJCQoJCQkJCSRyZXR1cm4gLj0gInskdGFic317JHZhbHVlfSA9IFNpbXBsZVRlbXBsYXRlX0ZOOjpjYWxsKCdpbmMnLCBpc3NldCh7JHZhbHVlfSk/eyR2YWx1ZX06MCwgeyRpbmN9KTsiIC4gUEhQX0VPTDsKCQkJCX0KCQkJCQoJCQkJcmV0dXJuICRyZXR1cm47CgkJCQkKCQkJfSwKCQkJJ2ZuJyA9PiBmdW5jdGlvbigkZGF0YSl1c2UoJiRyZXBsYWNlbWVudCwgJiRicmFja2V0cywgJiR0YWJzKXsKCQkJCWlmKAoJCQkJCXByZWdfbWF0Y2goCgkJCQkJCSdAXlxzKignIC4gc2VsZjo6JHJlZ2V4Wyd2YXJfc2ltcGxlJ10gLiAnKSg/OlxzKygnIC4gc2VsZjo6JHJlZ2V4Wyd2YXJfbmFtZSddIC4gJyg/OixccyonIC4gc2VsZjo6JHJlZ2V4Wyd2YXJfbmFtZSddIC4gJykqKSk/JEAnLAoJCQkJCQkkZGF0YSwgJGJpdHMKCQkJCQkpID09PSBmYWxzZQoJCQkJKQoJCQkJewoJCQkJCXJldHVybiAnJzsKCQkJCX0KCQkJCQoJCQkJKyskYnJhY2tldHM7CgkJCQkKCQkJCSR2ZXJzaW9uID0gU2ltcGxlVGVtcGxhdGU6OnZlcnNpb24oKTsKCQkJCSR2YXJfbmFtZSA9IHNlbGY6OiR2YXJfbmFtZTsKCQkJCQoJCQkJJHJldHVybiA9ICR0YWJzIC4gc2VsZjo6cmVuZGVyX3ZhcigkYml0cyA/ICRiaXRzWzFdIDogbnVsbCwgZmFsc2UpIC4gPDw8UEhQCj0gZnVuY3Rpb24oKXVzZSgmXCRfKXsKeyR0YWJzfQlcJHskdmFyX25hbWV9ID0gYXJyYXkoCnskdGFic30JCSdhcmd2JyA9PiBmdW5jX2dldF9hcmdzKCksCnskdGFic30JCSdhcmdjJyA9PiBmdW5jX251bV9hcmdzKCksCnskdGFic30JCSdWRVJTSU9OJyA9PiAneyR2ZXJzaW9ufScsCnskdGFic30JCSdFT0wnID0+IFBIUF9FT0wsCnskdGFic30JCSdQQVJFTlQnID0+ICZcJF8KeyR0YWJzfQkpOwp7JHRhYnN9CVwkXyA9ICZcJHskdmFyX25hbWV9OwoKUEhQOwoJCQkJaWYoaXNzZXQoJGJpdHNbMl0pICYmICRiaXRzWzJdKQoJCQkJewoJCQkJCSRhcmdzID0gYXJyYXkoKTsKCQkJCQlmb3JlYWNoKHNlbGY6OnNwbGl0X3ZhbHVlcygkYml0c1syXSkgYXMgJHZhbHVlKQoJCQkJCXsKCQkJCQkJaWYoIXNlbGY6OmlzX3ZhbHVlKHNlbGY6OnBhcnNlX3ZhbHVlKCR2YWx1ZSkpKQoJCQkJCQl7CgkJCQkJCQkkYXJnc1tdID0gJHZhbHVlOwoJCQkJCQl9CgkJCQkJfQoJCQkJCQoJCQkJCWZvcmVhY2goJGFyZ3MgYXMgJGsgPT4gJGFyZykKCQkJCQl7CgkJCQkJCSRyZXR1cm4gLj0gInskdGFic30JXCR7JHZhcl9uYW1lfVtcInskYXJnfVwiXSA9ICZcJHskdmFyX25hbWV9W1wiYXJndlwiXVt7JGt9XTsiIC4gUEhQX0VPTDsKCQkJCQl9CgkJCQl9CgkJCQkKCQkJCXJldHVybiAkcmV0dXJuOwoJCQl9LAoJCQknZXZhbCcgPT4gZnVuY3Rpb24oJGRhdGEpdXNlKCYkcmVwbGFjZW1lbnQsICYkYnJhY2tldHMsICYkdGFicyl7CgkJCQkkcmV0dXJuID0gJyc7CgkJCQkkdmFsdWUgPSBzZWxmOjpwYXJzZV92YWx1ZSgkZGF0YSk7CgkJCQkKCQkJCWlmKCR0aGlzLT5vcHRpb25zWydvcHRpbWl6ZSddICYmIHNlbGY6OmlzX3ZhbHVlKCR2YWx1ZSkpCgkJCQl7CgkJCQkJJHJldHVybiA9ICR0YWJzIC4gJy8vIH4gb3B0aW1pemF0aW9uIGVuYWJsZWQgfiB0cnlpbmcgdG8gYXZvaWQgY29tcGlsaW5nIGluIHJ1bnRpbWUnIC4gUEhQX0VPTDsKCQkJCQkKCQkJCQlzdGF0aWMgJGNhY2hlZCA9IGFycmF5KCk7CgkJCQkJCgkJCQkJJHNoYTEgPSBzaGExKCR2YWx1ZSk7CgkJCQkJCgkJCQkJaWYoaXNzZXQoJGNhY2hlZFskc2hhMV0pKQoJCQkJCXsKCQkJCQkJJHJldHVybiAuPSAkdGFicyAuICcvLyB7QGV2YWx9IGNhY2hlZCBjb2RlIGZvdW5kOiBjYWNoZSBlbnRyeSAnOwoJCQkJCX0KCQkJCQllbHNlCgkJCQkJewoJCQkJCQkkcmV0dXJuIC49ICR0YWJzIC4gJy8vIHtAZXZhbH0gbm8gY2FjaGVkIGNvZGUgZm91bmQ6IGNyZWF0aW5nIGVudHJ5ICc7CgkJCQkJCQoJCQkJCQkkY29tcGlsZXIgPSBuZXcgU2ltcGxlVGVtcGxhdGVfQ29tcGlsZXIoJHRoaXMtPnRlbXBsYXRlLCBzdHJpcHNsYXNoZXModHJpbSgkdmFsdWUsICciJykpLCAkdGhpcy0+b3B0aW9ucyk7CgkJCQkJCQoJCQkJCQkkY2FjaGVkWyRzaGExXSA9IHNlbGY6OmZvcm1hdF9jb2RlKCRjb21waWxlci0+Z2V0UEhQKCkgLiAnLy8ge0BldmFsfSBlbmRlZCcsICR0YWJzKTsKCQkJCQkJCgkJCQkJCXVuc2V0KCRjb21waWxlcik7CgkJCQkJfQoJCQkJCQoJCQkJCSRyZXR1cm4gLj0gJHNoYTEgLiBQSFBfRU9MIC4gJGNhY2hlZFskc2hhMV07CgkJCQl9CgkJCQllbHNlCgkJCQl7CgkJCQkJJG9wdGlvbnMgPSBzZWxmOjpmb3JtYXRfY29kZSh2YXJfZXhwb3J0KCR0aGlzLT5vcHRpb25zLCB0cnVlKSwgJHRhYnMgLiAiXHRcdCIsIHRydWUpOwoJCQkJCQoJCQkJCSRyZXR1cm4gPSA8PDxQSFAKeyR0YWJzfS8vIH4gb3B0aW1pemF0aW9uIERJU0FCTEVEIG9yIHVuZmVhc2FibGUgfiBjb21waWxhdGlvbiBpbiBydW50aW1lIGlzIHJlcXVpcmVkCnskdGFic31jYWxsX3VzZXJfZnVuY19hcnJheShmdW5jdGlvbigpdXNlKCZcJERBVEEpewp7JHRhYnN9CVwkY29tcGlsZXIgPSBuZXcgU2ltcGxlVGVtcGxhdGVfQ29tcGlsZXIoXCR0aGlzLCB7JHZhbHVlfSwgeyRvcHRpb25zfSk7CnskdGFic30JXCRmbiA9IFwkY29tcGlsZXItPmdldEZOKCk7CnskdGFic30JcmV0dXJuIFwkZm4oXCREQVRBKTsKeyR0YWJzfX0sIGFycmF5KCkpOwpQSFA7CgkJCQl9CgkJCQkKCQkJCXJldHVybiAkcmV0dXJuOwoJCQl9CgkJKTsKCQkKCQkkdHJpbV9mbiA9ICR0aGlzLT5vcHRpb25zWyd0cmltJ10gPyAndHJpbScgOiAnJzsKCQkKCQkkdGhpcy0+cGhwIC49ICJcclxuZWNobyB7JHRyaW1fZm59KDw8PCciIC4gc2VsZjo6JHZhcl9uYW1lIC4gInskVVVJRH0nXHJcbiIKCQkJLiBwcmVnX3JlcGxhY2VfY2FsbGJhY2soCgkJCQkvLyBodHRwOi8vc3RhY2tvdmVyZmxvdy5jb20vYS82NDY0NTAwCgkJCQknfntAKGV2YWx8ZWNob2o/bD98cHJpbnR8aWZ8ZWxzZXxmb3J8d2hpbGV8ZWFjaHxkb3x1bnRpbHwoPzp1bik/c2V0fGNhbGx8Z2xvYmFsfHBocHxyZXR1cm58aW5jfGZufC8vPykoPzpcXHMqKC4qPykpP30oPz0oPzpbXiJcXFxcXSooPzpcXFxcLnwiKD86W14iXFxcXF0qXFxcXC4pKlteIlxcXFxdKiIpKSpbXiJdKiQpficsCgkJCQlmdW5jdGlvbigkbWF0Y2hlcyl1c2UoJiRyZXBsYWNlbWVudCwgJiRicmFja2V0cywgJiR0YWJzLCAmJFVVSUQsICYkdHJpbV9mbil7CgkJCQkJCgkJCQkJJHRhYnMgPSAkYnJhY2tldHMKCQkJCQkJPyBzdHJfcmVwZWF0KCJcdCIsICRicmFja2V0cyAtICgkbWF0Y2hlc1sxXSA9PT0gJy8nKSkKCQkJCQkJOiAnJzsKCQkJCQkKCQkJCQkkdmFyX25hbWUgPSBzZWxmOjokdmFyX25hbWU7CgkJCQkJCgkJCQkJJHBocCA9ICRyZXBsYWNlbWVudFskbWF0Y2hlc1sxXV0oaXNzZXQoJG1hdGNoZXNbMl0pID8gJG1hdGNoZXNbMl0gOiBudWxsKTsKCQkJCQkKCQkJCQlyZXR1cm4gIlxyXG57JHZhcl9uYW1lfXskVVVJRH1cclxuKTtcclxueyR0YWJzfS8vIHskbWF0Y2hlc1swXX1cclxueyRwaHB9XHJcblxyXG57JHRhYnN9ZWNobyB7JHRyaW1fZm59KDw8PCd7JHZhcl9uYW1lfXskVVVJRH0nXHJcbiI7CgkJCQl9LAoJCQkJJHN0ciAuICcnCgkJCSkKCQkJLiAiXHJcbiIgLiBzZWxmOjokdmFyX25hbWUgLiAieyRVVUlEfVxyXG4pO1xyXG4iOwoJCQoJCSR0aGlzLT5waHAgPSBwcmVnX3JlcGxhY2UoCgkJCWFycmF5KAoJCQkJJ0BcclxuXHQqZWNob1xzKicgLiAkdHJpbV9mbiAuICdcKDw8PFwnJyAuIHNlbGY6OiR2YXJfbmFtZSAuICRVVUlEIC4gJ1wnKD86XHMqXHJcbik/JyAuIHNlbGY6OiR2YXJfbmFtZSAuICRVVUlEIC4gJ1xyXG5cKTtAJywKCQkJCSdAXHJcbicgLiBzZWxmOjokdmFyX25hbWUgLiAkVVVJRCAuICdcclxuXCk7KFxyXG4pKlx0KmVjaG9ccyonIC4gJHRyaW1fZm4gLiAnXCg8PDxcJycgLiBzZWxmOjokdmFyX25hbWUgLiAkVVVJRCAuICdcJ0AnCgkJCSksCgkJCWFycmF5KAoJCQkJJycsICcnCgkJCSksCgkJCSR0aGlzLT5waHAKCQkpOwoJCQoJCWlmKCRicmFja2V0cykKCQl7CgkJCSR0aGlzLT5waHAgLj0gICJcclxuLy8gQVVUTy1DTE9TRVxyXG4iIC4gc3RyX3JlcGVhdCgnfTsnLCAkYnJhY2tldHMpOwoJCX0KCX0KCQoJZnVuY3Rpb24gZ2V0UEhQKCl7CgkJcmV0dXJuICR0aGlzLT5waHA7Cgl9CgkKCWZ1bmN0aW9uIGdldEZOKCl7CgkJaWYoISR0aGlzLT5mbikKCQl7CgkJCSR0aGlzLT5mbiA9IGV2YWwoJ3JldHVybiBmdW5jdGlvbigmJCcgLiBzZWxmOjokdmFyX25hbWUgLiAnKXsnCgkJCQkJLiBQSFBfRU9MIC4gJHRoaXMtPnBocCAuIFBIUF9FT0wKCQkJCS4gJ307JwoJCQkpOwoJCQkKCQkJJHRoaXMtPmZuID0gJHRoaXMtPmZuLT5iaW5kVG8oJHRoaXMtPnRlbXBsYXRlKTsKCQl9CgkJCgkJcmV0dXJuICR0aGlzLT5mbjsKCX0KCQoJZnVuY3Rpb24gX19jb25zdHJ1Y3QoU2ltcGxlVGVtcGxhdGUgJHRlbXBsYXRlLCAkY29kZSwgYXJyYXkgJG9wdGlvbnMgPSBhcnJheSgpKXsKCQkkdGhpcy0+b3B0aW9ucyA9ICRvcHRpb25zOwoJCSR0aGlzLT50ZW1wbGF0ZSA9ICR0ZW1wbGF0ZTsKCQkKCQkvLyBBTE1PU1QgdW5ndWVzc2FibGUgbmFtZSwgdG8gYXZvaWQgc3ludGF4IGVycm9ycwoJCSR0aGlzLT51dWlkID0gc3RyX3NodWZmbGUobXRfcmFuZCgpIC4gdGltZSgpIC4gc2hhMSgkY29kZSkpOwoJCQoJCSR0aGlzLT5jb21waWxlKCRjb2RlKTsKCX0KfQoKLy8gYmFzZSBjbGFzcwpjbGFzcyBTaW1wbGVUZW1wbGF0ZSB7Cglwcml2YXRlIHN0YXRpYyAkdmVyc2lvbiA9ICcwLjg0JzsKCQoJcHJpdmF0ZSAkZGF0YSA9IGFycmF5KCk7Cglwcml2YXRlICRzZXR0aW5ncyA9IGFycmF5KAoJCSdvcHRpbWl6ZScgPT4gdHJ1ZSwKCQkndHJpbScgPT4gZmFsc2UKCSk7CgkKCXByaXZhdGUgJGNvbXBpbGVyID0gbnVsbDsKCQoJZnVuY3Rpb24gX19jb25zdHJ1Y3QoJGNvZGUsIGFycmF5ICRvcHRpb25zID0gYXJyYXkoKSl7CgkJaWYoISRjb2RlKQoJCXsKCQkJdGhyb3cgbmV3IEV4Y2VwdGlvbignTm8gY29kZSB3YXMgcHJvdmlkZWQnKTsKCQl9CgkJCgkJaWYoJG9wdGlvbnMpCgkJewoJCQkkdGhpcy0+c2V0dGluZ3MgPSBhcnJheV9tZXJnZSgkdGhpcy0+c2V0dGluZ3MsICRvcHRpb25zKTsKCQl9CgkJCgkJJHRoaXMtPmNvbXBpbGVyID0gbmV3IFNpbXBsZVRlbXBsYXRlX0NvbXBpbGVyKCR0aGlzLCAkY29kZSwgJHRoaXMtPnNldHRpbmdzKTsKCX0KCQoJZnVuY3Rpb24gc2V0RGF0YSgka2V5LCAkdmFsdWUpewoJCSR0aGlzLT5kYXRhWyRrZXldID0gJHZhbHVlOwoJfQoJCglmdW5jdGlvbiBnZXREYXRhKCRrZXksICR2YWx1ZSl7CgkJcmV0dXJuIGlzc2V0KCR0aGlzLT5kYXRhWyRrZXldKSA/ICR0aGlzLT5kYXRhWyRrZXldIDogbnVsbDsKCX0KCQoJZnVuY3Rpb24gdW5zZXREYXRhKCRrZXkpewoJCXVuc2V0KCR0aGlzLT5kYXRhWyRrZXldKTsKCX0KCQoJZnVuY3Rpb24gbG9hZERhdGEoJGRhdGEpewoJCWZvcmVhY2goJGRhdGEgYXMgJGsgPT4gJHZhbHVlKQoJCXsKCQkJJHRoaXMtPmRhdGFbJGtdID0gJHZhbHVlOwoJCX0KCX0KCQoJZnVuY3Rpb24gY2xlYXJEYXRhKCl7CgkJJHRoaXMtPmRhdGEgPSBhcnJheSgpOwoJfQoJCglmdW5jdGlvbiBnZXRQSFAoKXsKCQlyZXR1cm4gJHRoaXMtPmNvbXBpbGVyLT5nZXRQSFAoKTsKCX0KCQoJZnVuY3Rpb24gcmVuZGVyKCl7CgkJJHRoaXMtPmRhdGFbJ2FyZ3YnXSA9IGZ1bmNfZ2V0X2FyZ3MoKTsKCQkkdGhpcy0+ZGF0YVsnYXJnYyddID0gZnVuY19udW1fYXJncygpOwoJCQoJCSR0aGlzLT5kYXRhWydWRVJTSU9OJ10gPSBzZWxmOjokdmVyc2lvbjsKCQkkdGhpcy0+ZGF0YVsnRU9MJ10gPSBQSFBfRU9MOwoJCQoJCSRmbiA9ICR0aGlzLT5jb21waWxlci0+Z2V0Rk4oKTsKCQkKCQlyZXR1cm4gJGZuKCR0aGlzLT5kYXRhKTsKCX0KCQoJc3RhdGljIGZ1bmN0aW9uIGZyb21GaWxlKCRwYXRoLCBhcnJheSAkb3B0aW9ucyA9IGFycmF5KCkpewoJCXJldHVybiBuZXcgc2VsZihmaWxlX2dldF9jb250ZW50cygkcGF0aCksICRvcHRpb25zKTsKCX0KCQoJc3RhdGljIGZ1bmN0aW9uIGZyb21TdHJpbmcoJHN0cmluZywgYXJyYXkgJG9wdGlvbnMgPSBhcnJheSgpKXsKCQlyZXR1cm4gbmV3IHNlbGYoJHN0cmluZywgJG9wdGlvbnMpOwoJfQoJCglzdGF0aWMgZnVuY3Rpb24gdmVyc2lvbigpewoJCXJldHVybiBzZWxmOjokdmVyc2lvbjsKCX0KfQoKJGNvZGUgPSA8PDwnQ09ERScKe0Bmb3JpIHRvNTF9e0Bmb3J0bzc3fTxie0BpZmkgaXMgbG93ZXJ0aGFuMjZ9IHh7QC99PkB7QC99PGJyPntAL308c3R5bGU+Yntjb2xvcjojMDA1N0I3fVt4XXtjb2xvcjpnb2xkCkNPREU7Cgoka2V5ID0gJ3VuZ29sZmVkJzsgLy8gZ29sZmVkIC0gdW5nb2xmZWQKCiR4ID0gbmV3IFNpbXBsZVRlbXBsYXRlKCRjID0gJGNvZGUpOwoKZWNobyAkeC0+cmVuZGVyKCksIFBIUF9FT0wsIFBIUF9FT0wsICctLS0tLS0tLS0tLS0tLS0tLSB2IFBIUCB2IC0tLS0tLS0tLS0tLS0tLS0tJywgUEhQX0VPTCwgJHgtPmdldFBIUCgpOwplY2hvIFBIUF9FT0wsIFBIUF9FT0wsICctLS0tLS0tLS0tLS0tLS0tLSB2IENPREUgKCcsICBzdHJsZW4oJGMpLCAnYiAtICcsICRrZXksICcpIHYgLS0tLS0tLS0tLS0tLS0tLS0nLCBQSFBfRU9MLCAkYzsKICAgICAgICAgICAg