/*
** Name: CompareDirs (short: compd)
** Description: Compares two directories to find differences
** Author: Martin Gierich
** $VER: compd 1.0 (10.9.97)
** Copying: Freeware, no commercial usage. Use it at your own risc.
*/

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

#include <exec/types.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/utility_protos.h>


struct entry
{
 struct entry *next;
 long size;
 long othersize;
 char name[0];
};

int  fastmemcmp(char *buffer1, char *buffer2, long size);
BOOL scandir(char dirname[], struct entry **root);
BOOL scanfiles(struct entry **files, char *dirname, char *fullname, char* partname);
BOOL insertfile(struct entry **files, char *name, long size);
void showfiles(struct entry *files);
BOOL checkfiles(char *dir1, char* dir2, struct entry *root1, struct entry *root2);
BOOL comparefile(struct entry *file1, struct entry *file2, BPTR lock1, BPTR lock2, char *buffer);
void closeoutfile(void);


#define printf Printf
#define fprintf FPrintf
extern const char *version="$VER: compd 1.0 (10.9.97)\n";
const long bufsize=500000L;
BPTR outfile;





int main (int argc, char **argv)
{
 struct entry *root1=NULL, *root2=NULL;

 if (argc<3 || argc>4)
 {
  printf("Usage: compd <dir1> <dir2> [<outfile>]\n");
  return(20);
 }

 if (scandir(argv[1], &root1)) return(20);
 if (scandir(argv[2], &root2)) return(20);
 //printf("\n\nFiles in 1:\n\n"); showfiles(root1);
 //printf("\n\nFiles in 2:\n\n"); showfiles(root2);

 if (argc==4)
 {
  outfile=Open(argv[3], MODE_NEWFILE);
  if (!outfile)
  {
   printf("Error opening %s !\n", argv[3]);
   return(20);
  }
  if ( !onexit(closeoutfile) )
  {
   printf("No trap !\n");
   Close(outfile);
   return(20);
  }
 }
 else
   outfile=Output();

 if ( checkfiles(argv[1], argv[2], root1, root2) ) return(20);

 return(0);
}




void closeoutfile(void)
{
 Close(outfile);
}




BOOL scandir(char dirname[], struct entry **root)
{
 static char fullname[1000];

 printf("Scanning directory %s\n", dirname);
 strcpy(fullname, dirname);

 if (scanfiles(root, fullname, fullname, fullname))
 {
  printf("Error during scanning %s !\n", dirname);
  return(TRUE);
 }

 return(FALSE);
}




BOOL scanfiles(struct entry **files, char* dirname, char *fullname, char* partname)
{
 __aligned struct FileInfoBlock fib;
 BPTR lock;
 LONG len;

 if (! (lock=Lock(dirname, ACCESS_READ)) )
 {
  printf("Cannot find %s !\n", fullname);
  return(TRUE);
 }

 if (!Examine(lock, &fib))
 {
  printf("Examining %s failed !\n", fullname);
  UnLock(lock);
  return(TRUE);
 }

 while (ExNext(lock, &fib))
 {
  strcpy(partname, fib.fib_FileName);

  if (fib.fib_DirEntryType>=0 && fib.fib_DirEntryType!=3)
  {
   lock=CurrentDir(lock);
   len=strlen(partname);
   partname[len]='/';
   partname[len+1]='\0';
   if (insertfile(files, fullname, 0L))
   {
    lock=CurrentDir(lock);
    UnLock(lock);
    return(TRUE);
   }
   if (scanfiles(files, partname, fullname, partname+len+1))
   {
    lock=CurrentDir(lock);
    UnLock(lock);
    return(TRUE);
   }
   lock=CurrentDir(lock);
  }

  else
  {
   if (insertfile(files, fullname, fib.fib_DirEntryType==3 ?  0 : fib.fib_Size))
   {
    UnLock(lock);
    return(TRUE);
   }
  }

 }

 UnLock(lock);
 if (IoErr()!=ERROR_NO_MORE_ENTRIES) return(TRUE);
 return(FALSE);
}




BOOL insertfile(struct entry **files, char *name, long size)
{
 struct entry *newfile, *file, *prevfile;

 chkabort();
 //printf(" %s\n", name);
 newfile=malloc(sizeof(struct entry)+strlen(name)+1);
 if (!newfile)
 {
  printf("Out of memory !\n");
  return(TRUE);
 }
 newfile->size=size;
 newfile->othersize=-1L;
 strcpy(newfile->name, name);

 prevfile=(struct entry *)files;
 file=prevfile->next;
 while ( file!=NULL && Stricmp(name, file->name)<0 )
 {
  prevfile=file;
  file=file->next;
 }
 newfile->next=file;
 prevfile->next=newfile;
 return(FALSE);
}


