import tango.core.Exception;
import tango.io.Stdout;
import tango.io.Conduit;
import tango.io.MemoryConduit;
import tango.io.Console;
import tango.io.Print;
import tango.io.protocol.Writer;
import tango.text.stream.LineIterator;
import tango.text.convert.Integer;
import tango.text.convert.Layout;
import tango.text.Regex;
import tango.text.Util;
/*
Workaround for bug in Tango 0.99
see http://www.dsource.org/projects/tango/ticket/546
*/
ubyte
byteFromHex( char[] hex )
in
{
assert( hex.length == 1 || hex.length == 2 );
}
body
{
ubyte r = 0;
foreach( h; hex )
{
ubyte d = 0;
switch( h )
{
case '0': d = 0; break;
case '1': d = 1; break;
case '2': d = 2; break;
case '3': d = 3; break;
case '4': d = 4; break;
case '5': d = 5; break;
case '6': d = 6; break;
case '7': d = 7; break;
case '8': d = 8; break;
case '9': d = 9; break;
case 'a', 'A': d = 10; break;
case 'b', 'B': d = 11; break;
case 'c', 'C': d = 12; break;
case 'd', 'D': d = 13; break;
case 'e', 'E': d = 14; break;
case 'f', 'F': d = 15; break;
}
r = r * 16 + d;
}
return r;
}
unittest
{
assert( 0 == byteFromHex( "00" ) );
assert( 0 == byteFromHex( "0" ) );
assert( 1 == byteFromHex( "1" ) );
assert( 1 == byteFromHex( "01" ) );
assert( 0xb == byteFromHex( "b" ) );
assert( 0xb == byteFromHex( "B" ) );
assert( 0xb == byteFromHex( "0B" ) );
assert( 0xb == byteFromHex( "0b" ) );
}
ubyte[]
stringImageToBinary( char[] stringImage )
{
char[]
convert( char[] image )
{
char[] result;
for( uint i = 0; i != image.length; ++i )
{
auto ch = image[ i ];
if( '\\' == ch )
{
++i;
ch = image[ i ];
switch( ch )
{
case '\\', '"', '|', '{', '}': break;
case 'n' : ch = '\n'; break;
case 'r' : ch = '\r'; break;
case 't' : ch = '\t'; break;
case 'x' :
char[] hexCode = [
image[ i + 1 ],
image[ i + 2 ] ];
i += 2;
ch = cast(char) byteFromHex( hexCode );
break;
}
}
result ~= ch;
}
return result;
}
return cast(ubyte[])( convert( convert( stringImage ) ) );
}
ubyte[]
hexImageToBinary( char[] hexImage )
{
ubyte[] result;
foreach( hex; delimit( hexImage, " " ) )
result ~= byteFromHex( hex );
return result;
}
char[]
makeHexTextRepresentation( ubyte[] binImage )
{
auto resultBuffer = new MemoryConduit;
auto print = new Print!(char)( new Layout!(char), resultBuffer );
for( uint i = 0; i != binImage.length; )
{
print.format( "{0,2:X2}", binImage[ i ] );
if( ++i < binImage.length )
print.print( ' ' );
}
return cast(char[]) resultBuffer.slice;
}
unittest
{
auto r = stringImageToBinary( `\x0b\x0a\x03` );
assert( r == [ cast(ubyte)0x0b, 0x0a, 0x03 ] );
}
class AnalyzingResult
{
public :
abstract bool isGoodMessage();
abstract char[] toUtf8();
}
class HeaderElement
{
public :
this(
ubyte id,
ubyte[] data )
{
id_ = id;
data_ = data;
}
ubyte id() { return id_; }
ubyte[] data() { return data_; }
private :
ubyte id_;
ubyte[] data_;
}
class SuccessfulResult : AnalyzingResult
{
public :
this()
{
headerElements_ = [];
message_ = [];
}
this(
HeaderElement[] headerElements,
ubyte[] message )
{
headerElements_ = headerElements;
message_ = message;
}
override bool isGoodMessage() { return true; }
override char[] toUtf8()
{
auto resultBuffer = new MemoryConduit;
auto print = new Print!(char)( new Layout!(char), resultBuffer );
if( headerElements_.length )
foreach( element; headerElements_ )
print.format( "Element: Id: {0:X}, Data: {1}; ",
element.id,
makeHexTextRepresentation( element.data ) );
print.format( "Message: {0}",
makeHexTextRepresentation( message_ ) );
return cast(char[]) resultBuffer.slice;
}
private :
HeaderElement[] headerElements_;
ubyte[] message_;
}
class BadResult : AnalyzingResult
{
public :
this(
char[] reason,
ubyte[] fullBody )
{
reason_ = reason;
fullBody_ = fullBody;
}
override bool isGoodMessage() { return false; }
override char[] toUtf8()
{
auto resultBuffer = new MemoryConduit;
auto print = new Print!(char)( new Layout!(char), resultBuffer );
print.format( "What: {0}; Full body: {1}",
reason_,
makeHexTextRepresentation( fullBody_ ) );
return cast(char[]) resultBuffer.slice;
}
private :
char[] reason_;
ubyte[] fullBody_;
}
AnalyzingResult
tryHandleHeadersAndBody( ubyte[] binImage )
{
if( !binImage.length )
return new SuccessfulResult;
Layout!(char) print = null;
Layout!(char) printer()
{
if( print is null )
print = new Layout!(char);
return print;
}
ubyte headerLength = binImage[ 0 ];
if( !headerLength )
return new BadResult(
"Zero header length",
binImage );
if( headerLength >= binImage.length )
return new BadResult(
printer.convert(
"Header length too big! Length: {0}, Body length: {1}",
headerLength,
binImage.length ),
binImage );
HeaderElement[] elements;
for( int i = 1; i + 2 < headerLength; )
{
ubyte elementId = binImage[ i++ ];
ubyte elementSize = binImage[ i++ ];
if( elementSize <= headerLength + 1 - i )
elements ~= new HeaderElement(
elementId,
binImage[ i..(i + elementSize) ].dup );
else
return new BadResult(
printer.convert(
"Invalid element size! Position={0}, "
"HeaderLength={1}, ElementSize:{2}",
i,
headerLength,
elementSize ),
binImage );
i += elementSize;
}
ubyte[] message = binImage[ (1 + headerLength)..$ ];
foreach( b; message )
if( b & 0x80 )
return new BadResult(
printer.convert(
"Byte with non-zero highest bit in message! Byte: {0:X}", b ),
binImage );
return new SuccessfulResult(
elements,
binImage[ (1 + headerLength)..$ ].dup );
}
void
showBadMessage( char[] line, AnalyzingResult analyzingResult )
{
auto additionalDataMatch = search( line,
`^LOG \[([^]]+)\].*{source_addr "([^"]+)" }.*{dest_addr "([^"]+)" }` );
Stdout.formatln( "ERROR -- at: [{0}]; from '{1}'; to '{2}'; {3}",
additionalDataMatch.match( 1 ),
additionalDataMatch.match( 2 ),
additionalDataMatch.match( 3 ),
analyzingResult );
}
ubyte[]
tryExtractShortMessageBody( char[] line )
{
auto shortMessageFieldMatch = search( line, `{short_message "(.+)" }` );
if( shortMessageFieldMatch !is null )
return stringImageToBinary( shortMessageFieldMatch.match( 1 ) );
auto tlvMessagePayloadMatch = search( line,
`{tlv \{t 0x424} \{l 0x([0-9A-Fa-f]+)} \{v ([^}]+)}` );
if( tlvMessagePayloadMatch !is null )
return hexImageToBinary( tlvMessagePayloadMatch.match( 2 ) );
return null;
}
void
parseLine( char[] line, bool showGoodMessages )
{
if( line.length <= 2 )
return;
auto binImage = tryExtractShortMessageBody( line );
if( binImage !is null )
{
auto analyzingResult = tryHandleHeadersAndBody( binImage );
if( analyzingResult.isGoodMessage() )
{
if( showGoodMessages )
Stdout.formatln( "OK: {0}", analyzingResult );
}
else
showBadMessage( line, analyzingResult );
}
else
Stderr.formatln( "nothing found in {0}", line );
}
void
readLines( bool showGoodMessages )
{
foreach( line; new LineIterator!(char)( Cin.stream ) )
parseLine( line, showGoodMessages );
}
struct Options
{
bool showGoodMessages_ = false;
}
Options
processArgs( char[][] args )
{
Options result;
foreach( a; args )
{
if( "--show-good" == a )
result.showGoodMessages_ = true;
else
throw new IllegalArgumentException(
"unsupported agrument: " ~ a );
}
return result;
}
void
main( char[][] args )
{
try
{
auto options = processArgs( args[ 1..$ ] );
readLines( options.showGoodMessages_ );
}
catch( Exception x )
{
Stderr.formatln( "*** Exception: {0}", x );
}
}
// vim:ts=2:sts=2:sw=2:expandtab: