#ifndef DOS_EXALL_H
#include <dos/exall.h>
#endif

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/utility.h>

#include <string.h>
#include <MyStartup.h>

#ifndef V39
#ifndef CLIB_ALIB_PROTOS_H
APTR LibAllocPooled( APTR poolHeader, unsigned long memSize );
APTR LibCreatePool( unsigned long memFlags, unsigned long puddleSize,
	unsigned long threshSize );
void LibDeletePool( APTR poolHeader );
void LibFreePooled( APTR poolHeader, APTR memory, unsigned long memSize );
#endif   /* CLIB_ALIB_PROTOS_H */
#else /* V39 only */
#define LibAllocPooled AllocPooled
#define LibCreatePool CreatePool
#define LibDeletePool DeletePool
#define LibFreePooled FreePooled
#endif   /* V39 */

/***********************************************/

struct PathNode
   {
      BPTR Next;
      BPTR DirLock;
   };

struct FileNode
   {
      struct FileNode *Next;
      long Type;
      long Size;
      UBYTE Printed;
      char Filename[1];
   };

/***********************************************/

#define BUFFER_SIZE 20000

static struct Library *UtilityBase;

static void *MemoryPool;
static struct ExAllData *ExAllBuffer;
static struct ExAllControl *ExAllControl;

static char FilePattern[32];

static struct FileNode *FileList;

static char Version[]="$VER: CheckDouble"
#ifdef V39
   "39"
#endif
" 1.0 (10.01.94) © 1994 Christian Stieber";

/***********************************************/

static void CloseAll(long Error, char *Header)

{
   if (Error) PrintFault(Error,Header);

   if (MemoryPool) LibDeletePool(MemoryPool);
   if (ExAllControl) FreeDosObject(DOS_EXALLCONTROL,ExAllControl);

   SetIoErr(Error);
   exit(Error ? RETURN_FAIL : RETURN_OK);
}

/***********************************************/

static void ReadDir(BPTR DirLock)

{
   static struct FileNode *LastNode;

   char Filename[1024];
   int More;

   if (!NameFromLock(DirLock,Filename,sizeof(Filename))) CloseAll(IoErr(),NULL);
   ExAllControl->eac_LastKey=0;
   do
      {
         More=ExAll(DirLock,ExAllBuffer,BUFFER_SIZE,ED_PROTECTION,ExAllControl);
         if (More || IoErr()==ERROR_NO_MORE_ENTRIES)
            {
               if (ExAllControl->eac_Entries)
                  {
                     struct ExAllData *Data;
                     Data=ExAllBuffer;
                     do
                        {
                           struct FileNode *NewNode;
                           if (!AddPart(Filename,Data->ed_Name,sizeof(Filename))) CloseAll(IoErr(),Filename);
                           if (!(NewNode=LibAllocPooled(MemoryPool,sizeof(struct FileNode)+strlen(Filename)))) CloseAll(IoErr(),NULL);
                           NewNode->Type=Data->ed_Type;
                           NewNode->Size=Data->ed_Size;
                           NewNode->Printed=FALSE;
                           strcpy(NewNode->Filename,Filename);
                           NewNode->Next=NULL;
                           if (LastNode)
                              {
                                 LastNode->Next=NewNode;
                              }
                           else
                              {
                                 FileList=NewNode;
                              }
                           LastNode=NewNode;
                           *PathPart(Filename)='\0';
                           Data=Data->ed_Next;
                        }
                     while (Data);
                  }
            }
      }
   while (More);
   if (IoErr()!=ERROR_NO_MORE_ENTRIES) CloseAll(IoErr(),Filename);
}

/***********************************************/

static void ReadDirs(void)

{
   struct CommandLineInterface *CLI;

   if (CLI=Cli())
      {
         struct PathNode *CurrentNode;
         CurrentNode=BADDR(CLI->cli_CommandDir);
         while (CurrentNode)
            {
               if (CheckSignal(SIGBREAKF_CTRL_C)) CloseAll(ERROR_BREAK,NULL);
               ReadDir(CurrentNode->DirLock);
               CurrentNode=BADDR(CurrentNode->Next);
            }
      }
}

/***********************************************/

static ULONG /* __saveds */ __asm MatchFunc(register __a1 struct ExAllData *Data)

{
   if (Data->ed_Type>=0) return FALSE;   /* ignore directories */
   if (Data->ed_Prot & (FIBF_EXECUTE | FIBF_READ)) return FALSE;   /* read and execute permission required */
   if (Data->ed_Size<16) return FALSE;   /* file too short */
   return TRUE;
}

static struct Hook MatchHook=
   {
      {NULL,NULL},MatchFunc,NULL,NULL
   };

/***********************************************/

static void InitStuff(void)

