#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if defined(DEBUG)
unsigned int readFrom( void *p, int len, int how, FILE *fil )
{
  int rc( fread( p, len, how, fil ) );

  printf( "RD %2d (%2d) of %2d (%2d) - %d\n", rc, rc*len, how, how*len, errno );

  return rc;
}

unsigned int writeTo( void *p, int len, int how, FILE *fil )
{
  int rc( fwrite( p, len, how, fil ) );

  printf( "WR %2d (%2d) of %2d (%2d) - %d\n", rc, rc*len, how, how*len, errno );

  return rc;
}
#else
#define readFrom fread
#define writeTo fwrite
#endif

int writeNew( FILE *newFile,
              void *header, int sizeofHeader,
              void *elapsed = 0, int sizeofElapsed = 0 )
{
  int len( sizeofHeader+sizeofElapsed );
  char *p( (char *)alloca( len ) );

  memcpy( p, header, sizeofHeader );

  if( elapsed && sizeofElapsed )
    memcpy( p+sizeofHeader, elapsed, sizeofElapsed );

  return writeTo( p, 1, len, newFile )!=unsigned(len);
}

#pragma pack(1)

struct SessionInfoOld
{
  unsigned short zone, net, node, point;
  char type;
  char protocol;
  char crc;
  unsigned long elapsed;
  unsigned long netSend, netRecv,
                xmSend,  xmRecv,
                filesSend, filesRecv;
} sesInfoOld;

struct SessionInfoNew
{
  unsigned short zone, net, node, point;
  char type;
  char protocol;
  char crc;
  unsigned long elapsed;
  unsigned long netSend, netRecv,
           xmSend,  xmRecv,
           filesSend, filesRecv;
  char domain[17];
} sesInfoNew;

struct Header
{
  unsigned long stamp;
  unsigned char code;
} header;

struct Start
{
  unsigned long stamp;
  unsigned char ver[2];
};

#pragma pack()

int convert( FILE *oldFile, FILE *newFile )
{
  int logLines(0),
      rc(0);
  unsigned long elapsed;

  while( !feof(oldFile) && !ferror(oldFile) && !rc )
  {
    logLines++;

    int len( readFrom( &header, 1, sizeof header, oldFile ) );

    if( len!=sizeof header )
    {
      if( !len && feof( oldFile ) )
        break;

      rc = 4;
    }
    else
    {
      if( logLines==1 )
      {
        Start start=
        {
          header.stamp,
          { 254, 1 }
        };

        if( writeNew( newFile, &start, sizeof start ) )
        {
          rc = 5;
          break;
        }
      }

      switch( header.code )
      {
      case 0:			// Answer
      case 1:			// Dial
      case 2:			// terminal
      case 3:			// bbs
      case 4:			// external

        if( readFrom( &elapsed, 1, sizeof elapsed, oldFile )!=sizeof elapsed )
        {
          rc = 4;
          break;
        }

        if( writeNew( newFile,
                      &header, sizeof header,
                      &elapsed, sizeof elapsed ) )
          rc = 5;

        break;

      case 5:			// income session
      case 6:			// outcome session

        if( readFrom( &sesInfoOld, 1, sizeof sesInfoOld, oldFile )!=sizeof sesInfoOld )
        {
          rc = 4;
          break;
        }

        memcpy( &sesInfoNew, &sesInfoOld, sizeof sesInfoOld );
        memset( &sesInfoNew.domain[0], 0, sizeof sesInfoNew.domain );
        strcpy( &sesInfoNew.domain[0], "FidoNet" );

        if( writeNew( newFile,
                      &header, sizeof header,
                      &sesInfoNew, sizeof sesInfoNew ) )
          rc = 5;

        break;

      case 7: 			// Empty stamp

        if( writeNew( newFile, &header, sizeof header ) )
          rc = 5;

        break;

      default:

        rc = 6;
      }
#if defined(DEBUG)
      printf( "--\n" );
#endif
    }
  }

  int err( errno );

  switch( rc )
  {
  case 0:
    break;

  case 4:

    if( ferror( oldFile ) )
      fprintf( stderr, "*Error* Unable to read file at record %u - %s\n",
                       logLines,  strerror( err ) );

    if( feof( oldFile ) )
      fprintf( stderr, "*Error* Incomplete input file at record %u - %s\n",
                       logLines, strerror( err ) );
    break;

  case 5:

    fprintf( stderr, "*Error* Unable to write file at record %u - %s\n",
                     logLines, strerror( err ) );
    break;

  case 6:

    fprintf( stderr, "*Error* Illegal record type %u at record %u\n",
                     int(header.code), logLines );
    break;

  default:
    fprintf( stderr, "*BUG* Internal error %u at record %u - %s\n",
                     rc, logLines, strerror( err ) );
  }

  if( rc )
    printf( "*Error* Processing aborted! RC=%d\n", rc );

  printf( "Total successfully converted %u records\n", logLines );
  return rc;
}

void myperror( char *fil )
{
  fprintf( stderr, "*Fatal* Unable to open file %s - %s\n", fil, strerror( errno ) );
}

int main( int argn, char *argc[] )
{
  if( argn!=3 )
  {
    fprintf( stderr, "Usage: blgcvt old-blg new-blg\n" );
    return 1;
  }

  printf( "blgcvt -- convert The Brake!(tm) Mailer binary log from 717 to 718 -- "
          OS_MODE "\n"
          "Copyright (c) 1998 by John Gladkih and Bad Data, INC.\n" );

  FILE *oldf( fopen( argc[1], "rb" ) );

  if( !oldf )
  {
    myperror( argc[1] );
    return 2;
  }

  FILE *newf( fopen( argc[2], "wb" ) );

  if( !newf )
  {
    myperror( argc[2] );
    return 3;
  }

  return convert( oldf, newf );
}
