<?php
final class SimpleTemplate_SyntaxError extends \Exception {
function __construct($message) {
$this->message = 'SimpleTemplate - Syntax Error: ' . $message;
}
}
// contains all functions needed
final class SimpleTemplate_FN {
private static
$fn = array(); private static $init = false;
private static function init(){
self::$init = true;
// array_flat -> http://stackoverflow.com/a/1320156
'array_flat' => function(){
$return[] = $value;
});
return $return;
},
'inc' => function($_, $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 $_;
},
'len' => function($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;
},
'repeat' => function($_, $times = 1){
if($times < 1)
{
return '';
}
});
return $_;
}
);
}
static
function call
($fn, $args = array()){ if(!self::$init)
{
self::init();
}
if(!self::$fn[$fn])
{
throw new Exception('Invalid function ' . $fn);
}
}
static function name_list(){
if(!self::$init)
{
self::init();
}
}
}
// 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)\s+)?[_a-zA-Z]\w*(?:\.\w*)?)',
'value' => '(?:(?:"[^"\\\\]*(?:\\\\.[^"\\\\]*)*")|[\-+]?\d*(?:\.\d*)?|true|false|null)',
'var_value' => '(?:(?:"[^"\\\\]*(?:\\\\.[^"\\\\]*)*")|[\-+]?[\d\W]\d*(?:\.\d*)?|true|false|null|(?:(?:U|unsafe)\s+)?[_a-zA-Z]\w*(?:\.\w*)*)'
);
private $options = array(); private $template = null;
private $fn = null;
private static $fn_body = <<<'PHP'
// - FUNCTION BOILERPLATE
$FN = array();
array_map(function($name)use(&$FN){
$FN[$name] = function()use($name){
return SimpleTemplate_FN::call($name, func_get_args());
};
},
SimpleTemplate_FN::name_list()
);
// - END FUNCTION BOILERPLATE -
// - CODE
%s
// - END CODE -
PHP;
private $php = '';
private static function render_var($name = null, $safe = true){
preg_match('@^\s*(?:(?<unsafe>U|unsafe)\s+)?(?<var>.*)$@', $name ?
: self::$default_var_name, $bits);
$var = '$' . self::$var_name . ($bits['var'] ?
'[\'' . join('\'][\'', explode('.', $bits['var'])) . '\']' : '');
return $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){
$value_bits = self::split_values($values, $delimiter);
foreach($value_bits as $k => $value)
{
$value_bits[$k] = self::parse_value($value, $safe);
}
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)?)\s*(' . self::$regex['var_value'] . ')?)?@',
$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((array)' . self::parse_value($var1) . ', ' . self::parse_value($var2) . ')';
},
'isset' => function($data, $var1){
return ($data === 'not' ? '!': '') . 'isset(' . self::render_var($var1, false) . ')';
}
);
{
return $fn[$ops[0]](isset($ops[1]) ?
$ops[1]: '', $bits[1], isset($bits[3]) ?
$bits[3] : self::$default_var_name); }
else
{
return self::parse_value($bits[1]);
}
}
private static function parse_value($value, $safe = true){
if($value === '' || $value === '""')
{
return $value;
}
else if(preg_match('@^' . self::$regex['value'] . '$@', $value)) {
return $value[0] === '"'
: $value;
}
else if(preg_match('@^' . self::$regex['var'] . '$@', $value)) {
return self::render_var($value, $safe);
}
else
{
throw new SimpleTemplate_SyntaxError('Invalid value syntax: ' . $value);
}
}
private static function is_value($value){
return strlen($value) && $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 . ', $FN[\'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'] . ')\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::render_var($bits['var'], false);
$vars_as = self::render_var(isset($bits['as']) ?
$bits['as'] : '', false); $vars_key = self::render_var(isset($bits['key']) ?
$bits['as'] : '__', false);
return <<<PHP
{$tabs}// loop variables
{$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} : array_keys(range(0, strlen(\${$tmp_name}val = \${$tmp_name}val . '') - 1));
{$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}}, \$FN['array_flat'](
PHP;
$close = 2;
break;
case '+':
$return .= 'array_sum($FN[\'array_flat\'](';
$close = 2;
break;
case '*':
$return .= 'array_product($FN[\'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)
)
. ';}, $FN[\'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);
return $tabs . ($bits['into'] ? self::render_var($bits['into'], false) . ' = ' : '')
. 'call_user_func_array('
. 'isset(' . $var . ') && is_callable(' . $var . ')'
. '? ' . $var
. ': (isset($FN["' . $bits['fn'] . '"])'
. '? $FN["' . $bits['fn'] . '"]'
. '), '
. 'array(' . implode(',', self::parse_values($bits['args'])) . '));'; },
'php' => function($data)use(&$replacement, &$brackets, &$tabs){
return $tabs . 'call_user_func_array(function($FN, &$' . self::$var_name . '){' . PHP_EOL
. "{$tabs}\t{$data};" . PHP_EOL
. $tabs . '}, array($FN, &$' . self::$var_name . '));';
},
'return' => function($data)use(&$replacement, &$brackets, &$tabs){
return $tabs . 'return ' . ($data ? 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} = \$FN['inc'](isset({$value})?{$value}:0, {$inc});" . PHP_EOL;
}
return $return;
},
'fn' => function($data)use(&$replacement, &$brackets, &$tabs){
if(
'@^\s*(' . self::$regex['var'] . ')\s*(?:\s+(.*))?$@',
$data, $bits
) === false
)
{
return '';
}
++$brackets;
$version = SimpleTemplate::version();
$var_name = self::$var_name;
$return = $tabs . self::render_var($bits[1], false) . <<<PHP
= function()use(&\$FN, &\$_){
{$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(&\$FN, &\$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
. sprintf(self::$fn_body, $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.62';
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;
}
}
$c = <<<'CODE'
{@setA argv.1}{@eachargv.0}{@setC C,"{@echoA.",_,"}"}{@calljoin intoC"",C}{@/}{@evalC}
CODE;
/*
ungolfed:
$c = <<<'CODE'
{@set args argv.1}
{@each argv.0 as number}
{@set code code, "{@echo args.", number , "}"}
{@call join into code "", code}
{@/}
{@eval code}
CODE;*/
$x = new SimpleTemplate($c);
echo $x->render('0123456789', 'abcdefghijkl');
PD9waHAKCglmaW5hbCBjbGFzcyBTaW1wbGVUZW1wbGF0ZV9TeW50YXhFcnJvciBleHRlbmRzIFxFeGNlcHRpb24gewoJCWZ1bmN0aW9uIF9fY29uc3RydWN0KCRtZXNzYWdlKSB7CgkJCSR0aGlzLT5tZXNzYWdlID0gJ1NpbXBsZVRlbXBsYXRlIC0gU3ludGF4IEVycm9yOiAnIC4gJG1lc3NhZ2U7CgkJfQoJfQoKCS8vIGNvbnRhaW5zIGFsbCBmdW5jdGlvbnMgbmVlZGVkCglmaW5hbCBjbGFzcyBTaW1wbGVUZW1wbGF0ZV9GTiB7CgkJcHJpdmF0ZSBzdGF0aWMgJGZuID0gYXJyYXkoKTsKCQlwcml2YXRlIHN0YXRpYyAkaW5pdCA9IGZhbHNlOwoJCQoJCXByaXZhdGUgc3RhdGljIGZ1bmN0aW9uIGluaXQoKXsKCQkJc2VsZjo6JGluaXQgPSB0cnVlOwoJCQkKCQkJc2VsZjo6JGZuID0gYXJyYXkoCgkJCQkvLyBhcnJheV9mbGF0IC0+IGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS9hLzEzMjAxNTYKCQkJCSdhcnJheV9mbGF0JyA9PiBmdW5jdGlvbigpewoJCQkJCSRyZXR1cm4gPSBhcnJheSgpOwoJCQkJCSRhcnJheSA9IGZ1bmNfZ2V0X2FyZ3MoKTsKCQkJCQlhcnJheV93YWxrX3JlY3Vyc2l2ZSgkYXJyYXksIGZ1bmN0aW9uKCR2YWx1ZSl1c2UoJiRyZXR1cm4pewoJCQkJCQkkcmV0dXJuW10gPSAkdmFsdWU7CgkJCQkJfSk7CgkJCQkJcmV0dXJuICRyZXR1cm47CgkJCQl9LAoJCQkJJ2luYycgPT4gZnVuY3Rpb24oJF8sICRieSA9IDEpewoJCQkJCS8vIGlmIHRoZXJlJ3Mgbm8gaW5jcmVtZW50IHZhbHVlCgkJCQkJaWYoISRieSkKCQkJCQl7CgkJCQkJCXJldHVybiAkXzsKCQkJCQl9CgkJCQkJCgkJCQkJLy8gaWYgdGhlcmUncyBubyB2YWx1ZQoJCQkJCWlmKCEkXykKCQkJCQl7CgkJCQkJCXJldHVybiAkYnk7CgkJCQkJfQoJCQkJCQoJCQkJCSRmbiA9IGZ1bmN0aW9uKCRfLCAkYnkpewoJCQkJCQlzd2l0Y2goZ2V0dHlwZSgkXykpCgkJCQkJCXsKCQkJCQkJCWNhc2UgJ05VTEwnOgoJCQkJCQkJY2FzZSAnbnVsbCc6CgkJCQkJCQkJcmV0dXJuICRieTsKCQkJCQkJCWNhc2UgJ2ludGVnZXInOgoJCQkJCQkJY2FzZSAnZG91YmxlJzoKCQkJCQkJCWNhc2UgJ2Zsb2F0JzoKCQkJCQkJCQlyZXR1cm4gJF8gKyAkYnk7CgkJCQkJCQljYXNlICdzdHJpbmcnOgoJCQkJCQkJCWlmKCRfID09PSAnJykKCQkJCQkJCQl7CgkJCQkJCQkJCXJldHVybiAnJzsKCQkJCQkJCQl9CgkJCQkJCQkJCgkJCQkJCQkJJF9ieSA9IGFicygkYnkpOwoJCQkJCQkJCQoJCQkJCQkJCWZvcigkaSA9IDA7ICRpIDwgJF9ieTsgJGkrKykKCQkJCQkJCQl7CgkJCQkJCQkJCWlmKCRieSA+IDApCgkJCQkJCQkJCXsKCQkJCQkJCQkJCSsrJF87CgkJCQkJCQkJCX0KCQkJCQkJCQkJZWxzZQoJCQkJCQkJCQl7CgkJCQkJCQkJCQkkbGFzdCA9IHN0cmxlbigkXykgLSAxOwoJCQkJCQkJCQkJCgkJCQkJCQkJCQlpZigkX1skbGFzdF0gPT09ICdhJyB8fCAkX1skbGFzdF0gPT09ICdBJykKCQkJCQkJCQkJCXsKCQkJCQkJCQkJCQkvLyBoYW5kbGVzIGFhYWEgLT4genp6CgkJCQkJCQkJCQkJJF8gPSBwcmVnX3JlcGxhY2VfY2FsbGJhY2soJ0BbYUFdKyRAJywgZnVuY3Rpb24oJHN0cil7CgkJCQkJCQkJCQkJCXJldHVybiBzdHJfcmVwZWF0KCRzdHJbMF1bMF0gPT09ICdhJyA/ICd6JzogJ1onLCBzdHJsZW4oJHN0clswXSkgLSAxKTsKCQkJCQkJCQkJCQl9LCAkXyk7CgkJCQkJCQkJCQl9CgkJCQkJCQkJCQllbHNlCgkJCQkJCQkJCQl7CgkJCQkJCQkJCQkJJF9bJGxhc3RdID0gY2hyKG9yZCgkX1skbGFzdF0pIC0gMSk7CgkJCQkJCQkJCQl9CgkJCQkJCQkJCX0KCQkJCQkJCQl9CgkJCQkJCQkJCgkJCQkJCQkJcmV0dXJuICRfOwoJCQkJCQkJZGVmYXVsdDoKCQkJCQkJCQlyZXR1cm4gJGJ5OwoJCQkJCQl9CgkJCQkJfTsKCQoJCQkJCQoJCQkJCWlmKGdldHR5cGUoJF8pID09PSAnYXJyYXknKQoJCQkJCXsKCQkJCQkJYXJyYXlfd2Fsa19yZWN1cnNpdmUoJF8sIGZ1bmN0aW9uKCYkdmFsdWUpdXNlKCYkZm4sICYkYnkpewoJCQkJCQkJJHZhbHVlID0gJGZuKCR2YWx1ZSwgJGJ5KTsKCQkJCQkJfSk7CgkJCQkJfQoJCQkJCWVsc2UKCQkJCQl7CgkJCQkJCSRfID0gJGZuKCRfLCAkYnkpOwoJCQkJCX0KCQkJCQkKCQkJCQlyZXR1cm4gJF87CgkJCQl9LAoJCQkJJ2xlbicgPT4gZnVuY3Rpb24oJGFyZ3MpewoJCQkJCSRyZXN1bHQgPSBhcnJheSgpOwoJCQkJCQoJCQkJCWlmKGZ1bmNfbnVtX2FyZ3MoKSA+IDEpCgkJCQkJewoJCQkJCQkkYXJncyA9IGZ1bmNfZ2V0X2FyZ3MoKTsKCQkJCQl9CgkJCQkJZWxzZQoJCQkJCXsKCQkJCQkJJGFyZ3MgPSBhcnJheSgkYXJncyk7CgkJCQkJfQoJCQkJCQoJCQkJCWZvcmVhY2goJGFyZ3MgYXMgJGFyZykKCQkJCQl7CgkJCQkJCXN3aXRjaChnZXR0eXBlKCRhcmcpKQoJCQkJCQl7CgkJCQkJCQljYXNlICdhcnJheSc6CgkJCQkJCQkJJHJlc3VsdFtdID0gY291bnQoJGFyZyk7CgkJCQkJCQkJYnJlYWs7CgkJCQkJCQljYXNlICdzdHJpbmcnOgoJCQkJCQkJCSRyZXN1bHRbXSA9IHN0cmxlbigkYXJnKTsKCQkJCQkJCQlicmVhazsKCQkJCQkJCWNhc2UgJ2ludGVnZXInOgoJCQkJCQkJY2FzZSAnZG91YmxlJzoKCQkJCQkJCWNhc2UgJ2Zsb2F0JzoKCQkJCQkJCQkkcmVzdWx0W10gPSAwOwoJCQkJCQkJCWJyZWFrOwoJCQkJCQkJZGVmYXVsdDoKCQkJCQkJCQkkcmVzdWx0W10gPSBudWxsOwoJCQkJCQl9CgkJCQkJfQoJCQkJCQoJCQkJCXJldHVybiAkcmVzdWx0OwoJCQkJfSwKCQkJCSdyZXBlYXQnID0+IGZ1bmN0aW9uKCRfLCAkdGltZXMgPSAxKXsKCQkJCQlpZigkdGltZXMgPCAxKQoJCQkJCXsKCQkJCQkJcmV0dXJuICcnOwoJCQkJCX0KCQkJCQkKCQkJCQlhcnJheV93YWxrX3JlY3Vyc2l2ZSgkXywgZnVuY3Rpb24oJiR2YWx1ZSl1c2UoJiR0aW1lcyl7CgkJCQkJCSR2YWx1ZSA9IHN0cl9yZXBlYXQoJHZhbHVlLCAkdGltZXMpOwoJCQkJCX0pOwoJCQkJCQoJCQkJCXJldHVybiAkXzsKCQkJCX0KCQkJKTsKCQl9CgkJCgkJc3RhdGljIGZ1bmN0aW9uIGNhbGwoJGZuLCAkYXJncyA9IGFycmF5KCkpewoJCQlpZighc2VsZjo6JGluaXQpCgkJCXsKCQkJCXNlbGY6OmluaXQoKTsKCQkJfQoJCQkKCQkJaWYoIXNlbGY6OiRmblskZm5dKQoJCQl7CgkJCQl0aHJvdyBuZXcgRXhjZXB0aW9uKCdJbnZhbGlkIGZ1bmN0aW9uICcgLiAkZm4pOwoJCQl9CgkJCQoJCQlyZXR1cm4gY2FsbF91c2VyX2Z1bmNfYXJyYXkoc2VsZjo6JGZuWyRmbl0sICRhcmdzKTsKCQl9CgkJCgkJc3RhdGljIGZ1bmN0aW9uIG5hbWVfbGlzdCgpewoJCQlpZighc2VsZjo6JGluaXQpCgkJCXsKCQkJCXNlbGY6OmluaXQoKTsKCQkJfQoJCQkKCQkJcmV0dXJuIGFycmF5X2tleXMoc2VsZjo6JGZuKTsKCQl9Cgl9CgkKCS8vIGNvbXBpbGVyIGNsYXNzCgljbGFzcyBTaW1wbGVUZW1wbGF0ZV9Db21waWxlciB7CgkJcHJpdmF0ZSAkdXVpZCA9IG51bGw7CgkJCgkJcHJpdmF0ZSBzdGF0aWMgJHZhcl9uYW1lID0gJ0RBVEEnOwoJCXByaXZhdGUgc3RhdGljICRkZWZhdWx0X3Zhcl9uYW1lID0gJ18nOwoJCQoJCXByaXZhdGUgc3RhdGljICRyZWdleCA9IGFycmF5KAoJCQkndmFyJyA9PiAnKD86KD86KD86VXx1bnNhZmUpXHMrKT9bX2EtekEtWl1cdyooPzpcLlx3Kik/KScsCgkJCSd2YWx1ZScgPT4gJyg/Oig/OiJbXiJcXFxcXSooPzpcXFxcLlteIlxcXFxdKikqIil8W1wtK10/XGQqKD86XC5cZCopP3x0cnVlfGZhbHNlfG51bGwpJywKCQkJJ3Zhcl92YWx1ZScgPT4gJyg/Oig/OiJbXiJcXFxcXSooPzpcXFxcLlteIlxcXFxdKikqIil8W1wtK10/W1xkXFddXGQqKD86XC5cZCopP3x0cnVlfGZhbHNlfG51bGx8KD86KD86VXx1bnNhZmUpXHMrKT9bX2EtekEtWl1cdyooPzpcLlx3KikqKScKCQkpOwoJCQoJCXByaXZhdGUgJG9wdGlvbnMgPSBhcnJheSgpOwoJCXByaXZhdGUgJHRlbXBsYXRlID0gbnVsbDsKCQkKCQlwcml2YXRlICRmbiA9IG51bGw7CgkJcHJpdmF0ZSBzdGF0aWMgJGZuX2JvZHkgPSA8PDwnUEhQJwovLyAtIEZVTkNUSU9OIEJPSUxFUlBMQVRFCiRGTiA9IGFycmF5KCk7CgphcnJheV9tYXAoZnVuY3Rpb24oJG5hbWUpdXNlKCYkRk4pewoJCSRGTlskbmFtZV0gPSBmdW5jdGlvbigpdXNlKCRuYW1lKXsKCQkJcmV0dXJuIFNpbXBsZVRlbXBsYXRlX0ZOOjpjYWxsKCRuYW1lLCBmdW5jX2dldF9hcmdzKCkpOwoJCX07Cgl9LAoJU2ltcGxlVGVtcGxhdGVfRk46Om5hbWVfbGlzdCgpCik7Ci8vIC0gRU5EIEZVTkNUSU9OIEJPSUxFUlBMQVRFIC0KCi8vIC0gQ09ERQolcwovLyAtIEVORCBDT0RFIC0KUEhQOwoJCQoJCXByaXZhdGUgJHBocCA9ICcnOwoJCQoJCXByaXZhdGUgc3RhdGljIGZ1bmN0aW9uIHJlbmRlcl92YXIoJG5hbWUgPSBudWxsLCAkc2FmZSA9IHRydWUpewoJCQlwcmVnX21hdGNoKCdAXlxzKig/Oig/PHVuc2FmZT5VfHVuc2FmZSlccyspPyg/PHZhcj4uKikkQCcsICRuYW1lID86IHNlbGY6OiRkZWZhdWx0X3Zhcl9uYW1lLCAkYml0cyk7CgkJCQoJCQkkdmFyID0gJyQnIC4gc2VsZjo6JHZhcl9uYW1lIC4gKCRiaXRzWyd2YXInXSA/ICdbXCcnIC4gam9pbignXCddW1wnJywgZXhwbG9kZSgnLicsICRiaXRzWyd2YXInXSkpIC4gJ1wnXScgOiAnJyk7CgkJCQoJCQlyZXR1cm4gJHNhZmUgJiYgISRiaXRzWyd1bnNhZmUnXSA/ICcoaXNzZXQoJyAuICR2YXIgLiAnKT8nIC4gJHZhciAuICc6bnVsbCknIDogJHZhcjsKCQl9CgkJCgkJcHJpdmF0ZSBzdGF0aWMgZnVuY3Rpb24gc3BsaXRfdmFsdWVzKCR2YWx1ZXMsICRkZWxpbWl0ZXIgPSAnXHMqLFxzKicpewoJCQkvLyBodHRwOi8vc3RhY2tvdmVyZmxvdy5jb20vYS81Njk2MTQxLyAtLT4gcmVnZXggcXVvdGVkIHN0cmluZwoJCQkvLyBodHRwOi8vc3RhY2tvdmVyZmxvdy5jb20vYS82MzI1NTIvIC0tPiByZWdleCB0byBtYXRjaCAkZGVsaW1pdGVyIG91dHNpZGUgcXVvdGVzCgkJCXJldHVybiBwcmVnX3NwbGl0KCdAKCcgLiAoJGRlbGltaXRlciA/OiAnXHMqLFxzKicpIC4gJykoPz0oPzpbXiJdfCJbXiJcXFxcXSooPzpcXFxcLlteIlxcXFxdKikqIikqJClAJywgJHZhbHVlcyk7CgkJfQoJCQoJCXByaXZhdGUgc3RhdGljIGZ1bmN0aW9uIHBhcnNlX3ZhbHVlcygkdmFsdWVzLCAkZGVsaW1pdGVyID0gJ1xzKixccyonLCAkc2FmZSA9IHRydWUpewoJCQkkdmFsdWVfYml0cyA9IHNlbGY6OnNwbGl0X3ZhbHVlcygkdmFsdWVzLCAkZGVsaW1pdGVyKTsKCQkJCgkJCWZvcmVhY2goJHZhbHVlX2JpdHMgYXMgJGsgPT4gJHZhbHVlKQoJCQl7CgkJCQkkdmFsdWVfYml0c1ska10gPSBzZWxmOjpwYXJzZV92YWx1ZSgkdmFsdWUsICRzYWZlKTsKCQkJfQoJCQkKCQkJcmV0dXJuICR2YWx1ZV9iaXRzOwoJCX0KCQkKCQlwcml2YXRlIHN0YXRpYyBmdW5jdGlvbiBwYXJzZV9ib29sZWFuKCRkYXRhKXsKCQkJaWYoCgkJCQkhcHJlZ19tYXRjaCgKCQkJCQknQCgnIC4gc2VsZjo6JHJlZ2V4Wyd2YXJfdmFsdWUnXSAuICcpXHMqKD86KGlzc2V0fGlzKD86KD86XHMqbm90fG5cJz90KT9ccyooPzooPzpncmVhdGVyfGxvd2VyKSg/OlxzKnRoYW4pP3xlcXVhbCg/OlxzKnRvKT98ZXF1YWx8YXwoPzooPzppbnN0YW5jZXxtdWx0aXBsZXxtb2QpKD86XHMqb2YpPyl8bWF0Y2hlcykpP3xoYXMoPzpccypub3QpPylccyooJyAuIHNlbGY6OiRyZWdleFsndmFyX3ZhbHVlJ10gLiAnKT8pP0AnLAoJCQkJCSRkYXRhLCAkYml0cwoJCQkJKQoJCQkpCgkJCXsKCQkJCXJldHVybiAnJzsKCQkJfQoJCQkKCQkJJGZuID0gYXJyYXkoCgkJCQknaXMnID0+IGZ1bmN0aW9uKCRkYXRhLCAkdmFyMSwgJHZhcjIpewoJCQkJCSRzeW1ib2xzID0gYXJyYXkoCgkJCQkJCScnID0+ICclcyA9PT0gJXMnLAoJCQkJCQknYScgPT4gJ2dldHR5cGUoJXMpID09PSAlcycsCgkJCQkJCSdpbnN0YW5jZScgPT4gJ2lzX2EoJXMsICVzKScsCgkJCQkJCSdlcXVhbCcgPT4gJyVzID09ICVzJywKCQkJCQkJJ2xvd2VyJyA9PiAnJXMgPCAlcycsCgkJCQkJCSdncmVhdGVyJyA9PiAnJXMgPiAlcycsCgkJCQkJCSdtdWx0aXBsZScgPT4gJyEoJXMgJSUgJXMpJywKCQkJCQkJJ21vZCcgPT4gJyVzICUlICVzJywKCQkJCQkJJ21hdGNoZXMnID0+ICdwcmVnX21hdGNoKCUyJHMsICUxJHMpJwoJCQkJCSk7CgkJCQkJCgkJCQkJcHJlZ19tYXRjaCgnQCg/PG5vdD5ub3QpP1xzKig/PG9wZXJhdGlvbj5lcXVhbHxsb3dlcnxncmVhdGVyfGF8aW5zdGFuY2V8bXVsdGlwbGV8bW9kfG1hdGNoZXMpP1xzKig/Om9mfHRvfHRoYW4pP1xzKkAnLCAkZGF0YSwgJGJpdHMpOwoJCQkJCQoJCQkJCXJldHVybiAoaXNzZXQoJGJpdHNbJ25vdCddKSAmJiAkYml0c1snbm90J10gIT09ICcnID8gJyEnOiAnJykgLiAnKCcgLiBzcHJpbnRmKCRzeW1ib2xzW2lzc2V0KCRiaXRzWydvcGVyYXRpb24nXSkgPyAkYml0c1snb3BlcmF0aW9uJ106ICcnXSwgc2VsZjo6cGFyc2VfdmFsdWUoJHZhcjEpLCBzZWxmOjpwYXJzZV92YWx1ZSgkdmFyMikpIC4gJyknOwoJCQkJfSwKCQkJCSdoYXMnID0+IGZ1bmN0aW9uKCRkYXRhLCAkdmFyMSwgJHZhcjIpewoJCQkJCXJldHVybiAoJGRhdGEgPT09ICdub3QnID8gJyEnOiAnJykgLiAnYXJyYXlfa2V5X2V4aXN0cygoYXJyYXkpJyAuIHNlbGY6OnBhcnNlX3ZhbHVlKCR2YXIxKSAuICcsICcgLiBzZWxmOjpwYXJzZV92YWx1ZSgkdmFyMikgLiAnKSc7CgkJCQl9LAoJCQkJJ2lzc2V0JyA9PiBmdW5jdGlvbigkZGF0YSwgJHZhcjEpewoJCQkJCXJldHVybiAoJGRhdGEgPT09ICdub3QnID8gJyEnOiAnJykgLiAnaXNzZXQoJyAuIHNlbGY6OnJlbmRlcl92YXIoJHZhcjEsIGZhbHNlKSAuICcpJzsKCQkJCX0KCQkJKTsKCQkJCgkJCWlmKGlzc2V0KCRiaXRzWzJdKSkKCQkJewoJCQkJJG9wcyA9IGV4cGxvZGUoJyAnLCAkYml0c1syXSwgMik7CgkJCQkKCQkJCXJldHVybiAkZm5bJG9wc1swXV0oaXNzZXQoJG9wc1sxXSkgPyAkb3BzWzFdOiAnJywgJGJpdHNbMV0sIGlzc2V0KCRiaXRzWzNdKSA/ICRiaXRzWzNdIDogc2VsZjo6JGRlZmF1bHRfdmFyX25hbWUpOwoJCQl9CgkJCWVsc2UKCQkJewoJCQkJcmV0dXJuIHNlbGY6OnBhcnNlX3ZhbHVlKCRiaXRzWzFdKTsKCQkJfQoJCX0KCQkKCQlwcml2YXRlIHN0YXRpYyBmdW5jdGlvbiBwYXJzZV92YWx1ZSgkdmFsdWUsICRzYWZlID0gdHJ1ZSl7CgkJCWlmKCR2YWx1ZSA9PT0gJycgfHwgJHZhbHVlID09PSAnIiInKQoJCQl7CgkJCQlyZXR1cm4gJHZhbHVlOwoJCQl9CgkJCWVsc2UgaWYocHJlZ19tYXRjaCgnQF4nIC4gc2VsZjo6JHJlZ2V4Wyd2YWx1ZSddIC4gJyRAJywgJHZhbHVlKSkKCQkJewoJCQkJcmV0dXJuICR2YWx1ZVswXSA9PT0gJyInCgkJCQkJPyBzdHJfcmVwbGFjZSgnJCcsICdcXCQnLCAkdmFsdWUpCgkJCQkJOiAkdmFsdWU7CgkJCX0KCQkJZWxzZSBpZihwcmVnX21hdGNoKCdAXicgLiBzZWxmOjokcmVnZXhbJ3ZhciddIC4gJyRAJywgJHZhbHVlKSkKCQkJewoJCQkJcmV0dXJuIHNlbGY6OnJlbmRlcl92YXIoJHZhbHVlLCAkc2FmZSk7CgkJCX0KCQkJZWxzZQoJCQl7CgkJCQl0aHJvdyBuZXcgU2ltcGxlVGVtcGxhdGVfU3ludGF4RXJyb3IoJ0ludmFsaWQgdmFsdWUgc3ludGF4OiAnIC4gJHZhbHVlKTsKCQkJfQoJCX0KCQkKCQlwcml2YXRlIHN0YXRpYyBmdW5jdGlvbiBpc192YWx1ZSgkdmFsdWUpewoJCQlyZXR1cm4gc3RybGVuKCR2YWx1ZSkgJiYgJHZhbHVlWzBdICE9PSAnJCcgJiYgJHZhbHVlWzBdICE9PSAnKCc7CgkJfQoJCQoJCXByaXZhdGUgZnVuY3Rpb24gZm9ybWF0X2NvZGUoJGNvZGUsICR0YWJzLCAkc2tpcF9maXJzdCA9IGZhbHNlLCAkc2tpcF9sYXN0ID0gZmFsc2UpewoJCQkkbGluZXMgPSBwcmVnX3NwbGl0KCJAKD86XHI/XG58XHIpK0AiLCAkY29kZSk7CgkJCSRoZXJlZG9jX2Nsb3NpbmcgPSBzZWxmOjokdmFyX25hbWUgLiAkdGhpcy0+dXVpZDsKCQkJCgkJCSRyZXR1cm4gPSAkc2tpcF9maXJzdCA/IGFycmF5X3NoaWZ0KCRsaW5lcykgOiAnJzsKCQkJJGxhc3QgPSAkc2tpcF9sYXN0ID8gUEhQX0VPTCAuIGFycmF5X3BvcCgkbGluZXMpOiAnJzsKCQkJCgkJCWZvcmVhY2goJGxpbmVzIGFzICRsaW5lKQoJCQl7CgkJCQlpZigkcmV0dXJuKQoJCQkJewoJCQkJCSRyZXR1cm4gLj0gUEhQX0VPTDsKCQkJCX0KCQkJCQoJCQkJaWYoJGxpbmUgPT09ICRoZXJlZG9jX2Nsb3NpbmcpCgkJCQl7CgkJCQkJJHJldHVybiAuPSAkaGVyZWRvY19jbG9zaW5nOwoJCQkJfQoJCQkJZWxzZQoJCQkJewoJCQkJCSRyZXR1cm4gLj0gKAoJCQkJCQlwcmVnX21hdGNoKCdAXlxzKlwpKzs/XHMqJEAnLCAkbGluZSkKCQkJCQkJCT8gc3Vic3RyKCR0YWJzLCAxKQoJCQkJCQkJOiAkdGFicwoJCQkJCSkuIGx0cmltKCRsaW5lKTsKCQkJCX0KCQkJfQoJCQkKCQkJcmV0dXJuICRyZXR1cm4gLiAkbGFzdDsKCQl9CgkJCgkJcHJpdmF0ZSBmdW5jdGlvbiBjb21waWxlKCRzdHIpewoJCQkkVVVJRCA9ICR0aGlzLT51dWlkOwoJCQkKCQkJJGJyYWNrZXRzID0gMDsKCQkJJHRhYnMgPSAnJzsKCQkJCgkJCSRyZXBsYWNlbWVudCA9IGFycmF5KAoJCQkJJy8nID0+IGZ1bmN0aW9uKCRkYXRhKXVzZSgmJHJlcGxhY2VtZW50LCAmJGJyYWNrZXRzLCAmJHRhYnMpewoJCQkJCWlmKCRicmFja2V0cyA+IDApCgkJCQkJewoJCQkJCQktLSRicmFja2V0czsKCQkJCQkJCgkJCQkJCXJldHVybiAkdGFicyAuICd9Oyc7CgkJCQkJfQoJCQkJCWVsc2UKCQkJCQl7CgkJCQkJCXJldHVybiAkdGFicyAuICc7JzsKCQkJCQl9CgkJCQl9LAoJCQkJJy8vJyA9PiBmdW5jdGlvbigpewoJCQkJCXJldHVybiAnJzsKCQkJCX0sCgkJCQknZWNobycgPT4gZnVuY3Rpb24oJGRhdGEpdXNlKCYkcmVwbGFjZW1lbnQsICYkYnJhY2tldHMsICYkdGFicyl7CgkJCQkJcHJlZ19tYXRjaCgnQF4oPzpzZXBhcmF0b3JccysoPzxzZXBhcmF0b3I+JyAuIHNlbGY6OiRyZWdleFsndmFyX3ZhbHVlJ10gLiAnKVxzKyk/KD88ZGF0YT4uKikkQCcsICRkYXRhLCAkYml0cyk7CgkJCQkJCgkJCQkJJHNlcGFyYXRvciA9ICRiaXRzWydzZXBhcmF0b3InXSA/IHNlbGY6OnBhcnNlX3ZhbHVlKCRiaXRzWydzZXBhcmF0b3InXSk6ICdcJ1wnJzsKCQkJCQkKCQkJCQlyZXR1cm4gJHRhYnMgLiAnZWNobyBpbXBsb2RlKCcgLiAkc2VwYXJhdG9yIC4gJywgJEZOW1wnYXJyYXlfZmxhdFwnXSgnIC4gaW1wbG9kZSgnLCAnLCBzZWxmOjpwYXJzZV92YWx1ZXMoJGJpdHNbJ2RhdGEnXSkpIC4gJykpOyc7CgkJCQl9LAoJCQkJJ2VjaG9sJyA9PiBmdW5jdGlvbigkZGF0YSl1c2UoJiRyZXBsYWNlbWVudCwgJiRicmFja2V0cywgJiR0YWJzKXsKCQkJCQlyZXR1cm4gJHJlcGxhY2VtZW50WydlY2hvJ10oJGRhdGEpIC4gJ2VjaG8gUEhQX0VPTDsnOwoJCQkJfSwKCQkJCSdlY2hvaicgPT4gZnVuY3Rpb24oJGRhdGEpdXNlKCYkcmVwbGFjZW1lbnQsICYkYnJhY2tldHMsICYkdGFicyl7CgkJCQkJcmV0dXJuICRyZXBsYWNlbWVudFsnZWNobyddKCdzZXBhcmF0b3IgJyAuICRkYXRhKTsKCQkJCX0sCgkJCQknZWNob2psJyA9PiBmdW5jdGlvbigkZGF0YSl1c2UoJiRyZXBsYWNlbWVudCwgJiRicmFja2V0cywgJiR0YWJzKXsKCQkJCQlyZXR1cm4gJHJlcGxhY2VtZW50WydlY2hvbCddKCdzZXBhcmF0b3IgJyAuICRkYXRhKTsKCQkJCX0sCgkJCQknZWNob2YnID0+IGZ1bmN0aW9uKCRkYXRhKXVzZSgmJHJlcGxhY2VtZW50LCAmJGJyYWNrZXRzLCAmJHRhYnMpewoJCQkJCXJldHVybiAkcmVwbGFjZW1lbnRbJ2VjaG9sJ10oJ3NlcGFyYXRvciAnIC4gJGRhdGEpOwoJCQkJfSwKCQkJCSdwcmludCcgPT4gZnVuY3Rpb24oJGRhdGEpdXNlKCYkcmVwbGFjZW1lbnQsICYkYnJhY2tldHMsICYkdGFicyl7CgkJCQkJcmV0dXJuICRyZXBsYWNlbWVudFsnY2FsbCddKChzdHJwb3MoJ2ludG8nLCAkZGF0YSk9PT0wPyAncycgOiAnJykgLiAncHJpbnRmICcgLiAkZGF0YSk7CgkJCQl9LAoJCQkJJ2lmJyA9PiBmdW5jdGlvbigkZGF0YSl1c2UoJiRyZXBsYWNlbWVudCwgJiRicmFja2V0cywgJiR0YWJzKXsKCQkJCQkrKyRicmFja2V0czsKCQkJCQkKCQkJCQlyZXR1cm4gJHRhYnMgLiAnaWYoJyAuIHNlbGY6OnBhcnNlX2Jvb2xlYW4oJGRhdGEpIC4gJykgeyc7CgkJCQl9LAoJCQkJJ2Vsc2UnID0+IGZ1bmN0aW9uKCRkYXRhKXVzZSgmJHJlcGxhY2VtZW50LCAmJGJyYWNrZXRzLCAmJHRhYnMpewoJCQkJCXByZWdfbWF0Y2goJ0AoPzxpZj5pZikoPzxkYXRhPi4qKUAnLCAkZGF0YSwgJGJpdHMpOwoJCQkJCQoJCQkJCSRyZXR1cm4gPSBzdWJzdHIoJHRhYnMsIDEpIC4gJ30gZWxzZSc7CgkJCQkJCgkJCQkJaWYoaXNzZXQoJGJpdHNbJ2lmJ10pICYmIGlzc2V0KCRiaXRzWydkYXRhJ10pKQoJCQkJCXsKCQkJCQkJLS0kYnJhY2tldHM7CgkJCQkJCgkJCQkJCSRyZXR1cm4gLj0gbHRyaW0oJHJlcGxhY2VtZW50WydpZiddKHRyaW0oJGJpdHNbJ2RhdGEnXSkpKTsKCQkJCQl9CgkJCQkJZWxzZQoJCQkJCXsKCQkJCQkJJHJldHVybiAuPSAnIHsnOwoJCQkJCX0KCQkJCQkKCQkJCQlyZXR1cm4gJHJldHVybjsKCQkJCX0sCgkJCQknZWFjaCcgPT4gZnVuY3Rpb24oJGRhdGEpdXNlKCYkcmVwbGFjZW1lbnQsICYkYnJhY2tldHMsICYkdGFicyl7CgkJCQkJKyskYnJhY2tldHM7CgkJCQkJCgkJCQkJcHJlZ19tYXRjaCgnQF4oPzx2YXI+JyAuIHNlbGY6OiRyZWdleFsndmFyJ10gLiAnKVxzKig/OmFzXHMqKD88YXM+JyAuIHNlbGY6OiRyZWdleFsndmFyJ10gLiAnKSg/OlxzKmtleVxzKig/PGtleT4nIC4gc2VsZjo6JHJlZ2V4Wyd2YXInXSAuICcpXHMqKT8pPyRAJywgJGRhdGEsICRiaXRzKTsKCQkJCQkKCQkJCQlzdGF0aWMgJGNvdW50ID0gMDsKCQkJCQkKCQkJCQkkdmFyX25hbWUgPSBzZWxmOjokdmFyX25hbWU7CgkJCQkJJHRtcF9uYW1lID0gJ3RtcF8nIC4gKCsrJGNvdW50KSAuICdfJzsKCQkJCQkKCQkJCQkkdmFyc192YXIgPSBzZWxmOjpyZW5kZXJfdmFyKCRiaXRzWyd2YXInXSwgZmFsc2UpOwoJCQkJCSR2YXJzX2FzID0gc2VsZjo6cmVuZGVyX3Zhcihpc3NldCgkYml0c1snYXMnXSkgPyAkYml0c1snYXMnXSA6ICcnLCBmYWxzZSk7CgkJCQkJJHZhcnNfa2V5ID0gc2VsZjo6cmVuZGVyX3Zhcihpc3NldCgkYml0c1sna2V5J10pID8gJGJpdHNbJ2FzJ10gOiAnX18nLCBmYWxzZSk7CgkJCQkJCgkJCQkJcmV0dXJuIDw8PFBIUAp7JHRhYnN9Ly8gbG9vcCB2YXJpYWJsZXMKeyR0YWJzfVwkeyR0bXBfbmFtZX12YWwgPSBpc3NldCh7JHZhcnNfdmFyfSkgPyBcJHskdG1wX25hbWV9dmFsID0gJnskdmFyc192YXJ9IDogbnVsbDsKeyR0YWJzfVwkeyR0bXBfbmFtZX1rZXlzID0gZ2V0dHlwZSh7JHZhcnNfdmFyfSkgPT0gJ2FycmF5Jwp7JHRhYnN9CT8gYXJyYXlfa2V5cyh7JHZhcnNfdmFyfSkKeyR0YWJzfQk6IGFycmF5X2tleXMocmFuZ2UoMCwgc3RybGVuKFwkeyR0bXBfbmFtZX12YWwgPSBcJHskdG1wX25hbWV9dmFsIC4gJycpIC0gMSkpOwp7JHRhYnN9XCR7JHRtcF9uYW1lfWtleV9sYXN0ID0gZW5kKFwkeyR0bXBfbmFtZX1rZXlzKTsKeyR0YWJzfQp7JHRhYnN9Ly8gbG9vcAp7JHRhYnN9Zm9yZWFjaChcJHskdG1wX25hbWV9a2V5cyBhcyBcJHskdG1wX25hbWV9aW5kZXggPT4gXCR7JHRtcF9uYW1lfWtleSl7CnskdGFic30JXCR7JHZhcl9uYW1lfVsnbG9vcCddID0gYXJyYXkoCnskdGFic30JCSdpbmRleCcgPT4gXCR7JHRtcF9uYW1lfWluZGV4LAp7JHRhYnN9CQknaScgPT4gXCR7JHRtcF9uYW1lfWluZGV4LAp7JHRhYnN9CQkna2V5JyA9PiBcJHskdG1wX25hbWV9a2V5LAp7JHRhYnN9CQknaycgPT4gXCR7JHRtcF9uYW1lfWtleSwKeyR0YWJzfQkJJ3ZhbHVlJyA9PiBcJHskdG1wX25hbWV9dmFsW1wkeyR0bXBfbmFtZX1rZXldLAp7JHRhYnN9CQkndicgPT4gXCR7JHRtcF9uYW1lfXZhbFtcJHskdG1wX25hbWV9a2V5XSwKeyR0YWJzfQkJJ2ZpcnN0JyA9PiBcJHskdG1wX25hbWV9a2V5ID09PSBcJHskdG1wX25hbWV9a2V5c1swXSwKeyR0YWJzfQkJJ2xhc3QnID0+IFwkeyR0bXBfbmFtZX1rZXkgPT09IFwkeyR0bXBfbmFtZX1rZXlfbGFzdCwKeyR0YWJzfQkpOwp7JHRhYnN9CXskdmFyc19rZXl9ID0gXCR7JHRtcF9uYW1lfWtleTsKeyR0YWJzfQl7JHZhcnNfYXN9ID0gXCR7JHRtcF9uYW1lfXZhbFtcJHskdG1wX25hbWV9a2V5XTsKUEhQOwoJCQkJfSwKCQkJCSd3aGlsZScgPT4gZnVuY3Rpb24oJGRhdGEpdXNlKCYkcmVwbGFjZW1lbnQsICYkYnJhY2tldHMsICYkdGFicyl7CgkJCQkJKyskYnJhY2tldHM7CgkJCQkJCgkJCQkJcmV0dXJuICR0YWJzIC4gJ3doaWxlKCcgLiBzZWxmOjpwYXJzZV9ib29sZWFuKCRkYXRhKSAuICcpeyc7CgkJCQl9LAoJCQkJJ2ZvcicgPT4gZnVuY3Rpb24oJGRhdGEpdXNlKCYkcmVwbGFjZW1lbnQsICYkYnJhY2tldHMsICYkdGFicyl7CgkJCQkJKyskYnJhY2tldHM7CgkJCQkJCgkJCQkJcmV0dXJuIHByZWdfcmVwbGFjZV9jYWxsYmFjaygKCQkJCQkJJ0AoPzx2YXI+JyAuIHNlbGY6OiRyZWdleFsndmFyJ10gLiAnKT9ccyooPzpmcm9tXHMqKD88c3RhcnQ+JyAuIHNlbGY6OiRyZWdleFsndmFyX3ZhbHVlJ10gLiAnKSk/KD86XHMqdG9ccyooPzxlbmQ+JyAuIHNlbGY6OiRyZWdleFsndmFyX3ZhbHVlJ10gLiAnKSkoPzpccypzdGVwXHMqKD88c3RlcD4nIC4gc2VsZjo6JHJlZ2V4Wyd2YXJfdmFsdWUnXSAuICcpKT9AJywKCQkJCQkJZnVuY3Rpb24oJG1hdGNoZXMpdXNlKCYkcmVwbGFjZW1lbnQsICYkYnJhY2tldHMsICYkdGFicyl7CgkJCQkJCQoJCQkJCQkJJHZhbHVlcyA9IGFycmF5KAoJCQkJCQkJCSdzdGFydCcgPT4gaXNzZXQoJG1hdGNoZXNbJ3N0YXJ0J10pICYmICRtYXRjaGVzWydzdGFydCddICE9PSAnJyA/IHNlbGY6OnBhcnNlX3ZhbHVlKCRtYXRjaGVzWydzdGFydCddKSA6ICcwJywKCQkJCQkJCQknZW5kJyA9PiBpc3NldCgkbWF0Y2hlc1snZW5kJ10pID8gc2VsZjo6cGFyc2VfdmFsdWUoJG1hdGNoZXNbJ2VuZCddKSA6IHNlbGY6OnBhcnNlX3ZhbHVlKCRtYXRjaGVzWydzdGFydCddKSwKCQkJCQkJCQknc3RlcCcgPT4gaXNzZXQoJG1hdGNoZXNbJ3N0ZXAnXSkgPyBzZWxmOjpwYXJzZV92YWx1ZSgkbWF0Y2hlc1snc3RlcCddKSA6ICcxJwoJCQkJCQkJKTsKCQkJCQkJCQoJCQkJCQkJJHJldHVybiA9ICR0YWJzIC4gJ2ZvcmVhY2goJzsKCQkJCQkJCQoJCQkJCQkJaWYoc2VsZjo6aXNfdmFsdWUoJHZhbHVlc1snc3RhcnQnXSkgJiYgc2VsZjo6aXNfdmFsdWUoJHZhbHVlc1snZW5kJ10pICYmIHNlbGY6OmlzX3ZhbHVlKCR2YWx1ZXNbJ3N0ZXAnXSkpCgkJCQkJCQl7CgkJCQkJCQkJaWYoJHRoaXMtPm9wdGlvbnNbJ29wdGltaXplJ10pCgkJCQkJCQkJewoJCQkJCQkJCQkkcmV0dXJuID0gInskdGFic30vLyB+IG9wdGltaXphdGlvbiBlbmFibGVkIH4gaW5saW5pbmcgdGhlIHJlc3VsdHNcclxueyRyZXR1cm59IiAuIHNlbGY6OmZvcm1hdF9jb2RlKAoJCQkJCQkJCQkJdmFyX2V4cG9ydCgKCQkJCQkJCQkJCQlyYW5nZSgKCQkJCQkJCQkJCQkJcHJlZ19yZXBsYWNlKCdAXiJ8IiRAJywgJycsICR2YWx1ZXNbJ3N0YXJ0J10pLAoJCQkJCQkJCQkJCQlwcmVnX3JlcGxhY2UoJ0BeInwiJEAnLCAnJywgJHZhbHVlc1snZW5kJ10pLAoJCQkJCQkJCQkJCQlhYnMoJHZhbHVlc1snc3RlcCddKQoJCQkJCQkJCQkJCSksCgkJCQkJCQkJCQkJdHJ1ZQoJCQkJCQkJCQkJKSwKCQkJCQkJCQkJCSR0YWJzIC4gIlx0IiwKCQkJCQkJCQkJCXRydWUKCQkJCQkJCQkJKTsKCQkJCQkJCQl9CgkJCQkJCQkJZWxzZQoJCQkJCQkJCXsKCQkJCQkJCQkJJHJldHVybiA9ICJ7JHRhYnN9Ly8gfiBvcHRpbWl6YXRpb24gRElTQUJMRUQgfiByZXN1bHRzIGNvdWxkIGJlIGlubGluZWRcclxueyRyZXR1cm59cmFuZ2UoeyR2YWx1ZXNbJ3N0YXJ0J119LCB7JHZhbHVlc1snZW5kJ119LCBhYnMoeyR2YWx1ZXNbJ3N0ZXAnXX0pKSI7CgkJCQkJCQkJfQoJCQkJCQkJfQoJCQkJCQkJZWxzZQoJCQkJCQkJewoJCQkJCQkJCSRyZXR1cm4gLj0gJ3JhbmdlKCcgLiAkdmFsdWVzWydzdGFydCddIC4gJywgJyAuICR2YWx1ZXNbJ2VuZCddIC4gJywgYWJzKCcgLiAkdmFsdWVzWydzdGVwJ10gLiAnKSknOwoJCQkJCQkJfQoJCQkJCQkJCgkJCQkJCQlyZXR1cm4gJHJldHVybiAuICcgYXMgJyAuIHNlbGY6OnJlbmRlcl92YXIoaXNzZXQoJG1hdGNoZXNbJ3ZhciddKSA/ICRtYXRjaGVzWyd2YXInXSA6ICcnLCBmYWxzZSkgLiAnKXsnOwoJCQkJCQl9LAoJCQkJCQkkZGF0YQoJCQkJCSk7CgkJCQl9LAoJCQkJJ2RvJyA9PiBmdW5jdGlvbigkZGF0YSl1c2UoJiRyZXBsYWNlbWVudCwgJiRicmFja2V0cywgJiR0YWJzKXsKCQkJCQkrKyRicmFja2V0czsKCQkJCQkKCQkJCQlyZXR1cm4gJHRhYnMgLiAnZG97JzsKCQkJCX0sCgkJCQkndW50aWwnID0+IGZ1bmN0aW9uKCRkYXRhKXVzZSgmJHJlcGxhY2VtZW50LCAmJGJyYWNrZXRzLCAmJHRhYnMpewoJCQkJCS0tJGJyYWNrZXRzOwoJCQkJCQoJCQkJCXJldHVybiBzdWJzdHIoJHRhYnMsIDEpIC4gJ313aGlsZSghKCcgLiBzZWxmOjpwYXJzZV9ib29sZWFuKCRkYXRhKSAuICcpKTsnOwoJCQkJfSwKCQkJCSdzZXQnID0+IGZ1bmN0aW9uKCRkYXRhKXVzZSgmJHJlcGxhY2VtZW50LCAmJGJyYWNrZXRzLCAmJHRhYnMpewoJCQkJCXByZWdfbWF0Y2goJ0BeXHMqKD88b3A+W1wrXC1cKlxcXC9cJV0pP1xzKig/PHZhcj4nIC4gc2VsZjo6JHJlZ2V4Wyd2YXInXSAuICcpXHMqKD86KD88b3BfdmFsPicgLiBzZWxmOjokcmVnZXhbJ3Zhcl92YWx1ZSddIC4gJylccyk/XHMqKD88dmFsdWVzPi4qKSRAJywgJGRhdGEsICRiaXRzKTsKCQkJCQkKCQkJCQkkdmFsdWVzID0gc2VsZjo6cGFyc2VfdmFsdWVzKCRiaXRzWyd2YWx1ZXMnXSk7CgkJCQkJJGNvdW50ID0gY291bnQoJHZhbHVlcyk7CgkJCQkJCgkJCQkJJHJldHVybiA9ICR0YWJzIC4gc2VsZjo6cmVuZGVyX3ZhcigkYml0c1sndmFyJ10sIGZhbHNlKSAuICcgPSAnOwoJCQkJCQoJCQkJCSRjbG9zZSA9IDA7CgkJCQkJCgkJCQkJaWYoaXNzZXQoJGJpdHNbJ29wJ10pKQoJCQkJCXsKCQkJCQkJc3dpdGNoKCRiaXRzWydvcCddKQoJCQkJCQl7CgkJCQkJCQljYXNlICctJzoKCQkJCQkJCQkkcmV0dXJuIC49IDw8PFBIUApjYWxsX3VzZXJfZnVuY19hcnJheShmdW5jdGlvbigpewp7JHRhYnN9CVwkYXJncyA9IGZ1bmNfZ2V0X2FyZ3MoKTsKeyR0YWJzfQlcJGluaXRpYWwgPSBhcnJheV9zaGlmdChcJGFyZ3MpOwp7JHRhYnN9CXJldHVybiBhcnJheV9yZWR1Y2UoXCRhcmdzLCBmdW5jdGlvbihcJGNhcnJ5LCBcJHZhbHVlKXsKeyR0YWJzfQkJcmV0dXJuIFwkY2FycnkgLSBcJHZhbHVlOwp7JHRhYnN9CX0sIFwkaW5pdGlhbCk7CnskdGFic319LCBcJEZOWydhcnJheV9mbGF0J10oClBIUDsKCQkJCQkJCQkkY2xvc2UgPSAyOwoJCQkJCQkJCWJyZWFrOwoJCQkJCQkJY2FzZSAnKyc6CgkJCQkJCQkJJHJldHVybiAuPSAnYXJyYXlfc3VtKCRGTltcJ2FycmF5X2ZsYXRcJ10oJzsKCQkJCQkJCQkkY2xvc2UgPSAyOwoJCQkJCQkJCWJyZWFrOwoJCQkJCQkJY2FzZSAnKic6CgkJCQkJCQkJJHJldHVybiAuPSAnYXJyYXlfcHJvZHVjdCgkRk5bXCdhcnJheV9mbGF0XCddKCc7CgkJCQkJCQkJJGNsb3NlID0gMjsKCQkJCQkJCQlicmVhazsKCQkJCQkJCWNhc2UgJ1xcJzoKCQkJCQkJCWNhc2UgJy8nOgoJCQkJCQkJY2FzZSAnJSc6CgkJCQkJCQkJJG9wcyA9IGFycmF5KAoJCQkJCQkJCQknXFwnID0+ICdyb3VuZCglcyAvICR2YWx1ZSknLAoJCQkJCQkJCQknLycgPT4gJyglcyAvICR2YWx1ZSknLAoJCQkJCQkJCQknJScgPT4gJyglcyAlJSAkdmFsdWUpJwoJCQkJCQkJCSk7CgkJCQkJCQkJCgkJCQkJCQkJJHJldHVybiAuPSAnYXJyYXlfbWFwKGZ1bmN0aW9uKCR2YWx1ZSl1c2UoJiQnIC4gc2VsZjo6JHZhcl9uYW1lIC4gJyl7JwoJCQkJCQkJCQkuJ3JldHVybiAnIC4gc3ByaW50ZigKCQkJCQkJCQkJCSRvcHNbJGJpdHNbJ29wJ11dLAoJCQkJCQkJCQkJaXNzZXQoJGJpdHNbJ29wX3ZhbCddKQoJCQkJCQkJCQkJCT8gc2VsZjo6cGFyc2VfdmFsdWUoJGJpdHNbJ29wX3ZhbCddKQoJCQkJCQkJCQkJCTogc2VsZjo6cmVuZGVyX3ZhcigkYml0c1sndmFyJ10sIGZhbHNlKQoJCQkJCQkJCQkpCgkJCQkJCQkJCS4gJzt9LCAkRk5bXCdhcnJheV9mbGF0XCddKCc7CgkJCQkJCQkJJGNsb3NlID0gMjsKCQkJCQkJCQlicmVhazsKCQkJCQkJfQoJCQkJCX0KCQkJCQkKCQkJCQlpZigkY291bnQgPiAxKQoJCQkJCXsKCQkJCQkJJHJldHVybiAuPSAnYXJyYXkoJyAuIGltcGxvZGUoJywnLCAkdmFsdWVzKSAuICcpJzsKCQkJCQl9CgkJCQkJZWxzZQoJCQkJCXsKCQkJCQkJJHJldHVybiAuPSAoJGNvdW50ICYmIHN0cmxlbigkdmFsdWVzWzBdKSA/ICR2YWx1ZXNbMF0gOiAnbnVsbCcpOwoJCQkJCX0KCQkJCQkKCQkJCQlyZXR1cm4gJHJldHVybiAuIHN0cl9yZXBlYXQoJyknLCAkY2xvc2UpIC4gJzsnOwoJCQkJfSwKCQkJCSd1bnNldCcgPT4gZnVuY3Rpb24oJGRhdGEpdXNlKCYkcmVwbGFjZW1lbnQsICYkYnJhY2tldHMsICYkdGFicyl7CgkJCQkJJHZhbHVlcyA9IHNlbGY6OnBhcnNlX3ZhbHVlcygkZGF0YSwgbnVsbCwgZmFsc2UpOwoJCQkJCSR2YXJzID0gYXJyYXlfZmlsdGVyKCR2YWx1ZXMsIGZ1bmN0aW9uKCR2YXIpewoJCQkJCQlyZXR1cm4gIXNlbGY6OmlzX3ZhbHVlKCR2YXIpOwoJCQkJCX0pOwoJCQkJCQoJCQkJCSRyZXR1cm4gPSAkdGFicyAuICgKCQkJCQkJY291bnQoJHZhbHVlcykgIT09IGNvdW50KCR2YXJzKQoJCQkJCQkJPyAnLy8gV2FybmluZzogaW52YWxpZCB2YWx1ZXMgd2VyZSBwYXNzZWQnIC4gUEhQX0VPTCAuICR0YWJzCgkJCQkJCQk6ICcnCgkJCQkJKTsKCQkJCQkKCQkJCQlyZXR1cm4gJHJldHVybiAuICgKCQkJCQkJJHZhcnMKCQkJCQkJCT8gJ3Vuc2V0KCcgLiBpbXBsb2RlKCcsJywgJHZhcnMpIC4gJyk7JwoJCQkJCQkJOiAnLy8gV2FybmluZzogbm8gdmFsdWVzIHdlcmUgcGFzc2VkIG9yIGFsbCB3ZXJlIGZpbHRlcmVkIG91dCcKCQkJCQkpOwoJCQkJfSwKCQkJCSdnbG9iYWwnID0+IGZ1bmN0aW9uKCRkYXRhKXVzZSgmJHJlcGxhY2VtZW50LCAmJGJyYWNrZXRzLCAmJHRhYnMpewoJCQkJCSRkYXRhID0gc2VsZjo6c3BsaXRfdmFsdWVzKCRkYXRhLCAnICcpOwoJCQkJCQoJCQkJCXJldHVybiAkdGFicyAuIHNlbGY6OnJlbmRlcl92YXIoYXJyYXlfc2hpZnQoJGRhdGEpLCBmYWxzZSkgLiAnID0gJEdMT0JBTFNbXCcnIC4gam9pbignXCddW1wnJywgJGRhdGEpIC4gJ1wnXTsnOwoJCQkJfSwKCQkJCSdjYWxsJyA9PiBmdW5jdGlvbigkZGF0YSl1c2UoJiRyZXBsYWNlbWVudCwgJiRicmFja2V0cywgJiR0YWJzKXsKCQkJCQlwcmVnX21hdGNoKCdAXlxzKig/PGZuPicgLiBzZWxmOjokcmVnZXhbJ3ZhciddIC4gJylccyooPzppbnRvXHMqKD88aW50bz4nIC4gc2VsZjo6JHJlZ2V4Wyd2YXInXSAuICcpXHMqKT8oPzxhcmdzPi4qPykkQCcsICRkYXRhLCAkYml0cyk7CgkJCQkJCgkJCQkJJHZhciA9IHNlbGY6OnJlbmRlcl92YXIoJGJpdHNbJ2ZuJ10sIGZhbHNlKTsKCQkJCQkKCQkJCQlyZXR1cm4gJHRhYnMgLiAoJGJpdHNbJ2ludG8nXSA/IHNlbGY6OnJlbmRlcl92YXIoJGJpdHNbJ2ludG8nXSwgZmFsc2UpIC4gJyA9ICcgOiAnJykKCQkJCQkJLiAnY2FsbF91c2VyX2Z1bmNfYXJyYXkoJwoJCQkJCQkJLiAnaXNzZXQoJyAuICR2YXIgLiAnKSAmJiBpc19jYWxsYWJsZSgnIC4gJHZhciAuICcpJwoJCQkJCQkJCS4gJz8gJyAuICR2YXIKCQkJCQkJCQkuICc6IChpc3NldCgkRk5bIicgLiAkYml0c1snZm4nXSAuICciXSknCgkJCQkJCQkJCS4gJz8gJEZOWyInIC4gJGJpdHNbJ2ZuJ10gLiAnIl0nCgkJCQkJCQkJCS4nOiAiJyAuIHN0cl9yZXBsYWNlKCcuJywgJ18nLCAkYml0c1snZm4nXSkgLiAnIicKCQkJCQkJCQkuICcpLCAnCgkJCQkJCQkuICdhcnJheSgnIC4gaW1wbG9kZSgnLCcsIHNlbGY6OnBhcnNlX3ZhbHVlcygkYml0c1snYXJncyddKSkgLiAnKSk7JzsKCQkJCX0sCgkJCQkncGhwJyA9PiBmdW5jdGlvbigkZGF0YSl1c2UoJiRyZXBsYWNlbWVudCwgJiRicmFja2V0cywgJiR0YWJzKXsKCQkJCQlyZXR1cm4gJHRhYnMgLiAnY2FsbF91c2VyX2Z1bmNfYXJyYXkoZnVuY3Rpb24oJEZOLCAmJCcgLiBzZWxmOjokdmFyX25hbWUgLiAnKXsnIC4gUEhQX0VPTAoJCQkJCQkgICAuICJ7JHRhYnN9XHR7JGRhdGF9OyIgLiBQSFBfRU9MCgkJCQkJCSAgIC4gJHRhYnMgLiAnfSwgYXJyYXkoJEZOLCAmJCcgLiBzZWxmOjokdmFyX25hbWUgLiAnKSk7JzsKCQkJCX0sCgkJCQkncmV0dXJuJyA9PiBmdW5jdGlvbigkZGF0YSl1c2UoJiRyZXBsYWNlbWVudCwgJiRicmFja2V0cywgJiR0YWJzKXsKCQkJCQlyZXR1cm4gJHRhYnMgLiAncmV0dXJuICcgLiAoJGRhdGEgPyBzZWxmOjpwYXJzZV92YWx1ZSgkZGF0YSk6ICcnKS4nOyc7CgkJCQl9LAoJCQkJJ2luYycgPT4gZnVuY3Rpb24oJGRhdGEpdXNlKCYkcmVwbGFjZW1lbnQsICYkYnJhY2tldHMsICYkdGFicyl7CgkJCQkJcHJlZ19tYXRjaCgnQF4oPzpccypieVxzKig/PGJ5PicgLiBzZWxmOjokcmVnZXhbJ3Zhcl92YWx1ZSddIC4gJylccyopPyg/PHZhbHVlcz4uKj8pJEAnLCAkZGF0YSwgJGJpdHMpOwoJCQkJCSR2YWx1ZXMgPSBzZWxmOjpwYXJzZV92YWx1ZXMoJGJpdHNbJ3ZhbHVlcyddLCAnXHMqLFxzKicsIGZhbHNlKTsKCQkJCQkkaW5jID0gaXNzZXQoJGJpdHNbJ2J5J10pICYmICRiaXRzWydieSddICE9PSAnJyA/IHNlbGY6OnBhcnNlX3ZhbHVlKCRiaXRzWydieSddKTogJzEnOwoJCQkJCQoJCQkJCSRyZXR1cm4gPSAnJzsKCQkJCQkKCQkJCQlpZighJGluYyB8fCAkaW5jID09PSAnIjAiJyB8fCAkaW5jID09PSAnbnVsbCcgfHwgJGluYyA9PT0gJ2ZhbHNlJykKCQkJCQl7CgkJCQkJCWlmKCR0aGlzLT5vcHRpb25zWydvcHRpbWl6ZSddKQoJCQkJCQl7CgkJCQkJCQlyZXR1cm4gInskdGFic30vLyB+IG9wdGltaXphdGlvbiBlbmFibGVkIH4gaW5jcmVtZW50IGJ5IHskaW5jfSByZW1vdmVkIjsKCQkJCQkJfQoJCQkJCQllbHNlCgkJCQkJCXsKCQkJCQkJCSRyZXR1cm4gLj0gInskdGFic30vLyB+IG9wdGltaXphdGlvbiBESVNBQkxFRCB+IGluY3JlbWVudCBieSB7JGluY30gY291bGQgYmUgcmVtb3ZlZCIgLiBQSFBfRU9MOwoJCQkJCQl9CgkJCQkJfQoJCQkJCQoJCQkJCSR2YXJfbmFtZSA9IHNlbGY6OiR2YXJfbmFtZTsKCQkJCQkKCQkJCQlmb3JlYWNoKCR2YWx1ZXMgYXMgJHZhbHVlKQoJCQkJCXsKCQkJCQkJaWYoIWlzc2V0KCR2YWx1ZVswXSkgJiYgIXNlbGY6OmlzX3ZhbHVlKCR2YWx1ZVswXSkpCgkJCQkJCXsKCQkJCQkJCWNvbnRpbnVlOwoJCQkJCQl9CgkJCQkJCQoJCQkJCQkkcmV0dXJuIC49ICJ7JHRhYnN9eyR2YWx1ZX0gPSBcJEZOWydpbmMnXShpc3NldCh7JHZhbHVlfSk/eyR2YWx1ZX06MCwgeyRpbmN9KTsiIC4gUEhQX0VPTDsKCQkJCQl9CgkJCQkJCgkJCQkJcmV0dXJuICRyZXR1cm47CgkJCQkJCgkJCQl9LAoJCQkJJ2ZuJyA9PiBmdW5jdGlvbigkZGF0YSl1c2UoJiRyZXBsYWNlbWVudCwgJiRicmFja2V0cywgJiR0YWJzKXsKCQkJCQlpZigKCQkJCQkJcHJlZ19tYXRjaCgKCQkJCQkJCSdAXlxzKignIC4gc2VsZjo6JHJlZ2V4Wyd2YXInXSAuICcpXHMqKD86XHMrKC4qKSk/JEAnLAoJCQkJCQkJJGRhdGEsICRiaXRzCgkJCQkJCSkgPT09IGZhbHNlCgkJCQkJKQoJCQkJCXsKCQkJCQkJcmV0dXJuICcnOwoJCQkJCX0KCQkJCQkKCQkJCQkrKyRicmFja2V0czsKCQkJCQkKCQkJCQkkdmVyc2lvbiA9IFNpbXBsZVRlbXBsYXRlOjp2ZXJzaW9uKCk7CgkJCQkJJHZhcl9uYW1lID0gc2VsZjo6JHZhcl9uYW1lOwoJCQkJCQoJCQkJCSRyZXR1cm4gPSAkdGFicyAuIHNlbGY6OnJlbmRlcl92YXIoJGJpdHNbMV0sIGZhbHNlKSAuIDw8PFBIUAogPSBmdW5jdGlvbigpdXNlKCZcJEZOLCAmXCRfKXsKeyR0YWJzfQlcJHskdmFyX25hbWV9ID0gYXJyYXkoCnskdGFic30JCSdhcmd2JyA9PiBmdW5jX2dldF9hcmdzKCksCnskdGFic30JCSdhcmdjJyA9PiBmdW5jX251bV9hcmdzKCksCnskdGFic30JCSdWRVJTSU9OJyA9PiAneyR2ZXJzaW9ufScsCnskdGFic30JCSdFT0wnID0+IFBIUF9FT0wsCnskdGFic30JCSdQQVJFTlQnID0+ICZcJF8KeyR0YWJzfQkpOwp7JHRhYnN9CVwkXyA9ICZcJHskdmFyX25hbWV9OwoKUEhQOwoJCQkJCWlmKGlzc2V0KCRiaXRzWzJdKSAmJiAkYml0c1syXSkKCQkJCQl7CgkJCQkJCSRhcmdzID0gYXJyYXkoKTsKCQkJCQkJZm9yZWFjaChzZWxmOjpzcGxpdF92YWx1ZXMoJGJpdHNbMl0pIGFzICR2YWx1ZSkKCQkJCQkJewoJCQkJCQkJaWYoIXNlbGY6OmlzX3ZhbHVlKHNlbGY6OnBhcnNlX3ZhbHVlKCR2YWx1ZSkpKQoJCQkJCQkJewoJCQkJCQkJCSRhcmdzW10gPSAkdmFsdWU7CgkJCQkJCQl9CgkJCQkJCX0KCQkJCQkJCgkJCQkJCWZvcmVhY2goJGFyZ3MgYXMgJGsgPT4gJGFyZykKCQkJCQkJewoJCQkJCQkJJHJldHVybiAuPSAieyR0YWJzfQlcJHskdmFyX25hbWV9W1wieyRhcmd9XCJdID0gJlwkeyR2YXJfbmFtZX1bXCJhcmd2XCJdW3ska31dOyIgLiBQSFBfRU9MOwoJCQkJCQl9CgkJCQkJfQoJCQkJCQoJCQkJCXJldHVybiAkcmV0dXJuOwoJCQkJfSwKCQkJCSdldmFsJyA9PiBmdW5jdGlvbigkZGF0YSl1c2UoJiRyZXBsYWNlbWVudCwgJiRicmFja2V0cywgJiR0YWJzKXsKCQkJCQkkcmV0dXJuID0gJyc7CgkJCQkJJHZhbHVlID0gc2VsZjo6cGFyc2VfdmFsdWUoJGRhdGEpOwoJCQkJCQoJCQkJCWlmKCR0aGlzLT5vcHRpb25zWydvcHRpbWl6ZSddICYmIHNlbGY6OmlzX3ZhbHVlKCR2YWx1ZSkpCgkJCQkJewoJCQkJCQkkcmV0dXJuID0gJHRhYnMgLiAnLy8gfiBvcHRpbWl6YXRpb24gZW5hYmxlZCB+IHRyeWluZyB0byBhdm9pZCBjb21waWxpbmcgaW4gcnVudGltZScgLiBQSFBfRU9MOwoJCQkJCQkKCQkJCQkJc3RhdGljICRjYWNoZWQgPSBhcnJheSgpOwoJCQkJCQkKCQkJCQkJJHNoYTEgPSBzaGExKCR2YWx1ZSk7CgkJCQkJCQoJCQkJCQlpZihpc3NldCgkY2FjaGVkWyRzaGExXSkpCgkJCQkJCXsKCQkJCQkJCSRyZXR1cm4gLj0gJHRhYnMgLiAnLy8ge0BldmFsfSBjYWNoZWQgY29kZSBmb3VuZDogY2FjaGUgZW50cnkgJzsKCQkJCQkJfQoJCQkJCQllbHNlCgkJCQkJCXsKCQkJCQkJCSRyZXR1cm4gLj0gJHRhYnMgLiAnLy8ge0BldmFsfSBubyBjYWNoZWQgY29kZSBmb3VuZDogY3JlYXRpbmcgZW50cnkgJzsKCQkJCQkJCQoJCQkJCQkJJGNvbXBpbGVyID0gbmV3IFNpbXBsZVRlbXBsYXRlX0NvbXBpbGVyKCR0aGlzLT50ZW1wbGF0ZSwgc3RyaXBzbGFzaGVzKHRyaW0oJHZhbHVlLCAnIicpKSwgJHRoaXMtPm9wdGlvbnMpOwoJCQkJCQkJCgkJCQkJCQkkY2FjaGVkWyRzaGExXSA9IHNlbGY6OmZvcm1hdF9jb2RlKCRjb21waWxlci0+Z2V0UEhQKCkgLiAnLy8ge0BldmFsfSBlbmRlZCcsICR0YWJzKTsKCQkJCQkJCQoJCQkJCQkJdW5zZXQoJGNvbXBpbGVyKTsKCQkJCQkJfQoJCQkJCQkKCQkJCQkJJHJldHVybiAuPSAkc2hhMSAuIFBIUF9FT0wgLiAkY2FjaGVkWyRzaGExXTsKCQkJCQl9CgkJCQkJZWxzZQoJCQkJCXsKCQkJCQkJJG9wdGlvbnMgPSBzZWxmOjpmb3JtYXRfY29kZSh2YXJfZXhwb3J0KCR0aGlzLT5vcHRpb25zLCB0cnVlKSwgJHRhYnMgLiAiXHRcdCIsIHRydWUpOwoJCQkJCQkKCQkJCQkJJHJldHVybiA9IDw8PFBIUAp7JHRhYnN9Ly8gfiBvcHRpbWl6YXRpb24gRElTQUJMRUQgb3IgdW5mZWFzYWJsZSB+IGNvbXBpbGF0aW9uIGluIHJ1bnRpbWUgaXMgcmVxdWlyZWQKeyR0YWJzfWNhbGxfdXNlcl9mdW5jX2FycmF5KGZ1bmN0aW9uKCl1c2UoJlwkRk4sICZcJERBVEEpewp7JHRhYnN9CVwkY29tcGlsZXIgPSBuZXcgU2ltcGxlVGVtcGxhdGVfQ29tcGlsZXIoXCR0aGlzLCB7JHZhbHVlfSwgeyRvcHRpb25zfSk7CnskdGFic30JXCRmbiA9IFwkY29tcGlsZXItPmdldEZOKCk7CnskdGFic30JcmV0dXJuIFwkZm4oXCREQVRBKTsKeyR0YWJzfX0sIGFycmF5KCkpOwpQSFA7CgkJCQkJfQoJCQkJCQoJCQkJCXJldHVybiAkcmV0dXJuOwoJCQkJfQoJCQkpOwoJCQkKCQkJJHRyaW1fZm4gPSAkdGhpcy0+b3B0aW9uc1sndHJpbSddID8gJ3RyaW0nIDogJyc7CgkJCQoJCQkkdGhpcy0+cGhwIC49ICJcclxuZWNobyB7JHRyaW1fZm59KDw8PCciIC4gc2VsZjo6JHZhcl9uYW1lIC4gInskVVVJRH0nXHJcbiIKCQkJCS4gcHJlZ19yZXBsYWNlX2NhbGxiYWNrKAoJCQkJCS8vIGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS9hLzY0NjQ1MDAKCQkJCQknfntAKGV2YWx8ZWNob2o/bD98cHJpbnR8aWZ8ZWxzZXxmb3J8d2hpbGV8ZWFjaHxkb3x1bnRpbHwoPzp1bik/c2V0fGNhbGx8Z2xvYmFsfHBocHxyZXR1cm58aW5jfGZufC8vPykoPzpcXHMqKC4qPykpP30oPz0oPzpbXiJcXFxcXSooPzpcXFxcLnwiKD86W14iXFxcXF0qXFxcXC4pKlteIlxcXFxdKiIpKSpbXiJdKiQpficsCgkJCQkJZnVuY3Rpb24oJG1hdGNoZXMpdXNlKCYkcmVwbGFjZW1lbnQsICYkYnJhY2tldHMsICYkdGFicywgJiRVVUlELCAmJHRyaW1fZm4pewoJCQkJCQkKCQkJCQkJJHRhYnMgPSAkYnJhY2tldHMKCQkJCQkJCT8gc3RyX3JlcGVhdCgiXHQiLCAkYnJhY2tldHMgLSAoJG1hdGNoZXNbMV0gPT09ICcvJykpCgkJCQkJCQk6ICcnOwoJCQkJCQkKCQkJCQkJJHZhcl9uYW1lID0gc2VsZjo6JHZhcl9uYW1lOwoJCQkJCQkKCQkJCQkJJHBocCA9ICRyZXBsYWNlbWVudFskbWF0Y2hlc1sxXV0oaXNzZXQoJG1hdGNoZXNbMl0pID8gJG1hdGNoZXNbMl0gOiBudWxsKTsKCQkJCQkJCgkJCQkJCXJldHVybiAiXHJcbnskdmFyX25hbWV9eyRVVUlEfVxyXG4pO1xyXG57JHRhYnN9Ly8geyRtYXRjaGVzWzBdfVxyXG57JHBocH1cclxuXHJcbnskdGFic31lY2hvIHskdHJpbV9mbn0oPDw8J3skdmFyX25hbWV9eyRVVUlEfSdcclxuIjsKCQkJCQl9LAoJCQkJCSRzdHIgLiAnJwoJCQkJKQoJCQkJLiAiXHJcbiIgLiBzZWxmOjokdmFyX25hbWUgLiAieyRVVUlEfVxyXG4pO1xyXG4iOwoJCQkKCQkJJHRoaXMtPnBocCA9IHByZWdfcmVwbGFjZSgKCQkJCWFycmF5KAoJCQkJCSdAXHJcblx0KmVjaG9ccyonIC4gJHRyaW1fZm4gLiAnXCg8PDxcJycgLiBzZWxmOjokdmFyX25hbWUgLiAkVVVJRCAuICdcJyg/OlxzKlxyXG4pPycgLiBzZWxmOjokdmFyX25hbWUgLiAkVVVJRCAuICdcclxuXCk7QCcsCgkJCQkJJ0BcclxuJyAuIHNlbGY6OiR2YXJfbmFtZSAuICRVVUlEIC4gJ1xyXG5cKTsoXHJcbikqXHQqZWNob1xzKicgLiAkdHJpbV9mbiAuICdcKDw8PFwnJyAuIHNlbGY6OiR2YXJfbmFtZSAuICRVVUlEIC4gJ1wnQCcKCQkJCSksCgkJCQlhcnJheSgKCQkJCQknJywgJycKCQkJCSksCgkJCQkkdGhpcy0+cGhwCgkJCSk7CgkJCQoJCQlpZigkYnJhY2tldHMpCgkJCXsKCQkJCSR0aGlzLT5waHAgLj0gICJcclxuLy8gQVVUTy1DTE9TRVxyXG4iIC4gc3RyX3JlcGVhdCgnfTsnLCAkYnJhY2tldHMpOwoJCQl9CgkJfQoJCQoJCWZ1bmN0aW9uIGdldFBIUCgpewoJCQlyZXR1cm4gJHRoaXMtPnBocDsKCQl9CgkJCgkJZnVuY3Rpb24gZ2V0Rk4oKXsKCQkJaWYoISR0aGlzLT5mbikKCQkJewoJCQkJJHRoaXMtPmZuID0gZXZhbCgncmV0dXJuIGZ1bmN0aW9uKCYkJyAuIHNlbGY6OiR2YXJfbmFtZSAuICcpeycKCQkJCQkJLiBQSFBfRU9MCgkJCQkJCS4gc3ByaW50ZihzZWxmOjokZm5fYm9keSwgJHRoaXMtPnBocCkKCQkJCQkJLiBQSFBfRU9MCgkJCQkJLiAnfTsnCgkJCQkpOwoJCQkJCgkJCQkkdGhpcy0+Zm4gPSAkdGhpcy0+Zm4tPmJpbmRUbygkdGhpcy0+dGVtcGxhdGUpOwoJCQl9CgkJCQoJCQlyZXR1cm4gJHRoaXMtPmZuOwoJCX0KCQkKCQlmdW5jdGlvbiBfX2NvbnN0cnVjdChTaW1wbGVUZW1wbGF0ZSAkdGVtcGxhdGUsICRjb2RlLCBhcnJheSAkb3B0aW9ucyA9IGFycmF5KCkpewoJCQkkdGhpcy0+b3B0aW9ucyA9ICRvcHRpb25zOwoJCQkkdGhpcy0+dGVtcGxhdGUgPSAkdGVtcGxhdGU7CgkJCQoJCQkvLyBBTE1PU1QgdW5ndWVzc2FibGUgbmFtZSwgdG8gYXZvaWQgc3ludGF4IGVycm9ycwoJCQkkdGhpcy0+dXVpZCA9IHN0cl9zaHVmZmxlKG10X3JhbmQoKSAuIHRpbWUoKSAuIHNoYTEoJGNvZGUpKTsKCQkJCgkJCSR0aGlzLT5jb21waWxlKCRjb2RlKTsKCQl9Cgl9CgoJLy8gYmFzZSBjbGFzcwoJY2xhc3MgU2ltcGxlVGVtcGxhdGUgewoJCXByaXZhdGUgc3RhdGljICR2ZXJzaW9uID0gJzAuNjInOwoJCQoJCXByaXZhdGUgJGRhdGEgPSBhcnJheSgpOwoJCXByaXZhdGUgJHNldHRpbmdzID0gYXJyYXkoCgkJCSdvcHRpbWl6ZScgPT4gdHJ1ZSwKCQkJJ3RyaW0nID0+IGZhbHNlCgkJKTsKCQkKCQlwcml2YXRlICRjb21waWxlciA9IG51bGw7CgkJCgkJZnVuY3Rpb24gX19jb25zdHJ1Y3QoJGNvZGUsIGFycmF5ICRvcHRpb25zID0gYXJyYXkoKSl7CgkJCWlmKCEkY29kZSkKCQkJewoJCQkJdGhyb3cgbmV3IEV4Y2VwdGlvbignTm8gY29kZSB3YXMgcHJvdmlkZWQnKTsKCQkJfQoJCQkKCQkJaWYoJG9wdGlvbnMpCgkJCXsKCQkJCSR0aGlzLT5zZXR0aW5ncyA9IGFycmF5X21lcmdlKCR0aGlzLT5zZXR0aW5ncywgJG9wdGlvbnMpOwoJCQl9CgkJCQoJCQkkdGhpcy0+Y29tcGlsZXIgPSBuZXcgU2ltcGxlVGVtcGxhdGVfQ29tcGlsZXIoJHRoaXMsICRjb2RlLCAkdGhpcy0+c2V0dGluZ3MpOwoJCX0KCQkKCQlmdW5jdGlvbiBzZXREYXRhKCRrZXksICR2YWx1ZSl7CgkJCSR0aGlzLT5kYXRhWyRrZXldID0gJHZhbHVlOwoJCX0KCQkKCQlmdW5jdGlvbiBnZXREYXRhKCRrZXksICR2YWx1ZSl7CgkJCXJldHVybiBpc3NldCgkdGhpcy0+ZGF0YVska2V5XSkgPyAkdGhpcy0+ZGF0YVska2V5XSA6IG51bGw7CgkJfQoJCQoJCWZ1bmN0aW9uIHVuc2V0RGF0YSgka2V5KXsKCQkJdW5zZXQoJHRoaXMtPmRhdGFbJGtleV0pOwoJCX0KCQkKCQlmdW5jdGlvbiBsb2FkRGF0YSgkZGF0YSl7CgkJCWZvcmVhY2goJGRhdGEgYXMgJGsgPT4gJHZhbHVlKQoJCQl7CgkJCQkkdGhpcy0+ZGF0YVska10gPSAkdmFsdWU7CgkJCX0KCQl9CgkJCgkJZnVuY3Rpb24gY2xlYXJEYXRhKCl7CgkJCSR0aGlzLT5kYXRhID0gYXJyYXkoKTsKCQl9CgkJCgkJZnVuY3Rpb24gZ2V0UEhQKCl7CgkJCXJldHVybiAkdGhpcy0+Y29tcGlsZXItPmdldFBIUCgpOwoJCX0KCQkKCQlmdW5jdGlvbiByZW5kZXIoKXsKCQkJJHRoaXMtPmRhdGFbJ2FyZ3YnXSA9IGZ1bmNfZ2V0X2FyZ3MoKTsKCQkJJHRoaXMtPmRhdGFbJ2FyZ2MnXSA9IGZ1bmNfbnVtX2FyZ3MoKTsKCQkJCgkJCSR0aGlzLT5kYXRhWydWRVJTSU9OJ10gPSBzZWxmOjokdmVyc2lvbjsKCQkJJHRoaXMtPmRhdGFbJ0VPTCddID0gUEhQX0VPTDsKCQkJCgkJCSRmbiA9ICR0aGlzLT5jb21waWxlci0+Z2V0Rk4oKTsKCQkJCgkJCXJldHVybiAkZm4oJHRoaXMtPmRhdGEpOwoJCX0KCQkKCQlzdGF0aWMgZnVuY3Rpb24gZnJvbUZpbGUoJHBhdGgsIGFycmF5ICRvcHRpb25zID0gYXJyYXkoKSl7CgkJCXJldHVybiBuZXcgc2VsZihmaWxlX2dldF9jb250ZW50cygkcGF0aCksICRvcHRpb25zKTsKCQl9CgkJCgkJc3RhdGljIGZ1bmN0aW9uIGZyb21TdHJpbmcoJHN0cmluZywgYXJyYXkgJG9wdGlvbnMgPSBhcnJheSgpKXsKCQkJcmV0dXJuIG5ldyBzZWxmKCRzdHJpbmcsICRvcHRpb25zKTsKCQl9CgkJCgkJc3RhdGljIGZ1bmN0aW9uIHZlcnNpb24oKXsKCQkJcmV0dXJuIHNlbGY6OiR2ZXJzaW9uOwoJCX0KCX0KCQoJJGMgPSA8PDwnQ09ERScKe0BzZXRBIGFyZ3YuMX17QGVhY2hhcmd2LjB9e0BzZXRDIEMsIntAZWNob0EuIixfLCJ9In17QGNhbGxqb2luIGludG9DIiIsQ317QC99e0BldmFsQ30KQ09ERTsKCQoJLyoKCXVuZ29sZmVkOgoJJGMgPSA8PDwnQ09ERScKe0BzZXQgYXJncyBhcmd2LjF9CntAZWFjaCBhcmd2LjAgYXMgbnVtYmVyfQoJe0BzZXQgY29kZSBjb2RlLCAie0BlY2hvIGFyZ3MuIiwgbnVtYmVyICwgIn0ifQoJe0BjYWxsIGpvaW4gaW50byBjb2RlICIiLCBjb2RlfQp7QC99CntAZXZhbCBjb2RlfQpDT0RFOyovCgkKCSR4ID0gbmV3IFNpbXBsZVRlbXBsYXRlKCRjKTsKCQoJZWNobyAkeC0+cmVuZGVyKCcwMTIzNDU2Nzg5JywgJ2FiY2RlZmdoaWprbCcpOw==