<?php
/*
PL/0 Compiler+Interpreter
v1.0 Beta
2009-04-11 (last mod: 2009-04-11)
*/
// symbol table record structure
class tablerec
{
public $name ;
public $kind ;
public $lvl ;
public $addr ;
public $val ;
public function __construct( $n , $k )
{
$this -> name = $n ;
$this -> kind = $k ;
}
public function __toString( )
{
return "$this->name \t $this->kind \t $this->lvl \t $this->addr \t $this->val " ;
}
}
// p-code instruction structure
class instr
{
public $i ;
public $l ;
public $a ;
public function __construct( $i , $l , $a )
{
$this -> i = $i ;
$this -> l = $l ;
$this -> a = $a ;
}
public function __toString( )
{
return "$this->i $this->l $this->a " ;
}
}
// globals
$web = ( $_SERVER [ "DOCUMENT_ROOT" ] == "" ? false : true ) ; // we have to know if the input comes from stdin or the web
$data ; // the input
$ch ; // current character read
$line ; // current line read
$i = 0 ;
$rwords = array ( "BEGIN" , "CALL" , "CASE" , "CEND" , "CONST" , "DO" , "DOWNTO" , "ELSE" , "END" , "FOR" , "IF" , "ODD" , "OF" , "PROCEDURE" , "REPEAT" , "THEN" , "TO" , "UNTIL" , "VAR" , "WHILE" , "WRITE" , "WRITELN" ) ; // reserved words $ops = array ( "+" , "-" , "*" , "/" , "(" , ")" , "=" , "," , "." , "<>" , "<" , ">" , "<=" , ">=" , ";" , ":" , ":=" ) ; // operators $ops_names = array ( "PLUS" , "MINUS" , "TIMES" , "SLASH" , "LPAREN" , "RPAREN" , "EQUAL" , "COMMA" , "PERIOD" , "NE" , "LT" , "GT" , "LTE" , "GTE" , "SEMICOLON" , "COLON" , "BECOMES" ) ; // operator names $instr = array ( "LIT" , "OPR" , "LOD" , "STO" , "CAL" , "INT" , "JMP" , "JPC" ) ; // pcode instructions $st_types = array ( "CONSTANT" , "VARIABLE" , "PROCEDURE" ) ; // symbol table types $sym ; // current sym
$id ; // current IDENT
$table ; // symbol table
$code ; // program store
$value ; // current NUMBER value
$codeinx = 0 ;
$norw = 22 ; // number of reserved words
$txmax = 100 ; // max number of IDENT in symbol table
$nmax = 14 ; // max length of NUMBER
$al = 10 ; // max length of IDENT
// BLOCK
function block( $tx , $lev )
{
global $sym , $codeinx , $table , $code ;
$dx = 3 ;
$tx0 = $tx ;
$codeinx0 ;
$table [ $tx ] -> addr = $codeinx ;
gen( "JMP" , 0 , 0 ) ;
while ( $sym == "CONST" || $sym == "VAR" || $sym == "PROCEDURE" )
{
if ( $sym == "CONST" )
{
do
{
getsym( ) ;
$tx = constdeclaration( $tx , $lev , $dx ) ;
}
while ( $sym == "COMMA" ) ;
if ( $sym != "SEMICOLON" )
error( 4 ) ;
getsym( ) ;
}
if ( $sym == "VAR" )
{
do
{
getsym( ) ;
$tx = vardeclaration( $tx , $lev , $dx ) ;
}
while ( $sym == "COMMA" ) ;
if ( $sym != "SEMICOLON" )
error( 4 ) ;
getsym( ) ;
}
while ( $sym == "PROCEDURE" )
{
getsym( ) ;
if ( $sym != "IDENT" )
error( 1 ) ;
$tx = enter( "PROCEDURE" , $tx , $lev , $dx ) ;
getsym( ) ;
if ( $sym != "SEMICOLON" )
error( 4 ) ;
getsym( ) ;
block( $tx , $lev + 1 ) ;
if ( $sym != "SEMICOLON" )
error( 4 ) ;
getsym( ) ;
}
}
$code [ $table [ $tx0 ] -> addr ] -> a = $codeinx ;
$table [ $tx0 ] -> addr = $codeinx ;
$codeinx0 = $codeinx ;
gen( "INT" , 0 , $dx ) ;
# print_st($tx);
statement( $tx , $lev ) ;
gen( "OPR" , 0 , 0 ) ;
listcode( $codeinx0 ) ;
}
// STATEMENT
function statement( $tx , $lev )
{
global $sym , $id , $table , $codeinx , $code , $value ;
if ( $sym == "IDENT" )
{
$i = position( $id , $tx ) ;
if ( $i == 0 )
error( 13 ) ;
if ( $table [ $i ] -> kind != "VARIABLE" )
error( 14 ) ;
getsym( ) ;
if ( $sym != "BECOMES" )
error( 6 ) ;
getsym( ) ;
expression( $tx , $lev ) ;
gen( "STO" , $lev - $table [ $i ] -> lvl , $table [ $i ] -> addr ) ;
}
else if ( $sym == "CALL" )
{
getsym( ) ;
if ( $sym != "IDENT" )
error( 1 ) ;
$i = position( $id , $tx ) ;
if ( $i == 0 )
error( 13 ) ;
if ( $table [ $i ] -> kind != "PROCEDURE" )
error( 15 ) ;
getsym( ) ;
gen( "CAL" , $lev - $table [ $i ] -> lvl , $table [ $i ] -> addr ) ;
}
else if ( $sym == "BEGIN" )
{
do
{
getsym( ) ;
statement( $tx , $lev ) ;
}
while ( $sym == "SEMICOLON" ) ;
if ( $sym != "END" )
error( 7 ) ;
getsym( ) ;
}
else if ( $sym == "IF" )
{
getsym( ) ;
condition( $tx , $lev ) ;
$cx1 = $codeinx ;
gen( "JPC" , 0 , 0 ) ;
if ( $sym != "THEN" )
error( 8 ) ;
getsym( ) ;
statement( $tx , $lev ) ;
$code [ $cx1 ] -> a = $codeinx ;
/***** ADD CODE FOR ELSE HERE *****/
}
else if ( $sym == "WHILE" )
{
getsym( ) ;
$cx1 = $codeinx ;
condition( $tx , $lev ) ;
$cx2 = $codeinx ;
gen( "JPC" , 0 , 0 ) ;
if ( $sym != "DO" )
error( 9 ) ;
getsym( ) ;
statement( $tx , $lev ) ;
gen( "JMP" , 0 , $cx1 ) ;
$code [ $cx2 ] -> a = $codeinx ;
}
else if ( $sym == "REPEAT" )
{
/***** ADD CODE FOR REPEAT-UNTIL HERE *****/
}
else if ( $sym == "WRITE" )
{
/***** ADD CODE FOR WRITE HERE *****/
}
else if ( $sym == "WRITELN" )
{
/***** ADD CODE FOR WRITELN HERE *****/
}
else if ( $sym == "FOR" )
{
/***** ADD CODE FOR FOR-DO HERE *****/
}
else if ( $sym == "CASE" )
{
/***** ADD CODE FOR CASE-CEND HERE *****/
}
}
// CONDITION
function condition( $tx , $lev )
{
global $sym ;
$savesym ;
if ( $sym == "ODD" )
{
getsym( ) ;
expression( $tx , $lev ) ;
gen( "OPR" , 0 , 6 ) ;
}
else
{
expression( $tx , $lev ) ;
if ( $sym != "EQUAL" && $sym != "NE" && $sym != "LT" && $sym != "GT" && $sym != "LTE" && $sym != "GTE" )
error( 10 ) ;
$savesym = $sym ;
getsym( ) ;
expression( $tx , $lev ) ;
switch ( $savesym )
{
case "EQUAL" :
gen( "OPR" , 0 , 8 ) ;
break ;
case "NE" :
gen( "OPR" , 0 , 9 ) ;
break ;
case "LT" :
gen( "OPR" , 0 , 10 ) ;
break ;
case "GT" :
gen( "OPR" , 0 , 12 ) ;
break ;
case "LTE" :
gen( "OPR" , 0 , 13 ) ;
break ;
case "GTE" :
gen( "OPR" , 0 , 11 ) ;
break ;
}
}
}
// EXPRESSION
function expression( $tx , $lev )
{
global $sym ;
$savesym ;
if ( $sym == "PLUS" || $sym == "MINUS" )
{
$savesym = $sym ;
getsym( ) ;
}
term( $tx , $lev ) ;
if ( $savesym == "MINUS" )
gen( "OPR" , 0 , 1 ) ;
while ( $sym == "PLUS" || $sym == "MINUS" )
{
$savesym = $sym ;
getsym( ) ;
term( $tx , $lev ) ;
if ( $savesym == "PLUS" )
gen( "OPR" , 0 , 2 ) ;
else
gen( "OPR" , 0 , 3 ) ;
}
}
// TERM
function term( $tx , $lev )
{
global $sym ;
$savesym ;
factor( $tx , $lev ) ;
while ( $sym == "TIMES" || $sym == "SLASH" )
{
$savesym = $sym ;
getsym( ) ;
factor( $tx , $lev ) ;
if ( $savesym == "TIMES" )
gen( "OPR" , 0 , 4 ) ;
else
gen( "OPR" , 0 , 5 ) ;
}
}
// FACTOR
function factor( $tx , $lev )
{
global $sym , $id , $table , $value ;
if ( $sym == "IDENT" )
{
$i = position( $id , $tx ) ;
if ( $i == 0 )
error( 13 ) ;
switch ( $table [ $i ] -> kind )
{
case "VARIABLE" :
gen( "LOD" , $lev - $table [ $i ] -> lvl , $table [ $i ] -> addr ) ;
break ;
case "CONSTANT" :
gen( "LIT" , 0 , $table [ $i ] -> val ) ;
break ;
case "PROCEDURE" :
error( 14 ) ;
break ;
}
getsym( ) ;
}
else if ( $sym == "NUMBER" )
{
gen( "LIT" , 0 , $value ) ;
getsym( ) ;
}
else if ( $sym == "LPAREN" )
{
getsym( ) ;
expression( $tx , $lev ) ;
if ( $sym != "RPAREN" )
error( 11 ) ;
getsym( ) ;
}
else
error( 12 ) ;
}
// determines the position of an identifier in the symbol table
function position( $id , $tx )
{
global $table ;
// what we're looking for goes in table[0]
$table [ 0 ] -> name = $id ;
// search from the current tx on up
for ( $i = $tx ; $i >= 0 ; $i -- )
{
if ( $table [ $i ] -> name == $id )
return $i ;
}
return 0 ;
}
// enters an identifier in the symbol table
function enter( $kind , $tx , $lev , & $dx )
{
global $id , $table , $value ;
// we should use txmax to keep symbol table at its max size
$tx ++;
$table [ $tx ] = new tablerec( $id , $kind ) ;
if ( $kind == "CONSTANT" )
$table [ $tx ] -> val = $value ;
else if ( $kind == "VARIABLE" )
{
$table [ $tx ] -> lvl = $lev ;
$table [ $tx ] -> addr = $dx ;
$dx ++;
}
else if ( $kind == "PROCEDURE" )
$table [ $tx ] -> lvl = $lev ;
return $tx ;
}
// prints the symbol table (for debugging)
function print_st( $tx )
{
global $table ;
echo "\t NAME\t KIND\t \t LEVEL\t ADDRESS\t VALUE\n " ;
for ( $i = 0 ; $i <= $tx ; $i ++ )
echo "$i \t " . $table [ $i ] -> name . "\t " . $table [ $i ] -> kind . "\t " . $table [ $i ] -> lvl . "\t " . $table [ $i ] -> addr . "\t " . $table [ $i ] -> val . "\n " ;
}
// lists the code for the block
function listcode( $codeinx0 )
{
global $codeinx , $code , $web ;
for ( $i = $codeinx0 ; $i < $codeinx ; $i ++ )
echo "$i $code [$i ]" . ( $web ? "<br/>\n " : "\n " ) ;
echo "\n " ;
}
// generates a p-code instruction
function gen( $i , $l , $a )
{
global $codeinx , $code ;
$code [ $codeinx ] = new instr( $i , $l , $a ) ;
$codeinx ++;
}
// declares constants (and enters in symbol table)
function constdeclaration( $tx , $lev , & $dx )
{
global $sym ;
if ( $sym != "IDENT" )
error( 1 ) ;
getsym( ) ;
if ( $sym != "EQUAL" )
error( 2 ) ;
getsym( ) ;
if ( $sym != "NUMBER" )
error( 3 ) ;
getsym( ) ;
$tx = enter( "CONSTANT" , $tx , $lev , $dx ) ;
return $tx ;
}
// declares variables (and enters in symbol table)
function vardeclaration( $tx , $lev , & $dx )
{
global $sym ;
if ( $sym != "IDENT" )
error( 1 ) ;
getsym( ) ;
$tx = enter( "VARIABLE" , $tx , $lev , $dx ) ;
return $tx ;
}
// handles errors
function error( $i )
{
global $sym , $id ;
switch ( $i )
{
case 1 :
echo "Identifier expected" ;
break ;
case 2 :
echo "EQUAL (=) expected" ;
break ;
case 3 :
echo "NUMBER expected" ;
break ;
case 4 :
echo "SEMICOLON (;) expected" ;
break ;
case 5 :
echo "PERIOD (.) expected" ;
break ;
case 6 :
echo "BECOMES (:=) expected" ;
break ;
case 7 :
echo "END expected" ;
break ;
case 8 :
echo "THEN expected" ;
break ;
case 9 :
echo "DO expected" ;
break ;
case 10 :
echo "Relational operator expected" ;
break ;
case 11 :
echo "RPAREN (() expected" ;
break ;
case 12 :
echo "An expression cannot begin with this symbol" ;
break ;
case 13 :
echo "Undeclared identifier" ;
break ;
case 14 :
echo "Assignment to CONSTANT or PROCEDURE not allowed" ;
break ;
case 15 :
echo "CALL must be followed by a PROCEDURE" ;
break ;
case 16 :
echo "UNTIL expected" ;
break ;
case 17 :
echo "LPAREN (() expected" ;
break ;
case 18 :
echo "TO or DOWNTO expected" ;
break ;
case 19 :
echo "NUMBER or CONSTANT expected" ;
break ;
case 20 :
echo "COLON (:) expected" ;
break ;
case 21 :
echo "OF expected" ;
break ;
case 22 :
echo "CEND expected" ;
break ;
default :
echo "Unknown error!" ;
break ;
}
echo "; got $sym " . ( $sym == "IDENT" ? " ($id )" : "" ) . "\n " ;
}
// GETSYM
function getsym( )
{
global $sym , $ch , $id , $rwords , $ops , $ops_names , $line , $value , $nmax , $al ;
$sym = false ;
// skip whitespace
while ( ! isset ( $ch ) || $ch == "" || $ch == " " || $ch == "\r " || $ch == "\n " ) {
if ( $ch === false )
return ;
getchar( ) ;
}
// we either have an identifier or a reserved word
if ( $ch >= "A" && $ch <= "Z" )
{
$tok = $ch ;
getchar( ) ;
while ( ( $ch >= "A" && $ch <= "Z" ) || ( $ch >= "0" && $ch <= "9" ) )
{
error( ) ;
$tok .= $ch ;
if ( $line == "" )
{
getchar( ) ;
break ;
}
getchar( ) ;
}
// assume an identifier but search through reserved words
$sym = "IDENT" ;
// we could use norw here as sentinel, but why?
foreach ( $rwords as $rword )
{
if ( $tok == $rword )
{
$sym = $rword ;
break ;
}
}
if ( $sym == "IDENT" )
$id = $tok ;
}
// we have a number
else if ( $ch >= "0" && $ch <= "9" )
{
$tok = $ch ;
getchar( ) ;
while ( $ch >= "0" && $ch <= "9" )
{
error( ) ;
$tok .= $ch ;
getchar( ) ;
}
$sym = "NUMBER" ;
$value = $tok ;
}
// we have some other symbol
else
{
// perhaps : or :=
if ( $ch == ":" )
{
$tok = $ch ;
getchar( ) ;
if ( $ch == "=" )
{
$tok .= $ch ;
getchar( ) ;
}
}
// maybe <, <>, or <=
else if ( $ch == "<" )
{
$tok = $ch ;
getchar( ) ;
if ( $ch == ">" || $ch == "=" )
{
$tok .= $ch ;
getchar( ) ;
}
}
// > or >=
else if ( $ch == ">" )
{
$tok = $ch ;
getchar( ) ;
if ( $ch == "=" )
{
$tok .= $ch ;
getchar( ) ;
}
}
// some other single-character symbol
else
{
$tok = $ch ;
getchar( ) ;
}
// match the symbol and get its name
for ( $i = 0 ; $i < sizeof ( $ops ) ; $i ++ ) {
if ( $tok == $ops [ $i ] )
{
$sym = $ops_names [ $i ] ;
break ;
}
}
}
}
// GETCHAR
function getchar( )
{
global $line , $ch ;
// if we don't yet have a line, get one
if ( $line == "" )
{
// if we're at the end of input, get out
if ( getline( ) === false )
{
$ch = false ;
return ;
}
}
// get the current character and remainder of the line
$ch = $line [ 0 ] ;
}
// GETLINE
function getline( )
{
global $line , $data , $i , $web ;
// if we're at the end of input, get out
return false ;
// get the next line
$line = $data [ $i ++ ] ;
echo ( $web ?
( str_replace ( " " , " " , $line ) . "<br/>\n " ) : "$line \n " ) ; }
function print_stack( $s , $b , $t )
{
for ( $i = $t ; $i > 0 ; $i -- )
{
$st = "$i : $s [$i ]" ;
if ( $b == $i )
$st .= "\t B" ;
if ( $t == $i )
$st .= "\t T" ;
echo "$st \n " ;
}
}
function base( $l , $b , $s )
{
while ( $l > 0 )
{
$b = $s [ $b ] ;
$l --;
}
return $b ;
}
function interpret( )
{
global $web , $code ;
echo "Start PL/0" . ( $web ? "<br/>\n " : "\n " ) ;
$t = 0 ;
$b = 1 ;
$p = 0 ;
$s [ 1 ] = 0 ;
$s [ 2 ] = 0 ;
$s [ 3 ] = 0 ;
while ( true )
{
$i = $code [ $p ] -> i ;
$l = $code [ $p ] -> l ;
$a = $code [ $p ] -> a ;
# echo "$p: $i $l $a\n";
$p ++;
switch ( $i )
{
case "LIT" :
$t ++;
$s [ $t ] = $a ;
break ;
case "OPR" :
switch ( $a )
{
case 0 :
$t = $b - 1 ;
$p = $s [ $t + 3 ] ;
$b = $s [ $t + 2 ] ;
break ;
case 1 :
$s [ $t ] = $s [ $t ] * - 1 ;
break ;
case 2 :
$t --;
$s [ $t ] = $s [ $t ] + $s [ $t + 1 ] ;
break ;
case 3 :
$t --;
$s [ $t ] = $s [ $t ] - $s [ $t + 1 ] ;
break ;
case 4 :
$t --;
$s [ $t ] = $s [ $t ] * $s [ $t + 1 ] ;
break ;
case 5 :
$t --;
$s [ $t ] = ( $s [ $t ] - ( $s [ $t ] % $s [ $t + 1 ] ) ) / $s [ $t + 1 ] ;
break ;
case 6 :
$s [ $t ] = ( $s [ $t ] % 2 == 0 ? 0 : 1 ) ;
break ;
case 8 :
$t --;
$s [ $t ] = ( $s [ $t ] == $s [ $t + 1 ] ? 1 : 0 ) ;
break ;
case 9 :
$t --;
$s [ $t ] = ( $s [ $t ] != $s [ $t + 1 ] ? 1 : 0 ) ;
break ;
case 10 :
$t --;
$s [ $t ] = ( $s [ $t ] < $s [ $t + 1 ] ? 1 : 0 ) ;
break ;
case 11 :
$t --;
$s [ $t ] = ( $s [ $t ] >= $s [ $t + 1 ] ? 1 : 0 ) ;
break ;
case 12 :
$t --;
$s [ $t ] = ( $s [ $t ] > $s [ $t + 1 ] ? 1 : 0 ) ;
break ;
case 13 :
$t --;
$s [ $t ] = ( $s [ $t ] <= $s [ $t + 1 ] ? 1 : 0 ) ;
break ;
case 14 :
echo "$s [$t ] " ;
$t --;
break ;
case 15 :
echo ( $web ? "<br/>\n " : "\n " ) ;
break ;
default :
echo "!!HOUSTON, WE HAVE A PROBLEM!!" . ( $web ? "<br/>\n " : "\n " ) ;
break ;
}
break ;
case "LOD" :
$t ++;
$s [ $t ] = $s [ base( $l , $b , $s ) + $a ] ;
break ;
case "STO" :
$s [ base( $l , $b , $s ) + $a ] = $s [ $t ] ;
$t --;
break ;
case "CAL" :
$s [ $t + 1 ] = base( $l , $b , $s ) ;
$s [ $t + 2 ] = $b ;
$s [ $t + 3 ] = $p ;
$b = $t + 1 ;
$p = $a ;
break ;
case "INT" :
$t += $a ;
break ;
case "JMP" :
$p = $a ;
break ;
case "JPC" :
if ( $s [ $t ] == $l )
$p = $a ;
$t --;
break ;
}
# print_stack($s, $b, $t);
if ( $p == 0 )
break ;
}
echo "End PL/0" . ( $web ? "<br/>\n " : "\n " ) ;
}
// display the web interface (prompt for source file)
function show_intfc( )
{
echo <<< END
<html>
<body>
PL/0 Compiler+Interpreter<br/>
v1.0 Beta<br/>
2009-04-11 (last mod: 2009-04-11)<p/>
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="data"/>
PL/0 source file: <input type="file" name="file" size="80"/><p/>
<input type="submit" value="Compile!"/>
</form><p/>
</body>
</html>
END ;
}
// MAIN
// if we're accessed via the web, we get input through a form post
if ( $web )
{
// if we've got the source (via form submission)
if ( isset ( $_REQUEST [ "data" ] ) && $_FILES [ "file" ] [ "name" ] != "" ) {
// and if the file exists on local disk
{
// get its contents and split into lines
$data = split ( "\n " , $file ) ; }
// otherwise, bail!
else
{
echo "ERROR: Unable to open " . $_FILES [ "file" ] [ "name" ] . "<br/>\n " ;
}
}
// no submitted file? display the form
else
{
show_intfc( ) ;
}
}
// otherwise, get input from stdin
else
{
// get the input and split into lines
//$data = split("\n", $file);
//REPLACED DEPRECATED SPLIT WITH NEW EXPLODE
}
// PROGRAM
getsym( ) ;
block( 0 , 0 ) ;
if ( $sym != "PERIOD" )
error( 5 ) ;
echo "Successful compilation!" . ( $web ? "<p/>\n " : "\n \n " ) ;
interpret( ) ;
?>
PD9waHAKCi8qClBMLzAgQ29tcGlsZXIrSW50ZXJwcmV0ZXIKdjEuMCBCZXRhCjIwMDktMDQtMTEgKGxhc3QgbW9kOiAyMDA5LTA0LTExKQoqLwoKLy8gc3ltYm9sIHRhYmxlIHJlY29yZCBzdHJ1Y3R1cmUKY2xhc3MgdGFibGVyZWMKewoJCXB1YmxpYyAkbmFtZTsKCQlwdWJsaWMgJGtpbmQ7CgkJcHVibGljICRsdmw7CgkJcHVibGljICRhZGRyOwoJCXB1YmxpYyAkdmFsOwoKCQlwdWJsaWMgZnVuY3Rpb24gX19jb25zdHJ1Y3QoJG4sICRrKQoJCXsKCQkJJHRoaXMtPm5hbWUgPSAkbjsKCQkJJHRoaXMtPmtpbmQgPSAkazsKCQl9CgoJCXB1YmxpYyBmdW5jdGlvbiBfX3RvU3RyaW5nKCkKCQl7CgkJCXJldHVybiAiJHRoaXMtPm5hbWVcdCR0aGlzLT5raW5kXHQkdGhpcy0+bHZsXHQkdGhpcy0+YWRkclx0JHRoaXMtPnZhbCI7CgkJfQp9CgovLyBwLWNvZGUgaW5zdHJ1Y3Rpb24gc3RydWN0dXJlCmNsYXNzIGluc3RyCnsKCQlwdWJsaWMgJGk7CgkJcHVibGljICRsOwoJCXB1YmxpYyAkYTsKCgkJcHVibGljIGZ1bmN0aW9uIF9fY29uc3RydWN0KCRpLCAkbCwgJGEpCgkJewoJCQkkdGhpcy0+aSA9ICRpOwoJCQkkdGhpcy0+bCA9ICRsOwoJCQkkdGhpcy0+YSA9ICRhOwoJCX0KCgkJcHVibGljIGZ1bmN0aW9uIF9fdG9TdHJpbmcoKQoJCXsKCQkJcmV0dXJuICIkdGhpcy0+aSAkdGhpcy0+bCAkdGhpcy0+YSI7CgkJfQp9CgovLyBnbG9iYWxzCiR3ZWIgPSAoJF9TRVJWRVJbIkRPQ1VNRU5UX1JPT1QiXSA9PSAiIiA/IGZhbHNlIDogdHJ1ZSk7CS8vIHdlIGhhdmUgdG8ga25vdyBpZiB0aGUgaW5wdXQgY29tZXMgZnJvbSBzdGRpbiBvciB0aGUgd2ViCiRkYXRhOwkvLyB0aGUgaW5wdXQKJGNoOwkvLyBjdXJyZW50IGNoYXJhY3RlciByZWFkCiRsaW5lOwkvLyBjdXJyZW50IGxpbmUgcmVhZAokaSA9IDA7CiRyd29yZHMgPSBhcnJheSgiQkVHSU4iLCAiQ0FMTCIsICJDQVNFIiwgIkNFTkQiLCAiQ09OU1QiLCAiRE8iLCAiRE9XTlRPIiwgIkVMU0UiLCAiRU5EIiwgIkZPUiIsICJJRiIsICJPREQiLCAiT0YiLCAiUFJPQ0VEVVJFIiwgIlJFUEVBVCIsICJUSEVOIiwgIlRPIiwgIlVOVElMIiwgIlZBUiIsICJXSElMRSIsICJXUklURSIsICJXUklURUxOIik7CS8vIHJlc2VydmVkIHdvcmRzCiRvcHMgPSBhcnJheSgiKyIsICItIiwgIioiLCAiLyIsICIoIiwgIikiLCAiPSIsICIsIiwgIi4iLCAiPD4iLCAiPCIsICI+IiwgIjw9IiwgIj49IiwgIjsiLCAiOiIsICI6PSIpOwkvLyBvcGVyYXRvcnMKJG9wc19uYW1lcyA9IGFycmF5KCJQTFVTIiwgIk1JTlVTIiwgIlRJTUVTIiwgIlNMQVNIIiwgIkxQQVJFTiIsICJSUEFSRU4iLCAiRVFVQUwiLCAiQ09NTUEiLCAiUEVSSU9EIiwgIk5FIiwgIkxUIiwgIkdUIiwgIkxURSIsICJHVEUiLCAiU0VNSUNPTE9OIiwgIkNPTE9OIiwgIkJFQ09NRVMiKTsJLy8gb3BlcmF0b3IgbmFtZXMKJGluc3RyID0gYXJyYXkoIkxJVCIsICJPUFIiLCAiTE9EIiwgIlNUTyIsICJDQUwiLCAiSU5UIiwgIkpNUCIsICJKUEMiKTsJLy8gcGNvZGUgaW5zdHJ1Y3Rpb25zCiRzdF90eXBlcyA9IGFycmF5KCJDT05TVEFOVCIsICJWQVJJQUJMRSIsICJQUk9DRURVUkUiKTsJLy8gc3ltYm9sIHRhYmxlIHR5cGVzCiRzeW07CQkvLyBjdXJyZW50IHN5bQokaWQ7CQkvLyBjdXJyZW50IElERU5UCiR0YWJsZTsJCS8vIHN5bWJvbCB0YWJsZQokY29kZTsJCS8vIHByb2dyYW0gc3RvcmUKJHZhbHVlOwkJLy8gY3VycmVudCBOVU1CRVIgdmFsdWUKJGNvZGVpbnggPSAwOwokbm9ydyA9IDIyOwkvLyBudW1iZXIgb2YgcmVzZXJ2ZWQgd29yZHMKJHR4bWF4ID0gMTAwOwkvLyBtYXggbnVtYmVyIG9mIElERU5UIGluIHN5bWJvbCB0YWJsZQokbm1heCA9IDE0OwkvLyBtYXggbGVuZ3RoIG9mIE5VTUJFUgokYWwgPSAxMDsJLy8gbWF4IGxlbmd0aCBvZiBJREVOVAoKLy8gQkxPQ0sKZnVuY3Rpb24gYmxvY2soJHR4LCAkbGV2KQp7CglnbG9iYWwgJHN5bSwgJGNvZGVpbngsICR0YWJsZSwgJGNvZGU7CgoJJGR4ID0gMzsKCSR0eDAgPSAkdHg7CgkkY29kZWlueDA7CgkkdGFibGVbJHR4XS0+YWRkciA9ICRjb2RlaW54OwoJZ2VuKCJKTVAiLCAwLCAwKTsKCgl3aGlsZSAoJHN5bSA9PSAiQ09OU1QiIHx8ICRzeW0gPT0gIlZBUiIgfHwgJHN5bSA9PSAiUFJPQ0VEVVJFIikKCXsKCQlpZiAoJHN5bSA9PSAiQ09OU1QiKQoJCXsKCQkJZG8KCQkJewoJCQkJZ2V0c3ltKCk7CgkJCQkkdHggPSBjb25zdGRlY2xhcmF0aW9uKCR0eCwgJGxldiwgJGR4KTsKCQkJfQoJCQl3aGlsZSAoJHN5bSA9PSAiQ09NTUEiKTsKCQkJaWYgKCRzeW0gIT0gIlNFTUlDT0xPTiIpCgkJCQllcnJvcig0KTsKCQkJZ2V0c3ltKCk7CgkJfQoJCWlmICgkc3ltID09ICJWQVIiKQoJCXsKCQkJZG8KCQkJewoJCQkJZ2V0c3ltKCk7CgkJCQkkdHggPSB2YXJkZWNsYXJhdGlvbigkdHgsICRsZXYsICRkeCk7CgkJCX0KCQkJd2hpbGUgKCRzeW0gPT0gIkNPTU1BIik7CgkJCWlmICgkc3ltICE9ICJTRU1JQ09MT04iKQoJCQkJZXJyb3IoNCk7CgkJCWdldHN5bSgpOwoJCX0KCQl3aGlsZSAoJHN5bSA9PSAiUFJPQ0VEVVJFIikKCQl7CgkJCWdldHN5bSgpOwoJCQlpZiAoJHN5bSAhPSAiSURFTlQiKQoJCQkJZXJyb3IoMSk7CgkJCSR0eCA9IGVudGVyKCJQUk9DRURVUkUiLCAkdHgsICRsZXYsICRkeCk7CgkJCWdldHN5bSgpOwoJCQlpZiAoJHN5bSAhPSAiU0VNSUNPTE9OIikKCQkJCWVycm9yKDQpOwoJCQlnZXRzeW0oKTsKCQkJYmxvY2soJHR4LCAkbGV2ICsgMSk7CgkJCWlmICgkc3ltICE9ICJTRU1JQ09MT04iKQoJCQkJZXJyb3IoNCk7CgkJCWdldHN5bSgpOwoJCX0KCX0KCSRjb2RlWyR0YWJsZVskdHgwXS0+YWRkcl0tPmEgPSAkY29kZWlueDsKCSR0YWJsZVskdHgwXS0+YWRkciA9ICRjb2RlaW54OwoJJGNvZGVpbngwID0gJGNvZGVpbng7CglnZW4oIklOVCIsIDAsICRkeCk7CiMJcHJpbnRfc3QoJHR4KTsKCXN0YXRlbWVudCgkdHgsICRsZXYpOwoJZ2VuKCJPUFIiLCAwLCAwKTsKCWxpc3Rjb2RlKCRjb2RlaW54MCk7Cn0KCi8vIFNUQVRFTUVOVApmdW5jdGlvbiBzdGF0ZW1lbnQoJHR4LCAkbGV2KQp7CglnbG9iYWwgJHN5bSwgJGlkLCAkdGFibGUsICRjb2RlaW54LCAkY29kZSwgJHZhbHVlOwoKCWlmICgkc3ltID09ICJJREVOVCIpCgl7CgkJJGkgPSBwb3NpdGlvbigkaWQsICR0eCk7CgkJaWYgKCRpID09IDApCgkJCWVycm9yKDEzKTsKCQlpZiAoJHRhYmxlWyRpXS0+a2luZCAhPSAiVkFSSUFCTEUiKQoJCQllcnJvcigxNCk7CgkJZ2V0c3ltKCk7CgkJaWYgKCRzeW0gIT0gIkJFQ09NRVMiKQoJCQllcnJvcig2KTsKCQlnZXRzeW0oKTsKCQlleHByZXNzaW9uKCR0eCwgJGxldik7CgkJZ2VuKCJTVE8iLCAkbGV2IC0gJHRhYmxlWyRpXS0+bHZsLCAkdGFibGVbJGldLT5hZGRyKTsKCX0KCWVsc2UgaWYgKCRzeW0gPT0gIkNBTEwiKQoJewoJCWdldHN5bSgpOwoJCWlmICgkc3ltICE9ICJJREVOVCIpCgkJCWVycm9yKDEpOwoJCSRpID0gcG9zaXRpb24oJGlkLCAkdHgpOwoJCWlmICgkaSA9PSAwKQoJCQllcnJvcigxMyk7CgkJaWYgKCR0YWJsZVskaV0tPmtpbmQgIT0gIlBST0NFRFVSRSIpCgkJCWVycm9yKDE1KTsKCQlnZXRzeW0oKTsKCQlnZW4oIkNBTCIsICRsZXYgLSAkdGFibGVbJGldLT5sdmwsICR0YWJsZVskaV0tPmFkZHIpOwoJfQoJZWxzZSBpZiAoJHN5bSA9PSAiQkVHSU4iKQoJewoJCWRvCgkJewoJCQlnZXRzeW0oKTsKCQkJc3RhdGVtZW50KCR0eCwgJGxldik7CgkJfQoJCXdoaWxlICgkc3ltID09ICJTRU1JQ09MT04iKTsKCQlpZiAoJHN5bSAhPSAiRU5EIikKCQkJZXJyb3IoNyk7CgkJZ2V0c3ltKCk7Cgl9CgllbHNlIGlmICgkc3ltID09ICJJRiIpCgl7CgkJZ2V0c3ltKCk7CgkJY29uZGl0aW9uKCR0eCwgJGxldik7CgkJJGN4MSA9ICRjb2RlaW54OwoJCWdlbigiSlBDIiwgMCwgMCk7CgkJaWYgKCRzeW0gIT0gIlRIRU4iKQoJCQllcnJvcig4KTsKCQlnZXRzeW0oKTsKCQlzdGF0ZW1lbnQoJHR4LCAkbGV2KTsKCQkkY29kZVskY3gxXS0+YSA9ICRjb2RlaW54OwoJCS8qKioqKiBBREQgQ09ERSBGT1IgRUxTRSBIRVJFICoqKioqLwoJfQoJZWxzZSBpZiAoJHN5bSA9PSAiV0hJTEUiKQoJewoJCWdldHN5bSgpOwoJCSRjeDEgPSAkY29kZWlueDsKCQljb25kaXRpb24oJHR4LCAkbGV2KTsKCQkkY3gyID0gJGNvZGVpbng7CgkJZ2VuKCJKUEMiLCAwLCAwKTsKCQlpZiAoJHN5bSAhPSAiRE8iKQoJCQllcnJvcig5KTsKCQlnZXRzeW0oKTsKCQlzdGF0ZW1lbnQoJHR4LCAkbGV2KTsKCQlnZW4oIkpNUCIsIDAsICRjeDEpOwoJCSRjb2RlWyRjeDJdLT5hID0gJGNvZGVpbng7Cgl9CgllbHNlIGlmICgkc3ltID09ICJSRVBFQVQiKQoJewoJCS8qKioqKiBBREQgQ09ERSBGT1IgUkVQRUFULVVOVElMIEhFUkUgKioqKiovCgl9CgllbHNlIGlmICgkc3ltID09ICJXUklURSIpCgl7CgkJLyoqKioqIEFERCBDT0RFIEZPUiBXUklURSBIRVJFICoqKioqLwoJfQoJZWxzZSBpZiAoJHN5bSA9PSAiV1JJVEVMTiIpCgl7CgkJLyoqKioqIEFERCBDT0RFIEZPUiBXUklURUxOIEhFUkUgKioqKiovCgl9CgllbHNlIGlmICgkc3ltID09ICJGT1IiKQoJewoJCS8qKioqKiBBREQgQ09ERSBGT1IgRk9SLURPIEhFUkUgKioqKiovCgl9CgllbHNlIGlmICgkc3ltID09ICJDQVNFIikKCXsKCQkvKioqKiogQUREIENPREUgRk9SIENBU0UtQ0VORCBIRVJFICoqKioqLwoJfQp9CgovLyBDT05ESVRJT04KZnVuY3Rpb24gY29uZGl0aW9uKCR0eCwgJGxldikKewoJZ2xvYmFsICRzeW07Cgkkc2F2ZXN5bTsKCglpZiAoJHN5bSA9PSAiT0REIikKCXsKCQlnZXRzeW0oKTsKCQlleHByZXNzaW9uKCR0eCwgJGxldik7CgkJZ2VuKCJPUFIiLCAwLCA2KTsKCX0KCWVsc2UKCXsKCQlleHByZXNzaW9uKCR0eCwgJGxldik7CgkJaWYgKCRzeW0gIT0gIkVRVUFMIiAmJiAkc3ltICE9ICJORSIgJiYgJHN5bSAhPSAiTFQiICYmICRzeW0gIT0gIkdUIiAmJiAkc3ltICE9ICJMVEUiICYmICRzeW0gIT0gIkdURSIpCgkJCWVycm9yKDEwKTsKCQkkc2F2ZXN5bSA9ICRzeW07CgkJZ2V0c3ltKCk7CgkJZXhwcmVzc2lvbigkdHgsICRsZXYpOwoJCXN3aXRjaCAoJHNhdmVzeW0pCgkJewoJCQljYXNlICJFUVVBTCI6CgkJCQlnZW4oIk9QUiIsIDAsIDgpOwoJCQkJYnJlYWs7CgkJCWNhc2UgIk5FIjoKCQkJCWdlbigiT1BSIiwgMCwgOSk7CgkJCQlicmVhazsKCQkJY2FzZSAiTFQiOgoJCQkJZ2VuKCJPUFIiLCAwLCAxMCk7CgkJCQlicmVhazsKCQkJY2FzZSAiR1QiOgoJCQkJZ2VuKCJPUFIiLCAwLCAxMik7CgkJCQlicmVhazsKCQkJY2FzZSAiTFRFIjoKCQkJCWdlbigiT1BSIiwgMCwgMTMpOwoJCQkJYnJlYWs7CgkJCWNhc2UgIkdURSI6CgkJCQlnZW4oIk9QUiIsIDAsIDExKTsKCQkJCWJyZWFrOwoJCX0KCX0KfQoKLy8gRVhQUkVTU0lPTgpmdW5jdGlvbiBleHByZXNzaW9uKCR0eCwgJGxldikKewoJZ2xvYmFsICRzeW07Cgkkc2F2ZXN5bTsKCglpZiAoJHN5bSA9PSAiUExVUyIgfHwgJHN5bSA9PSAiTUlOVVMiKQoJewoJCSRzYXZlc3ltID0gJHN5bTsKCQlnZXRzeW0oKTsKCX0KCXRlcm0oJHR4LCAkbGV2KTsKCWlmICgkc2F2ZXN5bSA9PSAiTUlOVVMiKQoJCWdlbigiT1BSIiwgMCwgMSk7Cgl3aGlsZSAoJHN5bSA9PSAiUExVUyIgfHwgJHN5bSA9PSAiTUlOVVMiKQoJewoJCSRzYXZlc3ltID0gJHN5bTsKCQlnZXRzeW0oKTsKCQl0ZXJtKCR0eCwgJGxldik7CgkJaWYgKCRzYXZlc3ltID09ICJQTFVTIikKCQkJZ2VuKCJPUFIiLCAwLCAyKTsKCQllbHNlCgkJCWdlbigiT1BSIiwgMCwgMyk7Cgl9Cn0KCi8vIFRFUk0KZnVuY3Rpb24gdGVybSgkdHgsICRsZXYpCnsKCWdsb2JhbCAkc3ltOwoJJHNhdmVzeW07CgoJZmFjdG9yKCR0eCwgJGxldik7Cgl3aGlsZSAoJHN5bSA9PSAiVElNRVMiIHx8ICRzeW0gPT0gIlNMQVNIIikKCXsKCQkkc2F2ZXN5bSA9ICRzeW07CgkJZ2V0c3ltKCk7CgkJZmFjdG9yKCR0eCwgJGxldik7CgkJaWYgKCRzYXZlc3ltID09ICJUSU1FUyIpCgkJCWdlbigiT1BSIiwgMCwgNCk7CgkJZWxzZQoJCQlnZW4oIk9QUiIsIDAsIDUpOwoJfQp9CgovLyBGQUNUT1IKZnVuY3Rpb24gZmFjdG9yKCR0eCwgJGxldikKewoJZ2xvYmFsICRzeW0sICRpZCwgJHRhYmxlLCAkdmFsdWU7CgoJaWYgKCRzeW0gPT0gIklERU5UIikKCXsKCQkkaSA9IHBvc2l0aW9uKCRpZCwgJHR4KTsKCQlpZiAoJGkgPT0gMCkKCQkJZXJyb3IoMTMpOwoJCXN3aXRjaCAoJHRhYmxlWyRpXS0+a2luZCkKCQl7CgkJCWNhc2UgIlZBUklBQkxFIjoKCQkJCWdlbigiTE9EIiwgJGxldiAtICR0YWJsZVskaV0tPmx2bCwgJHRhYmxlWyRpXS0+YWRkcik7CgkJCQlicmVhazsKCQkJY2FzZSAiQ09OU1RBTlQiOgoJCQkJZ2VuKCJMSVQiLCAwLCAkdGFibGVbJGldLT52YWwpOwoJCQkJYnJlYWs7CgkJCWNhc2UgIlBST0NFRFVSRSI6CgkJCQllcnJvcigxNCk7CgkJCQlicmVhazsKCQl9CgkJZ2V0c3ltKCk7Cgl9CgllbHNlIGlmICgkc3ltID09ICJOVU1CRVIiKQoJewoJCWdlbigiTElUIiwgMCwgJHZhbHVlKTsKCQlnZXRzeW0oKTsKCX0KCWVsc2UgaWYgKCRzeW0gPT0gIkxQQVJFTiIpCgl7CgkJZ2V0c3ltKCk7CgkJZXhwcmVzc2lvbigkdHgsICRsZXYpOwoJCWlmICgkc3ltICE9ICJSUEFSRU4iKQoJCQllcnJvcigxMSk7CgkJZ2V0c3ltKCk7Cgl9CgllbHNlCgkJZXJyb3IoMTIpOwp9CgovLyBkZXRlcm1pbmVzIHRoZSBwb3NpdGlvbiBvZiBhbiBpZGVudGlmaWVyIGluIHRoZSBzeW1ib2wgdGFibGUKZnVuY3Rpb24gcG9zaXRpb24oJGlkLCAkdHgpCnsKCWdsb2JhbCAkdGFibGU7CgoJLy8gd2hhdCB3ZSdyZSBsb29raW5nIGZvciBnb2VzIGluIHRhYmxlWzBdCgkkdGFibGVbMF0tPm5hbWUgPSAkaWQ7CgkvLyBzZWFyY2ggZnJvbSB0aGUgY3VycmVudCB0eCBvbiB1cAoJZm9yICgkaSA9ICR0eDsgJGkgPj0gMDsgJGktLSkKCXsKCQlpZiAoJHRhYmxlWyRpXS0+bmFtZSA9PSAkaWQpCgkJCXJldHVybiAkaTsKCX0KCglyZXR1cm4gMDsKfQoKLy8gZW50ZXJzIGFuIGlkZW50aWZpZXIgaW4gdGhlIHN5bWJvbCB0YWJsZQpmdW5jdGlvbiBlbnRlcigka2luZCwgJHR4LCAkbGV2LCAmJGR4KQp7CglnbG9iYWwgJGlkLCAkdGFibGUsICR2YWx1ZTsKCgkvLyB3ZSBzaG91bGQgdXNlIHR4bWF4IHRvIGtlZXAgc3ltYm9sIHRhYmxlIGF0IGl0cyBtYXggc2l6ZQoJJHR4Kys7CgkkdGFibGVbJHR4XSA9IG5ldyB0YWJsZXJlYygkaWQsICRraW5kKTsKCWlmICgka2luZCA9PSAiQ09OU1RBTlQiKQoJCSR0YWJsZVskdHhdLT52YWwgPSAkdmFsdWU7CgllbHNlIGlmICgka2luZCA9PSAiVkFSSUFCTEUiKQoJewoJCSR0YWJsZVskdHhdLT5sdmwgPSAkbGV2OwoJCSR0YWJsZVskdHhdLT5hZGRyID0gJGR4OwoJCSRkeCsrOwoJfQoJZWxzZSBpZiAoJGtpbmQgPT0gIlBST0NFRFVSRSIpCgkJJHRhYmxlWyR0eF0tPmx2bCA9ICRsZXY7CgoJcmV0dXJuICR0eDsKfQoKLy8gcHJpbnRzIHRoZSBzeW1ib2wgdGFibGUgKGZvciBkZWJ1Z2dpbmcpCmZ1bmN0aW9uIHByaW50X3N0KCR0eCkKewoJZ2xvYmFsICR0YWJsZTsKCgllY2hvICJcdE5BTUVcdEtJTkRcdFx0TEVWRUxcdEFERFJFU1NcdFZBTFVFXG4iOwoJZm9yICgkaSA9IDA7ICRpIDw9ICR0eDsgJGkrKykKCQllY2hvICIkaVx0IiAuICR0YWJsZVskaV0tPm5hbWUgLiAiXHQiIC4gJHRhYmxlWyRpXS0+a2luZCAuICJcdCIgLiAkdGFibGVbJGldLT5sdmwgLiAiXHQiIC4gJHRhYmxlWyRpXS0+YWRkciAuICJcdCIgLiAkdGFibGVbJGldLT52YWwgLiAiXG4iOwp9CgovLyBsaXN0cyB0aGUgY29kZSBmb3IgdGhlIGJsb2NrCmZ1bmN0aW9uIGxpc3Rjb2RlKCRjb2RlaW54MCkKewoJZ2xvYmFsICRjb2RlaW54LCAkY29kZSwgJHdlYjsKCglmb3IgKCRpID0gJGNvZGVpbngwOyAkaSA8ICRjb2RlaW54OyAkaSsrKQoJCWVjaG8gIiRpICRjb2RlWyRpXSIgLiAoJHdlYiA/ICI8YnIvPlxuIiA6ICJcbiIpOwoJZWNobyAiXG4iOwp9CgovLyBnZW5lcmF0ZXMgYSBwLWNvZGUgaW5zdHJ1Y3Rpb24KZnVuY3Rpb24gZ2VuKCRpLCAkbCwgJGEpCnsKCWdsb2JhbCAkY29kZWlueCwgJGNvZGU7CgoJJGNvZGVbJGNvZGVpbnhdID0gbmV3IGluc3RyKCRpLCAkbCwgJGEpOwoJJGNvZGVpbngrKzsKfQoKLy8gZGVjbGFyZXMgY29uc3RhbnRzIChhbmQgZW50ZXJzIGluIHN5bWJvbCB0YWJsZSkKZnVuY3Rpb24gY29uc3RkZWNsYXJhdGlvbigkdHgsICRsZXYsICYkZHgpCnsKCWdsb2JhbCAkc3ltOwoKCWlmICgkc3ltICE9ICJJREVOVCIpCgkJZXJyb3IoMSk7CglnZXRzeW0oKTsKCWlmICgkc3ltICE9ICJFUVVBTCIpCgkJZXJyb3IoMik7CglnZXRzeW0oKTsKCWlmICgkc3ltICE9ICJOVU1CRVIiKQoJCWVycm9yKDMpOwoJZ2V0c3ltKCk7CgkkdHggPSBlbnRlcigiQ09OU1RBTlQiLCAkdHgsICRsZXYsICRkeCk7CgoJcmV0dXJuICR0eDsKfQoKLy8gZGVjbGFyZXMgdmFyaWFibGVzIChhbmQgZW50ZXJzIGluIHN5bWJvbCB0YWJsZSkKZnVuY3Rpb24gdmFyZGVjbGFyYXRpb24oJHR4LCAkbGV2LCAmJGR4KQp7CglnbG9iYWwgJHN5bTsKCglpZiAoJHN5bSAhPSAiSURFTlQiKQoJCWVycm9yKDEpOwoJZ2V0c3ltKCk7CgkkdHggPSBlbnRlcigiVkFSSUFCTEUiLCAkdHgsICRsZXYsICRkeCk7CgoJcmV0dXJuICR0eDsKfQoKLy8gaGFuZGxlcyBlcnJvcnMKZnVuY3Rpb24gZXJyb3IoJGkpCnsKCWdsb2JhbCAkc3ltLCAkaWQ7CgoJc3dpdGNoICgkaSkKCXsKCQljYXNlIDE6CgkJCWVjaG8gIklkZW50aWZpZXIgZXhwZWN0ZWQiOwoJCQlicmVhazsKCQljYXNlIDI6CgkJCWVjaG8gIkVRVUFMICg9KSBleHBlY3RlZCI7CgkJCWJyZWFrOwoJCWNhc2UgMzoKCQkJZWNobyAiTlVNQkVSIGV4cGVjdGVkIjsKCQkJYnJlYWs7CgkJY2FzZSA0OgoJCQllY2hvICJTRU1JQ09MT04gKDspIGV4cGVjdGVkIjsKCQkJYnJlYWs7CgkJY2FzZSA1OgoJCQllY2hvICJQRVJJT0QgKC4pIGV4cGVjdGVkIjsKCQkJYnJlYWs7CgkJY2FzZSA2OgoJCQllY2hvICJCRUNPTUVTICg6PSkgZXhwZWN0ZWQiOwoJCQlicmVhazsKCQljYXNlIDc6CgkJCWVjaG8gIkVORCBleHBlY3RlZCI7CgkJCWJyZWFrOwoJCWNhc2UgODoKCQkJZWNobyAiVEhFTiBleHBlY3RlZCI7CgkJCWJyZWFrOwoJCWNhc2UgOToKCQkJZWNobyAiRE8gZXhwZWN0ZWQiOwoJCQlicmVhazsKCQljYXNlIDEwOgoJCQllY2hvICJSZWxhdGlvbmFsIG9wZXJhdG9yIGV4cGVjdGVkIjsKCQkJYnJlYWs7CgkJY2FzZSAxMToKCQkJZWNobyAiUlBBUkVOICgoKSBleHBlY3RlZCI7CgkJCWJyZWFrOwoJCWNhc2UgMTI6CgkJCWVjaG8gIkFuIGV4cHJlc3Npb24gY2Fubm90IGJlZ2luIHdpdGggdGhpcyBzeW1ib2wiOwoJCQlicmVhazsKCQljYXNlIDEzOgoJCQllY2hvICJVbmRlY2xhcmVkIGlkZW50aWZpZXIiOwoJCQlicmVhazsKCQljYXNlIDE0OgoJCQllY2hvICJBc3NpZ25tZW50IHRvIENPTlNUQU5UIG9yIFBST0NFRFVSRSBub3QgYWxsb3dlZCI7CgkJCWJyZWFrOwoJCWNhc2UgMTU6CgkJCWVjaG8gIkNBTEwgbXVzdCBiZSBmb2xsb3dlZCBieSBhIFBST0NFRFVSRSI7CgkJCWJyZWFrOwoJCWNhc2UgMTY6CgkJCWVjaG8gIlVOVElMIGV4cGVjdGVkIjsKCQkJYnJlYWs7CgkJY2FzZSAxNzoKCQkJZWNobyAiTFBBUkVOICgoKSBleHBlY3RlZCI7CgkJCWJyZWFrOwoJCWNhc2UgMTg6CgkJCWVjaG8gIlRPIG9yIERPV05UTyBleHBlY3RlZCI7CgkJCWJyZWFrOwoJCWNhc2UgMTk6CgkJCWVjaG8gIk5VTUJFUiBvciBDT05TVEFOVCBleHBlY3RlZCI7CgkJCWJyZWFrOwoJCWNhc2UgMjA6CgkJCWVjaG8gIkNPTE9OICg6KSBleHBlY3RlZCI7CgkJCWJyZWFrOwoJCWNhc2UgMjE6CgkJCWVjaG8gIk9GIGV4cGVjdGVkIjsKCQkJYnJlYWs7CgkJY2FzZSAyMjoKCQkJZWNobyAiQ0VORCBleHBlY3RlZCI7CgkJCWJyZWFrOwoJCWRlZmF1bHQ6CgkJCWVjaG8gIlVua25vd24gZXJyb3IhIjsKCQkJYnJlYWs7Cgl9CgoJZWNobyAiOyBnb3QgJHN5bSIgLiAoJHN5bSA9PSAiSURFTlQiID8gIiAoJGlkKSIgOiAiIikgLiAiXG4iOwoKCWV4aXQ7Cn0KCi8vIEdFVFNZTQpmdW5jdGlvbiBnZXRzeW0oKQp7CglnbG9iYWwgJHN5bSwgJGNoLCAkaWQsICRyd29yZHMsICRvcHMsICRvcHNfbmFtZXMsICRsaW5lLCAkdmFsdWUsICRubWF4LCAkYWw7CgoJJHN5bSA9IGZhbHNlOwoJLy8gc2tpcCB3aGl0ZXNwYWNlCgl3aGlsZSAoIWlzc2V0KCRjaCkgfHwgJGNoID09ICIiIHx8ICRjaCA9PSAiICIgfHwgJGNoID09ICJcciIgfHwgJGNoID09ICJcbiIpCgl7CgkJaWYgKCRjaCA9PT0gZmFsc2UpCgkJCXJldHVybjsKCQlnZXRjaGFyKCk7Cgl9CgoJLy8gd2UgZWl0aGVyIGhhdmUgYW4gaWRlbnRpZmllciBvciBhIHJlc2VydmVkIHdvcmQKCWlmICgkY2ggPj0gIkEiICYmICRjaCA8PSAiWiIpCgl7CgkJJHRvayA9ICRjaDsKCQlnZXRjaGFyKCk7CgkJd2hpbGUgKCgkY2ggPj0gIkEiICYmICRjaCA8PSAiWiIpIHx8ICgkY2ggPj0gIjAiICYmICRjaCA8PSAiOSIpKQoJCXsKCQkJaWYgKHN0cmxlbigkdG9rKSA9PSAkYWwpCgkJCQllcnJvcigpOwoKCQkJJHRvayAuPSAkY2g7CgkJCWlmICgkbGluZSA9PSAiIikKCQkJewoJCQkJZ2V0Y2hhcigpOwoJCQkJYnJlYWs7CgkJCX0KCQkJZ2V0Y2hhcigpOwoJCX0KCgkJLy8gYXNzdW1lIGFuIGlkZW50aWZpZXIgYnV0IHNlYXJjaCB0aHJvdWdoIHJlc2VydmVkIHdvcmRzCgkJJHN5bSA9ICJJREVOVCI7CgkJLy8gd2UgY291bGQgdXNlIG5vcncgaGVyZSBhcyBzZW50aW5lbCwgYnV0IHdoeT8KCQlmb3JlYWNoKCRyd29yZHMgYXMgJHJ3b3JkKQoJCXsKCQkJaWYgKCR0b2sgPT0gJHJ3b3JkKQoJCQl7CgkJCQkkc3ltID0gJHJ3b3JkOwoJCQkJYnJlYWs7CgkJCX0KCQl9CgkJaWYgKCRzeW0gPT0gIklERU5UIikKCQkJJGlkID0gJHRvazsKCX0KCS8vIHdlIGhhdmUgYSBudW1iZXIKCWVsc2UgaWYgKCRjaCA+PSAiMCIgJiYgJGNoIDw9ICI5IikKCXsKCQkkdG9rID0gJGNoOwoJCWdldGNoYXIoKTsKCQl3aGlsZSAoJGNoID49ICIwIiAmJiAkY2ggPD0gIjkiKQoJCXsKCQkJaWYgKHN0cmxlbigkdG9rKSA9PSAkbm1heCkKCQkJCWVycm9yKCk7CgoJCQkkdG9rIC49ICRjaDsKCQkJZ2V0Y2hhcigpOwoJCX0KCgkJJHN5bSA9ICJOVU1CRVIiOwoJCSR2YWx1ZSA9ICR0b2s7Cgl9CgkvLyB3ZSBoYXZlIHNvbWUgb3RoZXIgc3ltYm9sCgllbHNlCgl7CgkJLy8gcGVyaGFwcyA6IG9yIDo9CgkJaWYgKCRjaCA9PSAiOiIpCgkJewoJCQkkdG9rID0gJGNoOwoJCQlnZXRjaGFyKCk7CgkJCWlmICgkY2ggPT0gIj0iKQoJCQl7CgkJCQkkdG9rIC49ICRjaDsKCQkJCWdldGNoYXIoKTsKCQkJfQoJCX0KCQkvLyBtYXliZSA8LCA8Piwgb3IgPD0KCQllbHNlIGlmICgkY2ggPT0gIjwiKQoJCXsKCQkJJHRvayA9ICRjaDsKCQkJZ2V0Y2hhcigpOwoJCQlpZiAoJGNoID09ICI+IiB8fCAkY2ggPT0gIj0iKQoJCQl7CgkJCQkkdG9rIC49ICRjaDsKCQkJCWdldGNoYXIoKTsKCQkJfQoJCX0KCQkvLyA+IG9yID49CgkJZWxzZSBpZiAoJGNoID09ICI+IikKCQl7CgkJCSR0b2sgPSAkY2g7CgkJCWdldGNoYXIoKTsKCQkJaWYgKCRjaCA9PSAiPSIpCgkJCXsKCQkJCSR0b2sgLj0gJGNoOwoJCQkJZ2V0Y2hhcigpOwoJCQl9CgkJfQoJCS8vIHNvbWUgb3RoZXIgc2luZ2xlLWNoYXJhY3RlciBzeW1ib2wKCQllbHNlCgkJewoJCQkkdG9rID0gJGNoOwoJCQlnZXRjaGFyKCk7CgkJfQoKCQkvLyBtYXRjaCB0aGUgc3ltYm9sIGFuZCBnZXQgaXRzIG5hbWUKCQlmb3IgKCRpID0gMDsgJGkgPCBzaXplb2YoJG9wcyk7ICRpKyspCgkJewoJCQlpZiAoJHRvayA9PSAkb3BzWyRpXSkKCQkJewoJCQkJJHN5bSA9ICRvcHNfbmFtZXNbJGldOwoJCQkJYnJlYWs7CgkJCX0KCQl9Cgl9Cn0KCi8vIEdFVENIQVIKZnVuY3Rpb24gZ2V0Y2hhcigpCnsKCWdsb2JhbCAkbGluZSwgJGNoOwoKCS8vIGlmIHdlIGRvbid0IHlldCBoYXZlIGEgbGluZSwgZ2V0IG9uZQoJaWYgKCRsaW5lID09ICIiKQoJewoJCS8vIGlmIHdlJ3JlIGF0IHRoZSBlbmQgb2YgaW5wdXQsIGdldCBvdXQKCQlpZiAoZ2V0bGluZSgpID09PSBmYWxzZSkKCQl7CgkJCSRjaCA9IGZhbHNlOwoJCQlyZXR1cm47CgkJfQoJfQoKCS8vIGdldCB0aGUgY3VycmVudCBjaGFyYWN0ZXIgYW5kIHJlbWFpbmRlciBvZiB0aGUgbGluZQoJJGNoID0gJGxpbmVbMF07CgkkbGluZSA9IHN1YnN0cigkbGluZSwgMSk7Cn0KCi8vIEdFVExJTkUKZnVuY3Rpb24gZ2V0bGluZSgpCnsKCWdsb2JhbCAkbGluZSwgJGRhdGEsICRpLCAkd2ViOwoKCS8vIGlmIHdlJ3JlIGF0IHRoZSBlbmQgb2YgaW5wdXQsIGdldCBvdXQKCWlmICgkaSA9PSBzaXplb2YoJGRhdGEpKQoJCXJldHVybiBmYWxzZTsKCgkvLyBnZXQgdGhlIG5leHQgbGluZQoJJGxpbmUgPSAkZGF0YVskaSsrXTsKCWVjaG8gKCR3ZWIgPyAoc3RyX3JlcGxhY2UoIiAiLCAiJm5ic3A7IiwgJGxpbmUpIC4gIjxici8+XG4iKSA6ICIkbGluZVxuIik7Cn0KCmZ1bmN0aW9uIHByaW50X3N0YWNrKCRzLCAkYiwgJHQpCnsKCWZvciAoJGkgPSAkdDsgJGkgPiAwOyAkaS0tKQoJewoJCSRzdCA9ICIkaTogJHNbJGldIjsKCQlpZiAoJGIgPT0gJGkpCgkJCSRzdCAuPSAiXHRCIjsKCQlpZiAoJHQgPT0gJGkpCgkJCSRzdCAuPSAiXHRUIjsKCQllY2hvICIkc3RcbiI7Cgl9Cn0KCmZ1bmN0aW9uIGJhc2UoJGwsICRiLCAkcykKewoJd2hpbGUgKCRsID4gMCkKCXsKCQkkYiA9ICRzWyRiXTsKCQkkbC0tOwoJfQoKCXJldHVybiAkYjsKfQoKZnVuY3Rpb24gaW50ZXJwcmV0KCkKewoJZ2xvYmFsICR3ZWIsICRjb2RlOwoKCWVjaG8gIlN0YXJ0IFBMLzAiIC4gKCR3ZWIgPyAiPGJyLz5cbiIgOiAiXG4iKTsKCSR0ID0gMDsKCSRiID0gMTsKCSRwID0gMDsKCSRzWzFdID0gMDsKCSRzWzJdID0gMDsKCSRzWzNdID0gMDsKCgl3aGlsZSAodHJ1ZSkKCXsKCQkkaSA9ICRjb2RlWyRwXS0+aTsKCQkkbCA9ICRjb2RlWyRwXS0+bDsKCQkkYSA9ICRjb2RlWyRwXS0+YTsKIwkJZWNobyAiJHA6ICRpICRsICRhXG4iOwoJCSRwKys7CgoJCXN3aXRjaCAoJGkpCgkJewoJCQljYXNlICJMSVQiOgoJCQkJJHQrKzsKCQkJCSRzWyR0XSA9ICRhOwoJCQkJYnJlYWs7CgkJCWNhc2UgIk9QUiI6CgkJCQlzd2l0Y2ggKCRhKQoJCQkJewoJCQkJCWNhc2UgMDoKCQkJCQkJJHQgPSAkYiAtIDE7CgkJCQkJCSRwID0gJHNbJHQrM107CgkJCQkJCSRiID0gJHNbJHQrMl07CgkJCQkJCWJyZWFrOwoJCQkJCWNhc2UgMToKCQkJCQkJJHNbJHRdID0gJHNbJHRdICogLTE7CgkJCQkJCWJyZWFrOwoJCQkJCWNhc2UgMjoKCQkJCQkJJHQtLTsKCQkJCQkJJHNbJHRdID0gJHNbJHRdICsgJHNbJHQrMV07CgkJCQkJCWJyZWFrOwoJCQkJCWNhc2UgMzoKCQkJCQkJJHQtLTsKCQkJCQkJJHNbJHRdID0gJHNbJHRdIC0gJHNbJHQrMV07CgkJCQkJCWJyZWFrOwoJCQkJCWNhc2UgNDoKCQkJCQkJJHQtLTsKCQkJCQkJJHNbJHRdID0gJHNbJHRdICogJHNbJHQrMV07CgkJCQkJCWJyZWFrOwoJCQkJCWNhc2UgNToKCQkJCQkJJHQtLTsKCQkJCQkJJHNbJHRdID0gKCRzWyR0XSAtICgkc1skdF0gJSAkc1skdCsxXSkpIC8gJHNbJHQrMV07CgkJCQkJCWJyZWFrOwoJCQkJCWNhc2UgNjoKCQkJCQkJJHNbJHRdID0gKCRzWyR0XSAlIDIgPT0gMCA/IDAgOiAxKTsKCQkJCQkJYnJlYWs7CgkJCQkJY2FzZSA4OgoJCQkJCQkkdC0tOwoJCQkJCQkkc1skdF0gPSAoJHNbJHRdID09ICRzWyR0KzFdID8gMSA6IDApOwoJCQkJCQlicmVhazsKCQkJCQljYXNlIDk6CgkJCQkJCSR0LS07CgkJCQkJCSRzWyR0XSA9ICgkc1skdF0gIT0gJHNbJHQrMV0gPyAxIDogMCk7CgkJCQkJCWJyZWFrOwoJCQkJCWNhc2UgMTA6CgkJCQkJCSR0LS07CgkJCQkJCSRzWyR0XSA9ICgkc1skdF0gPCAkc1skdCsxXSA/IDEgOiAwKTsKCQkJCQkJYnJlYWs7CgkJCQkJY2FzZSAxMToKCQkJCQkJJHQtLTsKCQkJCQkJJHNbJHRdID0gKCRzWyR0XSA+PSAkc1skdCsxXSA/IDEgOiAwKTsKCQkJCQkJYnJlYWs7CgkJCQkJY2FzZSAxMjoKCQkJCQkJJHQtLTsKCQkJCQkJJHNbJHRdID0gKCRzWyR0XSA+ICRzWyR0KzFdID8gMSA6IDApOwoJCQkJCQlicmVhazsKCQkJCQljYXNlIDEzOgoJCQkJCQkkdC0tOwoJCQkJCQkkc1skdF0gPSAoJHNbJHRdIDw9ICRzWyR0KzFdID8gMSA6IDApOwoJCQkJCQlicmVhazsKCQkJCQljYXNlIDE0OgoJCQkJCQllY2hvICIkc1skdF0gIjsKCQkJCQkJJHQtLTsKCQkJCQkJYnJlYWs7CgkJCQkJY2FzZSAxNToKCQkJCQkJZWNobyAoJHdlYiA/ICI8YnIvPlxuIiA6ICJcbiIpOwoJCQkJCQlicmVhazsKCQkJCQlkZWZhdWx0OgoJCQkJCQllY2hvICIhIUhPVVNUT04sIFdFIEhBVkUgQSBQUk9CTEVNISEiIC4gKCR3ZWIgPyAiPGJyLz5cbiIgOiAiXG4iKTsKCQkJCQkJYnJlYWs7CgkJCQl9CgkJCQlicmVhazsKCQkJY2FzZSAiTE9EIjoKCQkJCSR0Kys7CgkJCQkkc1skdF0gPSAkc1tiYXNlKCRsLCAkYiwgJHMpICsgJGFdOwoJCQkJYnJlYWs7CgkJCWNhc2UgIlNUTyI6CgkJCQkkc1tiYXNlKCRsLCAkYiwgJHMpICsgJGFdID0gJHNbJHRdOwoJCQkJJHQtLTsKCQkJCWJyZWFrOwoJCQljYXNlICJDQUwiOgoJCQkJJHNbJHQrMV0gPSBiYXNlKCRsLCAkYiwgJHMpOwoJCQkJJHNbJHQrMl0gPSAkYjsKCQkJCSRzWyR0KzNdID0gJHA7CgkJCQkkYiA9ICR0ICsgMTsKCQkJCSRwID0gJGE7CgkJCQlicmVhazsKCQkJY2FzZSAiSU5UIjoKCQkJCSR0ICs9ICRhOwoJCQkJYnJlYWs7CgkJCWNhc2UgIkpNUCI6CgkJCQkkcCA9ICRhOwoJCQkJYnJlYWs7CgkJCWNhc2UgIkpQQyI6CgkJCQlpZiAoJHNbJHRdID09ICRsKQoJCQkJCSRwID0gJGE7CgkJCQkkdC0tOwoJCQkJYnJlYWs7CgkJfQoKIwkJcHJpbnRfc3RhY2soJHMsICRiLCAkdCk7CgkJaWYgKCRwID09IDApCgkJCWJyZWFrOwoJfQoKCWVjaG8gIkVuZCBQTC8wIiAuICgkd2ViID8gIjxici8+XG4iIDogIlxuIik7Cn0KCi8vIGRpc3BsYXkgdGhlIHdlYiBpbnRlcmZhY2UgKHByb21wdCBmb3Igc291cmNlIGZpbGUpCmZ1bmN0aW9uIHNob3dfaW50ZmMoKQp7CgllY2hvIDw8PCBFTkQKPGh0bWw+Cgk8Ym9keT4KCQlQTC8wIENvbXBpbGVyK0ludGVycHJldGVyPGJyLz4KCQl2MS4wIEJldGE8YnIvPgoJCTIwMDktMDQtMTEgKGxhc3QgbW9kOiAyMDA5LTA0LTExKTxwLz4KCgkJPGZvcm0gbWV0aG9kPSJwb3N0IiBlbmN0eXBlPSJtdWx0aXBhcnQvZm9ybS1kYXRhIj4KCQkJPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iZGF0YSIvPgoJCQlQTC8wIHNvdXJjZSBmaWxlOiA8aW5wdXQgdHlwZT0iZmlsZSIgbmFtZT0iZmlsZSIgc2l6ZT0iODAiLz48cC8+CgkJCTxpbnB1dCB0eXBlPSJzdWJtaXQiIHZhbHVlPSJDb21waWxlISIvPgoJCTwvZm9ybT48cC8+Cgk8L2JvZHk+CjwvaHRtbD4KRU5EOwp9CgovLyBNQUlOCi8vIGlmIHdlJ3JlIGFjY2Vzc2VkIHZpYSB0aGUgd2ViLCB3ZSBnZXQgaW5wdXQgdGhyb3VnaCBhIGZvcm0gcG9zdAppZiAoJHdlYikKewoJLy8gaWYgd2UndmUgZ290IHRoZSBzb3VyY2UgKHZpYSBmb3JtIHN1Ym1pc3Npb24pCglpZiAoaXNzZXQoJF9SRVFVRVNUWyJkYXRhIl0pICYmICRfRklMRVNbImZpbGUiXVsibmFtZSJdICE9ICIiKQoJewoJCS8vIGFuZCBpZiB0aGUgZmlsZSBleGlzdHMgb24gbG9jYWwgZGlzawoJCWlmIChmaWxlX2V4aXN0cygkX0ZJTEVTWyJmaWxlIl1bInRtcF9uYW1lIl0pKQoJCXsKCQkJLy8gZ2V0IGl0cyBjb250ZW50cyBhbmQgc3BsaXQgaW50byBsaW5lcwoJCQkkZmlsZSA9IHN0cl9yZXBsYWNlKCJcciIsICIiLCBmaWxlX2dldF9jb250ZW50cygkX0ZJTEVTWyJmaWxlIl1bInRtcF9uYW1lIl0pKTsKCQkJJGRhdGEgPSBzcGxpdCgiXG4iLCAkZmlsZSk7CgkJfQoJCS8vIG90aGVyd2lzZSwgYmFpbCEKCQllbHNlCgkJewoJCQllY2hvICJFUlJPUjogVW5hYmxlIHRvIG9wZW4gIiAuICRfRklMRVNbImZpbGUiXVsibmFtZSJdIC4gIjxici8+XG4iOwoJCQlleGl0OwoJCX0KCX0KCS8vIG5vIHN1Ym1pdHRlZCBmaWxlPyAgZGlzcGxheSB0aGUgZm9ybQoJZWxzZQoJewoJCXNob3dfaW50ZmMoKTsKCQlleGl0OwoJfQp9Ci8vIG90aGVyd2lzZSwgZ2V0IGlucHV0IGZyb20gc3RkaW4KZWxzZQp7CgkvLyBnZXQgdGhlIGlucHV0IGFuZCBzcGxpdCBpbnRvIGxpbmVzCgkkZmlsZSA9IHN0cl9yZXBsYWNlKCJcciIsICIiLCBmaWxlX2dldF9jb250ZW50cygicGhwOi8vc3RkaW4iKSk7CgkvLyRkYXRhID0gc3BsaXQoIlxuIiwgJGZpbGUpOwoJCgkvL1JFUExBQ0VEIERFUFJFQ0FURUQgU1BMSVQgV0lUSCBORVcgRVhQTE9ERQoJJGRhdGEgPSBleHBsb2RlKCJcbiIsICRmaWxlKTsKfQoKLy8gUFJPR1JBTQpnZXRzeW0oKTsKYmxvY2soMCwgMCk7CmlmICgkc3ltICE9ICJQRVJJT0QiKQoJZXJyb3IoNSk7CgplY2hvICJTdWNjZXNzZnVsIGNvbXBpbGF0aW9uISIgLiAoJHdlYiA/ICI8cC8+XG4iIDogIlxuXG4iKTsKaW50ZXJwcmV0KCk7Cgo/Pgo=