/*
  bdiff.c - Byte Difference: diff two almost identical files.

  This program is used to display the bytes changed between two files. The
  files are assumed to be the same length. The bytes from the first (changed)
  file are displayed first, followed by the bytes from the second (original)
  file, in square brackets. Bytes that are printable (ASCII) are displayed as
  characters enclosed in quotes (a quote itself is duplicated).

  Jason Hood, 7 September, 1998.
  Public Domain.

  981228: display the changed filename, or optionally the original filename;
	  if one or two bytes are identical within a difference, include them
	   in the diff list.
  000222: stop diffing if too different; display message if identical.
  000303: changed message if too different.
  001001: split difference across lines;
	  option to disable string translation.

  v1.00, 15 & 16 October, 2003:
    better handling of different sized files;
    display the count if diff is too big;
    surround the filename in quotes if it contains spaces.

  v1.01, 7 September, 2004:
    use a buffer to store the different bytes;
    increase default maximum to 88 bytes;
    line up the last line of a multi-line diff;
    require four printable characters before using a string;
    calculate the lengths of both files, use the smaller;
    modified return codes.
*/

#define PVERS "1.01"
#define PDATE "7 September, 2004"


#include 
#include 
#include 
#include 

#ifdef __DJGPP__
void   __crt0_load_environment_file( char* dummy ) { }
char** __crt0_glob_function( char* dummy ) { return 0; }
#endif


int string = 1; 		// Display strings

int calculate_len( unsigned char* buf, int len );
int display( unsigned char* buf, int len );


enum
{
  E_SAME,			// Files are identical
  E_DIFF,			// Files are different
  E_TOO,			// Files are too different
  E_OPT,			// Unknown/invalid option
  E_MEM,			// Not enough memory for buffer
  E_NEW,			// Unable to open changed file
  E_OLD,			// Unable to open original file
};


int main( int argc, char* argv[] )
{
  FILE* file1;			// Changed file
  FILE* file2;			// Original file
  long	offset; 		// Offset of difference
  int	byte1, byte2;		// The two bytes
  char* buf1;			// Changed bytes
  char* buf2;			// Original bytes
  char* disp1;			// Changed bytes being displayed
  char* disp2;			// Original bytes being displayed
  int	len;			// Length of difference
  int	cnt1, cnt2;		// Length of difference to display
  int	maxlen = 88;		// Stop diffing if length exceeds this
  int	out = 1;		// Filename to display
  int	different = E_SAME;	// Were any differences found?

  if (argc < 3 || strcmp( argv[1], "/?" ) == 0 ||
		  strcmp( argv[1], "-?" ) == 0 ||
		  strcmp( argv[1], "--help" ) == 0)
  {
    puts(
    "BDiff by Jason Hood .\n"
    "Version "PVERS" ("PDATE"). Public Domain.\n"
    "http://misc.adoxa.cjb.net/\n"
    "\n"
    "Display the byte difference between two almost-identical files.\n"
    "\n"
    "bdiff [-o] [-b] [-]  \n"
    "\n"
    "  -o    display original filename, not changed\n"
    "  -b    bytes only (no strings)\n"
    "  -  no more than n bytes should differ (default is 88)"
    );
    return 0;
  }

  while (argv[1][0] == '-')
  {
    switch (argv[1][1])
    {
      case 'o': out    = 2; break;
      case 'b': string = 0; break;
      default:
	maxlen = (int)strtol( argv[1] + 1, NULL, 0 );
	if (maxlen == 0)
	{
	  fprintf( stderr, "Unknown option: %s\n", argv[1] );
	  return E_OPT;
	}
      break;
    }
    ++argv; --argc;
  }

  if ((buf1 = malloc( maxlen * 2 )) == NULL)
  {
    fputs( "Not enough memory.\n", stderr );
    return E_MEM;
  }
  buf2 = buf1 + maxlen;

  if ((file1 = fopen( argv[1], "rb" )) == NULL)
  {
    fprintf( stderr, "%s: unable to open.\n", argv[1] );
    return E_NEW;
  }
  if ((file2 = fopen( argv[2], "rb" )) == NULL)
  {
    fprintf( stderr, "%s: unable to open.\n", argv[2] );
    return E_OLD;
  }

  fputs( "File: ", stdout );
  if (strchr( argv[out], ' ' ) == NULL)
    puts( argv[out] );
  else
    printf( "\"%s\"\n", argv[out] );

  for (;;)
  {
    do					// Skip identical bytes
    {
      byte1 = getc( file1 );
      byte2 = getc( file2 );
    } while (byte1 == byte2 && byte1 != EOF);
    if ((byte1 | byte2) == EOF)
      break;
    different = E_DIFF;

    offset = ftell( file1 ) - 1;	// Pointing at byte after first diff.
    printf( "%06lX:", offset );

    len = 0;				// Count the different bytes
    for (;;)
    {
      ++len;
      byte1 = getc( file1 );
      byte2 = getc( file2 );
      if ((byte1 | byte2) == EOF)
	break;
      if (byte1 == byte2)
      {
	byte1 = getc( file1 );
	byte2 = getc( file2 );
	if ((byte1 | byte2) == EOF)
	  break;
	if (byte1 == byte2)
	{
	  byte1 = getc( file1 );
	  byte2 = getc( file2 );
	  if (byte1 == byte2 || (byte1 | byte2) == EOF)
	    break;
	  ++len;
	}
	++len;
      }
    }
    if (len > maxlen)
    {
      printf( " Difference is too great! (%d bytes found.)\n", len );
      return E_TOO;
    }
    fseek( file1, offset, SEEK_SET );
    fseek( file2, offset, SEEK_SET );
    fread( buf1, len, 1, file1 );
    fread( buf2, len, 1, file2 );
    for (disp1 = buf1, disp2 = buf2; ; disp1 += cnt1, disp2 += cnt1)
    {
      cnt1 = calculate_len( disp1, len );
      cnt2 = calculate_len( disp2, len );
      if (cnt2 < cnt1)
	cnt1 = cnt2;
      cnt2 = display( disp1, cnt1 );	// Display changed bytes
      if (cnt2 > 32)
	putchar( ' ' ), putchar( ' ' );
      else if (buf1 != disp1)
	printf( "%*c", 43-7-1 - cnt2, ' ' );
      else
	putchar( '\t' );
      putchar( '[' );
      display( disp2, cnt1 );		// Display original bytes
      puts( " ]" );
      len -= cnt1;
      if (len > 0)
	fputs( "       ", stdout );     // Seven spaces ("offset:")
      else
	break;
    }
  }
  if (!different)
    puts( "Files are identical." );

  return different;
}


