XCOMP

XCOMP is a companion to XCOPY and lets you compare entire directory subtrees with a single command.

XCOMP

/************************************************************************************************
*                                                                                               *
*  Copyright (c) 1989 - 2011 David Cass Tyler, PO Box 1026, Willard, NM 87063, (505) 384-5342   *
*                                                                                               *
*  When you use XCOPY to copy a directory subtree, how do you know that the copy was            *
*  successful?  That is why XCOMP was written - to give you the assurance that your files were  *
*  all successfully backed up.  XCOMP does a byte for byte comparison of the source file to the *
*  destination file to assure that everything worked.  You are going to be suprised how often   *
*  what you think are successful copies are not - files that were miscopied, files and          *
*  expecially files that weren't copied that you thought were.                                  *
*                                                                                               *
*  XCOMP was NOT written to commercial standards.  It was written to satisfy my own needs and   *
*  I am sharing it with you, so don't gripe about it.  If you don't like how something works,   *
*  correct it (and send me a copy of your corrected code please so that I can benefit from      *
*  your efforts also).                                                                          *
*                                                                                               *
*  To compile it from the command line:  "CL XCOMP.cpp"                                         *
*                                                                                               *
************************************************************************************************/


#define VC_EXTRALEAN    // Exclude rarely-used stuff from Windows headers

#include <WINDOWS.H>

#include <IO.H>
#include <STDIO.H>

void  directory   ( char *, char * );
int   file        ( char *, char * );

BOOL  RECURSIVE   = FALSE;
int   FAILURES    = 0;

void main ( int argc, char * argv[] )

   {
   int   i              = 0;

   DWORD dwResult       = 0LU;

   char  szSrcDirectory [ _MAX_PATH ];
   char  szDstDirectory [ _MAX_PATH ];

   if ( argc < 3 )
      {
      fprintf (
         stdout,
         "\n"
         "XCOMP 2.00 (C) 1989 - 2011, David Cass Tyler, P.O. Box 1026, Willard, NM 87063\n"
         "\n"
         "%s:  Compares files in different directories\n"
         "\n"
         "Usage:\t\tXCOMP pathname pathname [/S]\n"
         "/S\t\tto include subdirectories\n"
         "\n"
         "Example:\tXCOPY . A: /S - backup all files"
         " in this directory subtree\n"
         "\t\tXCOMP . A: /S - now verify them\n",
         strupr ( argv[0] )
      );
      exit ( 1 );
      }

   // Test for the /S flag

   for ( i = 3; i < argc; i++ )
      {
      if ( ! strcmp ( strupr ( argv[i] ), "/S" ) )
         RECURSIVE = TRUE;
      }

   // Test files for equality

   if ( ! GetFullPathName ( argv[1], sizeof szSrcDirectory, szSrcDirectory, NULL ) )
      {
      fprintf ( stderr, "\aCouldn't identify directory %s\n", argv[1] );
      exit ( 1 );
      }

   if ( ! GetFullPathName ( argv[2], sizeof szDstDirectory, szDstDirectory, NULL ) )
      {
      fprintf ( stderr, "\aCouldn't identify directory %s\n", argv[2] );
      exit ( 1 );
      }

   if ( ( dwResult = GetFileAttributes ( szSrcDirectory ) ) == 0xFFFFFFFFL )
      {
      fprintf ( stderr, "\aDirectory %s not found!\n", szSrcDirectory );
      exit ( 1 );
      }

   if ( ! ( dwResult & FILE_ATTRIBUTE_DIRECTORY ) )
      {
      fprintf ( stderr, "\a%s is not a directory!\n", szSrcDirectory );
      exit ( 1 );
      }

   if ( ( dwResult = GetFileAttributes ( szDstDirectory ) ) == 0xFFFFFFFFL )
      {
      fprintf ( stderr, "\aDirectory %s not found!\n", szDstDirectory );
      exit ( 1 );
      }

   if ( ! ( dwResult & FILE_ATTRIBUTE_DIRECTORY ) )
      {
      fprintf ( stderr, "\a%s is not a directory!\n", szDstDirectory );
      exit ( 1 );
      }

   fprintf ( stderr, "\r%s\n", argv[1] );
   directory ( strupr ( szSrcDirectory ), strupr ( szDstDirectory ) );

   if ( FAILURES )
      {
      fprintf ( stderr, "\x07%d File(s) failed to compare\n", FAILURES );
      exit ( 2 );
      }
   else
      {
      fprintf ( stderr, "All files compared OK\n" );
      exit ( 0 );
      }
   }

