/* 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; }