{
   if (!(UtilityBase=OpenLibrary("utility.library",37))) exit(100);

   if (!(MemoryPool=LibCreatePool(0,50*1024,50*1024)) ||
       !(ExAllBuffer=LibAllocPooled(MemoryPool,BUFFER_SIZE)) ||
       !(ExAllControl=AllocDosObject(DOS_EXALLCONTROL,NULL)) ||
       (ParsePatternNoCase("~(#?.info)",FilePattern,sizeof(FilePattern))==-1))
      {
         CloseAll(IoErr(),NULL);
      }
   ExAllControl->eac_MatchString=FilePattern;
   ExAllControl->eac_MatchFunc=&MatchHook;
}

/***********************************************/

static void FindDuplicateNames(void)

{
   struct FileNode *CurrentNode;

   CurrentNode=FileList;
   while (CurrentNode)
      {
         if (CheckSignal(SIGBREAKF_CTRL_C)) CloseAll(ERROR_BREAK,NULL);
         if (!CurrentNode->Printed)
            {
               struct FileNode *t;
               int FirstMatch;
               char *CurrentFile;

               FirstMatch=TRUE;
               CurrentFile=FilePart(CurrentNode->Filename);
               t=CurrentNode;
               while (t=t->Next)
                  {
                     if (!Stricmp(CurrentFile,FilePart(t->Filename)))
                        {
                           t->Printed=TRUE;
                           if (FirstMatch)
                              {
                                 VPrintf("Duplicate commandname \x22%s\x22:\n   ",&CurrentFile);
                                 PutStr(CurrentNode->Filename);
                                 PutStr("\n");
                                 FirstMatch=FALSE;
                              }
                           PutStr("   ");
                           PutStr(t->Filename);
                           PutStr("\n");
                        }
                  }
            }
         else
            {
               CurrentNode->Printed=FALSE;
            }
         CurrentNode=CurrentNode->Next;
      }
}

/***********************************************/

static long *ReadFile(struct FileNode *FileNode)

{
   BPTR FileHandle;
   long *Memory;
   long Error;

   if (CheckSignal(SIGBREAKF_CTRL_C)) CloseAll(ERROR_BREAK,NULL);
   if (!(Memory=LibAllocPooled(MemoryPool,(FileNode->Size+3)&~3))) CloseAll(IoErr(),FileNode->Filename);
   Memory[((FileNode->Size+3)>>2)-1]=0;
   if (!(FileHandle=Open(FileNode->Filename,MODE_OLDFILE))) CloseAll(IoErr(),FileNode->Filename);
   if (Read(FileHandle,Memory,FileNode->Size)!=FileNode->Size)
      {
         Error=IoErr();
      }
   else
      {
         Error=0;
      }
   Close(FileHandle);
   if (Error) CloseAll(Error,FileNode->Filename);
   return Memory;
}

/***********************************************/

static void FindDuplicateFiles(void)

{
   struct FileNode *CurrentNode;

   CurrentNode=FileList;
   while (CurrentNode)
      {
         if (CheckSignal(SIGBREAKF_CTRL_C)) CloseAll(ERROR_BREAK,NULL);
         if (CurrentNode->Type!=ST_LINKFILE && !CurrentNode->Printed)
            {
               long *CurrentFile;
               struct FileNode *t;
               int FirstMatch;

               CurrentFile=NULL;
               t=CurrentNode;
               FirstMatch=TRUE;
               while (t=t->Next)
                  {
                     if (t->Type!=ST_LINKFILE && t->Size==CurrentNode->Size)
                        {
                           long *tFile;
                           long i;
                           int Identical;

                           if (!CurrentFile) CurrentFile=ReadFile(CurrentNode);
                           tFile=ReadFile(t);
                           Identical=TRUE;
                           for (i=0; Identical && i<(CurrentNode->Size+3)>>2; i++)
                              {
                                 if (CurrentFile[i]!=tFile[i])
                                    {
                                       Identical=FALSE;
                                    }
                              }
                           LibFreePooled(MemoryPool,tFile,(t->Size+3)&~3);
                           if (Identical)
                              {
                                 if (FirstMatch)
                                    {
                                       PutStr("Identical files:\n   ");
                                       PutStr(CurrentNode->Filename);
                                       PutStr("\n");
                                       FirstMatch=FALSE;
                                    }
                                 PutStr("   ");
                                 PutStr(t->Filename);
                                 PutStr("\n");
                                 t->Printed=TRUE;
                              }
                        }
                  }
               if (CurrentFile) LibFreePooled(MemoryPool,CurrentFile,(CurrentNode->Size+3)&~3);
            }
         CurrentNode=CurrentNode->Next;
      }
}

/***********************************************/

void main(void)

{
   if (CommandLineLength)
      {
         InitStuff();
         ReadDirs();
         FindDuplicateNames();
         FindDuplicateFiles();
         CloseAll(0,NULL);
      }
}
