import java.io.*;
import java.util.regex.*;
import nice.getopt;

let Pattern shortMessageFieldRegex = Pattern.compile(
    "\\{short_message \"(.+)\" \\}" );
let Pattern messagePayloadTlvRegex = Pattern.compile(
    "\\{tlv \\{t 0x424\\} \\{l 0x([0-9A-Fa-f]+)\\} \\{v ([^}]+)\\}" );
let Pattern additionalDataRegex = Pattern.compile(
    "^LOG \\[([^]]+)\\].*\\{source_addr \"([^\"]+)\" \\}.*\\{dest_addr \"([^\"]+)\" }" );

int getByte( byte v ) = int(v) & 0xff;

byte[]
stringImageToBinary( String line )
  {
    String unescapeString( String what )
      {
        (char, int) specialCharHandler( char ch, int index, String where )
          {
            char charFromHex( String hex ) = char(Integer.parseInt( hex, 16 ));

            if'n' == ch )
              return ('\n', index);
            else if'r' == ch )
              return ('\r', index);
            else if't' == ch )
              return ('\t', index);
            else if'x' == ch )
              return ( charFromHex(
                  where.substring( index + 1, index + 3 ) ), index + 2 );
            else
              return (ch, index);
          }

        let result = new StringBuilder( what.length() );
        forvar i = 0; i != what.length(); ++i )
          {
            var c = what.charAt( i );
            if'\\' == c )
              (c, i) = specialCharHandler( what.charAt( i + 1 ), i + 1, what );

            result.append( c );
          }
        return result.toString();
      }

    return unescapeString( unescapeString( line ) ).getBytes();
  }

byte[]
hexImageToBinary( String line )
  {
    let hexSubstrings = line.split( " " );
    let result = new byte[ hexSubstrings.length ];
    for( i : 0..(hexSubstrings.length - 1) )
      result[ i ] = byte(Integer.parseInt( hexSubstrings[ i ], 16 ));

    return result;
  }

?(byte[])
tryExtractShortMessageBody( String line )
  {
    let shortMessageFieldMatch = shortMessageFieldRegex.matcher( line );
    if( shortMessageFieldMatch.find() )
      return stringImageToBinary( shortMessageFieldMatch.group( 1 ) );

    let messagePayloadMatch = messagePayloadTlvRegex.matcher( line );
    if( messagePayloadMatch.find() )
      return hexImageToBinary( messagePayloadMatch.group( 2 ) );

    return null;
  }

String
formatHelper( PrintStream -> void formatter )
  {
    let buffer = new ByteArrayOutputStream();
    let printer = new PrintStream( buffer );
    formatter( printer );

    return buffer.toString();
  }

String
printf( String formatString, Object[] args ) =
  formatHelper( PrintStream printer => printer.printf( formatString, args ) );

String
makeHexTextRepresentation( byte[] what )
  {
    return formatHelper( PrintStream printer =>
      {
        for( i : 0..(what.length - 1) )
          {
            let v = getByte( what[ i ] );
            printer.printf( ( 0 == i ? "%02X" : " %02X" ), [ v ] );
          }
      }
    );
  }

interface AnalyzingResult
  {
    boolean isGoodMessage();
  }

class HeaderElement
  {
    byte id;
    byte[] data;
  }

class SuccessfulResult implements AnalyzingResult
  {
    HeaderElement[] headerElements = [];
    byte[] message = [];

    isGoodMessage() = true;

    toString()
      {
        return formatHelper( PrintStream printer =>
          {
            for( element : headerElements )
              printer.printf( "Element: Id: %X, Data: %s; ",
                  [ element.id,
                    makeHexTextRepresentation( element.data ) ] );
            printer.printf( "Message: %s",
                [ makeHexTextRepresentation( message ) ] );
          }
        );
      }
  }

class BadResult implements AnalyzingResult
  {
    String reason;
    byte[] fullBody;

    isGoodMessage() = false;

    toString() =
        "What: " reason "; Full body: " makeHexTextRepresentation( fullBody );
  }

BadResult
makeBadResult( String reason, byte[] fullBody ) = new BadResult(
    reason: reason, fullBody: fullBody );

AnalyzingResult
tryHandleHeadersAndBody( byte[] binImage )
  {
    if0 == binImage.length )
      return new SuccessfulResult();

    int headerLength = getByte( binImage[ 0 ] );
    if0 == headerLength )
      return makeBadResult( "Zero header length", binImage );
    if( headerLength >= binImage.length )
      return makeBadResult(
          printf(
              "Header length too big! Length: %d, Body length: %d",
              [ headerLength, binImage.length ] ),
          binImage );

    List< HeaderElement > elements = new ArrayList();
    forint i = 1; i + 2 < headerLength; )
      {
        byte elementId = binImage[ i++ ];
        int elementSize = getByte( binImage[ i++ ] );
        if( elementSize <= headerLength + 1 - i )
          elements.add(
              new HeaderElement(
                  id: elementId,
                  data: binImage.slice( i, i + elementSize - 1 ) ) );
        else
          return makeBadResult(
              printf(
                  "Invalid element size! Position=%d, " +
                      "HeaderLength=%d, ElementSize:%d",
                  [ i, headerLength, elementSize ] ),
              binImage );

        i += elementSize;
      }

    byte[] message = binImage.slice( 1 + headerLength );
    for( b : message )
      if0 != ( b & 0x80 ) )
        return makeBadResult(
            printf(
                "Byte with non-zero highest bit in message! Byte: {0:X}",
                [ b ] ),
            binImage );

    return new SuccessfulResult(
        headerElements: elements.toArray(),
        message: message );
  }

void
showBadMessage( String line, AnalyzingResult analyzingResult )
  {
    let additionalDataMatch = additionalDataRegex.matcher( line );
    additionalDataMatch.find;
    println(
        printf( "ERROR -- at: [%s]; from '%s'; to '%s'; ",
            [ additionalDataMatch.group( 1 ),
              additionalDataMatch.group( 2 ),
              additionalDataMatch.group( 3 ) ] ) +
        analyzingResult );
  }

void
parseLine( String line, boolean showGoodMessages )
  {
    if( line.length() <= 2 )
      return;

    let binImage = tryExtractShortMessageBody( line );
    if( binImage != null )
      {
        let analyzingResult = tryHandleHeadersAndBody( binImage );
        if( analyzingResult.isGoodMessage() )
          {
            if( showGoodMessages )
              println( "OK: " analyzingResult );
          }
        else
          showBadMessage( line, analyzingResult );
      }
    else
      println( stderr, "nothing found in " line );
  }

void
readLines( boolean showGoodMessages )
  {
    let stdin = new BufferedReader( new InputStreamReader( System.in ) );
    var line = readLine( stdin );
    while( line != null )
      {
        parseLine( line, showGoodMessages );
        line = readLine( stdin );
      }
  }

class Options
  {
    boolean showGoodMessages = false;
  }

Options
processArgs( String[] args )
  {
    let result = new Options();
    let cmdLineOptions = [
        option(
            "show-good",
            "show good messages",
            () => result.showGoodMessages = true )
    ];

    parse( "decode_7bit_bodies", args, cmdLineOptions );
    return result;
  }

void
main( String[] args )
  {
    try
      {
        let options = processArgs( args );
        readLines( options.showGoodMessages );
      }
    catch( Exception x )
      {
        println( stderr, "*** Exception: " x );
        x.printStackTrace();
      }
  }

// vim:ft=nice:ts=2:sw=2:sts=2:expandtab

Hosted by uCoz