void directory ( char original [ _MAX_PATH ], char duplicate [ _MAX_PATH ] )

   {
   char                 search   [ _MAX_PATH+4  ];
   char                 found    [ _MAX_PATH+14 ];
   char                 compare  [ _MAX_PATH+14 ];
   struct _finddata_t   s_file   = { 0LU, 0L, 0L, 0L, 0LU, TEXT ( "" ) };
   long                 hFile    = 0L;

   original  [ _MAX_PATH - 1 ] = 0;
   duplicate [ _MAX_PATH - 1 ] = 0;

   strcpy ( search, original );
   if ( original[strlen(original)-1] != '\\' )
      strcat ( search, "\\" );
   strcat ( search, "*.*" );

   if ( strlen ( search ) > _MAX_PATH )
      {
      fprintf ( stderr, "\aMaximum path length exceeded\n" );
      exit ( 3 );
      }

   // Process Files Including Hidden and System Files

   hFile = _findfirst ( search, &s_file );
   if ( hFile == -1 )
      {
      fprintf ( stderr, "\aFile not found!\n" );
      return;
      }

   do
      {
   // TRACE ( "%s\n", s_file.name );

      if ( s_file.attrib & _A_SUBDIR )
         continue;

      if ( s_file.name[0] != '.' )
         {
         strcpy ( found, original );
         if ( original[strlen(original)-1] != '\\' )
            strcat ( found, "\\" );
         strcat ( found, s_file.name );

         if ( strlen ( found ) > _MAX_PATH )
            {
            fprintf ( stderr, "\aMaximum path length exceeded\n" );
            exit ( 4 );
            }

         strcpy ( compare, duplicate   );
         if ( duplicate[strlen(duplicate)-1] != '\\' )
            strcat ( compare, "\\"  );
         strcat ( compare, s_file.name );

         if ( strlen ( compare ) > _MAX_PATH )
            {
            fprintf ( stderr, "\aMaximum path length exceeded\n" );
            exit ( 5 );
            }

         if ( file ( found, compare ) )
            {
            FAILURES++;
            }
         else
            {
            fprintf ( stderr, "\r%-79s\r", strlwr ( s_file.name ) );
            }
         }
      } while ( _findnext ( hFile, &s_file ) == 0L );

   if ( hFile )
      {
      _findclose ( hFile );
      hFile = 0L;
      }

   fprintf ( stderr, "%-12s\r", "" );

   // Process Subdirectories

   if ( RECURSIVE )
      {
      hFile = _findfirst ( search, &s_file );
      if ( hFile == -1 )
      return;

      do {
         if ( s_file.attrib & _A_SUBDIR )
            {
            if ( s_file.name[0] != '.' )
               {
               strcpy ( found, original    );
               if ( original[strlen(original)-1] != '\\' )
                  strcat ( found, "\\"  );
               strcat ( found, s_file.name );

               if ( strlen ( found ) > _MAX_PATH )
                  {
                  fprintf ( stderr, "\aMaximum path length exceeded\n" );
                  exit ( 6 );
                  }

               strcpy ( compare, duplicate   );
               if ( duplicate[strlen(duplicate)-1] != '\\' )
                  strcat ( compare, "\\"  );
               strcat ( compare, s_file.name );

               if ( strlen ( compare ) > _MAX_PATH )
                  {
                  fprintf ( stderr, "\aMaximum path length exceeded\n" );
                  exit ( 7 );
                  }

               fprintf ( stderr, "\r%s\n", found );
               directory ( found, compare );
               }
            }
         } while ( _findnext ( hFile, &s_file ) == 0L );

      if ( hFile )
         {
         _findclose ( hFile );
         hFile = 0L;
         }
      }
   }

int file ( char original[_MAX_PATH], char duplicate[_MAX_PATH] )

   {
   unsigned char org_buf[2048], dup_buf[2048];
   int org_bytes, dup_bytes;
   long org_len, dup_len;
   FILE * org_fp = NULL, * dup_fp = NULL;
   long i;
   int  j;
   int hFile = 1;
   
   // Open the files

   if ( ( org_fp = fopen ( original, "rb" ) ) == NULL ) {
      
      fprintf ( stdout, "%s - Unable to open file\n", original );
      goto exit;
      
      } else if ( ( dup_fp = fopen ( duplicate, "rb" ) ) == NULL ) {
      
      fprintf ( stdout, "%s - File not found\n", duplicate );
      goto exit;
      
         }
      
      // Allocate buffers

      setvbuf ( org_fp, _IOFBF, NULL, 32768 );
      setvbuf ( dup_fp, _IOFBF, NULL, 32768 );
      
      // Compare file lengths

      org_len = filelength ( fileno ( org_fp ) );
      dup_len = filelength ( fileno ( dup_fp ) );
      
      if ( org_len != dup_len ) {
         fprintf ( stdout, "%s - %s Files are different sizes\n", original, duplicate );
         goto exit;
         }
      
      // Compare the file contents

      for ( i = 0; i < org_len; i += 2048 ) {
         
         org_bytes = fread ( org_buf, 1, 2048, org_fp );
         dup_bytes = fread ( dup_buf, 1, 2048, dup_fp );
         
         if ( org_bytes != dup_bytes ) {
            fprintf ( stderr, "%s Read Error\n" );
            goto exit;
            }
         if ( memcmp ( org_buf, dup_buf, org_bytes ) ) {
            for ( j = 0; j < org_bytes; j++ ) {
               if ( org_buf[j] != dup_buf[j] ) {
                  fprintf ( stdout, "%s - %s Compare error at OFFSET %lX\n", original, duplicate, i + j );
                  goto exit;
                  }
               }
            }
         }
      hFile = 0;
      
exit: // Yes, I know I am using a goto - the aim of this code is to get a job done - not to impress you
      // with my programming purity.

      if ( org_fp ) { fclose ( org_fp );  org_fp = NULL; }
      if ( dup_fp ) { fclose ( dup_fp );  dup_fp = NULL; }
      
      return ( hFile );
   }