/*
  Calculate the length to display. Allow for 11 bytes or 30 characters.
*/
int calculate_len( unsigned char* buf, int len )
{
  int byte;
  int quote = 0;
  int pos = 0;
  int cnt;

  for (cnt = 0; len; ++cnt, --len)
  {
    byte = *buf++;
    if (string &&
	isprint( byte ) && (quote || (len >= 4 &&
	isprint( buf[0] ) && isprint( buf[1] ) && isprint( buf[2] ))))
    {
      ++pos;
      if (!quote)
	quote = 1, pos += 2;	// Space & quote
      if (byte == '\"')
	++pos;
    }
    else
    {
      if (quote)
	quote = 0, ++pos;
      pos += 3;
    }
    if (pos > 33 - quote)
      break;
  }
  return cnt;
}


/*
  Display len bytes from buf. Printable characters are displayed within
  quotes (unless there's only one); otherwise two hexadecimal digits are
  used. If the character is a quote, another quote is also displayed
  (eg. """Hello,"" I said.").
  Returns the number of characters written.
*/
int display( unsigned char* buf, int len )
{
  int byte;
  int quote = 0;
  int out = 0;

  for (; len; --len)
  {
    byte = *buf++;
    if (string &&
	isprint( byte ) && (quote || (len >= 4 &&
	isprint( buf[0] ) && isprint( buf[1] ) && isprint( buf[2] ))))
    {
      if (!quote)
      {
	quote = 1;
	putchar( ' ' );
	putchar( '\"' );
	out += 2;
      }
      putchar( byte ), ++out;
      if (byte == '\"')
	putchar( '\"' ), ++out;
    }
    else
    {
      if (quote)
      {
	putchar( '\"' ), ++out;
	quote = 0;
      }
      out += printf( " %02X", byte );
    }
  }
  if (quote)
    putchar( '\"' ), ++out;

  return out;
}

    Source: geocities.com/jadoxa/misc

               ( geocities.com/jadoxa)