void showfiles(struct entry *files)
{
 struct entry *file;

 file=files;
 while (file!=NULL)
 {
  chkabort();
  if (file->othersize!=-1)
    fprintf(outfile, " %s  (%ld <-> %ld)\n", file->name, file->size, file->othersize);
  else
    fprintf(outfile, " %s  (%ld)\n", file->name, file->size);
  file=file->next;
 }
}




BOOL checkfiles(char *dir1, char *dir2, struct entry *file1, struct entry *file2)
{
 struct entry *ident=NULL, *onlyin1=NULL, *onlyin2=NULL, *differ=NULL;
 struct entry *nfile1, *nfile2;
 int rel;
 BPTR lock1, lock2;
 char *buffer;

 printf("Comparing files\n");
 lock1=Lock(dir1, ACCESS_READ);
 if (!lock1)
 {
  printf("Error accessing %s !\n", dir1);
  return(TRUE);
 }

 lock2=Lock(dir2, ACCESS_READ);
 if (!lock2)
 {
  printf("Error accessing %s !\n", dir2);
  UnLock(lock1);
  return(TRUE);
 }
 
 buffer=halloc(2*bufsize);
 if (!buffer)
 {
  printf("Out of memory !\n");
  UnLock(lock1);
  UnLock(lock2);
  return(TRUE);
 }

  while (file1!=NULL && file2!=NULL)
  {
   nfile1=file1->next;
   nfile2=file2->next;
   rel=Stricmp(file1->name, file2->name);

   if (rel==0)
   {
    if ( comparefile(file1, file2, lock1, lock2, buffer) )
    {
     file1->next=ident;
     ident=file1;
    }
    else
    {
     file1->next=differ;
     differ=file1;
    }
    file1=nfile1;
    file2=nfile2;
   }

   else if (rel>0)
   {
    file1->next=onlyin1;
    onlyin1=file1;
    file1=nfile1;
   }

   else
   {
    file2->next=onlyin2;
    onlyin2=file2;
    file2=nfile2;
   }

  }

  while (file1!=NULL)
  {
   nfile1=file1->next;
   file1->next=onlyin1;
   onlyin1=file1;
   file1=nfile1;
  }

  while (file2!=NULL)
  {
   nfile2=file2->next;
   file2->next=onlyin2;
   onlyin2=file2;
   file2=nfile2;
  }

 free(buffer);
 UnLock(lock1);
 UnLock(lock2);

 printf("Showing results\n");
 fprintf(outfile, "\n\nIdentical files:\n\n");
 showfiles(ident);
 fprintf(outfile, "\n\nFiles only in %s :\n\n", dir1);
 showfiles(onlyin1);
 fprintf(outfile, "\n\nFiles only in %s :\n\n", dir2);
 showfiles(onlyin2);
 fprintf(outfile, "\n\nDifferent files:\n\n");
 showfiles(differ);

 return(FALSE);
}




BOOL comparefile(struct entry *file1, struct entry *file2, BPTR lock1, BPTR lock2, char *buffer1)
{
 BPTR fh1, fh2;
 BPTR oldlock;
 BOOL result=FALSE;
 LONG res1, res2;
 char *name;
 char *buffer2;

 chkabort();
 if (file1->size!=file2->size)
 {
  file1->othersize=file2->size;
  goto exit2;
 }
 if (!file1->size) return(TRUE);
 name=file1->name;

 oldlock=CurrentDir(lock1);
 fh1=Open(name, MODE_OLDFILE);
 if (!fh1)
 {
  printf("Error opening %s !\n", name);
  CurrentDir(oldlock);
  goto exit2;
 }

 CurrentDir(lock2);
 fh2=Open(name, MODE_OLDFILE);
 if (!fh2)
 {
  printf("Error opening %s !\n", name);
  CurrentDir(oldlock);
  goto exit3;
 }
 CurrentDir(oldlock);

 buffer2=buffer1+bufsize;
 do
 {
  res1=Read(fh1, buffer1, bufsize);
  res2=Read(fh2, buffer2, bufsize);
  if (res1==-1 || res2==-1)
  {
   printf("Read error in %s !", name);
   goto exit4;
  }
  if (res1!=res2)
  {
   file1->othersize=file2->size;
   goto exit4;
  }
  if (res1 && fastmemcmp(buffer1, buffer2, res1)) goto exit4;
 }
 while (res1);
 result=TRUE;

 exit4: Close(fh2);
 exit3: Close(fh1);
 exit2: return(result);
}

