
#include <exec/alerts.h>
#include <exec/memory.h>
#include <exec/execbase.h>

#include <dos/dosextens.h>
#include <dos/rdargs.h>

#include <libraries/xpk.h>

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

#include <string.h>

#define BUFFERSIZE 16384L

struct ExecBase *SysBase;
struct DosLibrary *DOSBase;
struct Library *XpkBase;

LONG __builtin_min(LONG,LONG);
#define MIN __builtin_min

void __stdargs PrintF(char *,...);
void __regargs PackFile(char *,struct FileInfoBlock *,char *,LONG);
LONG __asm __saveds ChunkFunc(register __a1 struct XpkProgress *);

struct Hook ChunkHook = {{0L},ChunkFunc};

LONG __saveds main(void)

{
 struct Process *MyProc;
 struct RDArgs *RDArgs;
 char ProgName[32];
 LONG Result;
 LONG Array[4];
 char **Files;
 struct AnchorPath *AnchorPath;

 SysBase=*((struct ExecBase **)4L);
 MyProc=(struct Process *)SysBase->ThisTask;

 if (MyProc->pr_CLI==NULL)
  {
   (void)WaitPort(&MyProc->pr_MsgPort);
   Forbid();
   ReplyMsg (GetMsg(&MyProc->pr_MsgPort));

   return 0L;
  }

 if ((DOSBase=(struct DosLibrary *)OpenLibrary("dos.library",37L))==NULL)
  {
   Alert (AT_Recovery|AG_OpenLib|AO_DOSLib);

   return 20L;
  }

 if (!GetProgramName(ProgName,32L)) (void)strcpy(ProgName,"xSmart");
 Result=10L;

 if ((XpkBase=OpenLibrary(XPKNAME,0L))==NULL)
  {
   PrintF ("%s: %s required !\n",XPKNAME);

   goto Close1;
  }

 Array[0]=Array[1]=Array[3]=NULL;
 Array[2]=512L;
 if ((RDArgs=ReadArgs("FILE/M,METHOD/K/A,MINSIZE/N/K,ALL/S",Array,NULL))==NULL)
  {
   PrintFault (IoErr(),ProgName);

   goto Close2;
  }

 if ((AnchorPath=(struct AnchorPath *)AllocVec(sizeof(struct AnchorPath)+256L,
                                               MEMF_PUBLIC|MEMF_CLEAR))==NULL)
  {
   PrintFault (ERROR_NO_FREE_STORE,ProgName);

   goto Close3;
  }
 AnchorPath->ap_BreakBits=SIGBREAKF_CTRL_C;
 AnchorPath->ap_Strlen=256;

 Files=(char **)Array[0];
 while (*Files)
  {
   LONG RetVal;

   for (RetVal=MatchFirst(*Files++,AnchorPath); RetVal==0L; RetVal=MatchNext(AnchorPath))
    {
     if (AnchorPath->ap_Info.fib_DirEntryType>0L)
      {
       if (((AnchorPath->ap_Flags&APF_DIDDIR)==0L)&&(Array[3])) AnchorPath->ap_Flags|=APF_DODIR;
       AnchorPath->ap_Flags&=~APF_DIDDIR;
      }
     else
      {
       BPTR DirLock;

       DirLock=CurrentDir(AnchorPath->ap_Current->an_Lock);
       PackFile (AnchorPath->ap_Buf,&AnchorPath->ap_Info,(char *)Array[1],Array[2]);
       (void)CurrentDir(DirLock);
      }
    }
   MatchEnd (AnchorPath);

   if (RetVal!=ERROR_NO_MORE_ENTRIES)
    {
     PrintFault (RetVal,ProgName);
     
     goto Close4;
    }
  }
 Result=0L;

Close4:
 FreeVec ((APTR)AnchorPath);
Close3:
 FreeArgs (RDArgs);
Close2:
 CloseLibrary (XpkBase);
Close1:
 CloseLibrary (&DOSBase->dl_lib);
 return Result;
}

char *VersionString = "$VER: xSmart 1.0 ("__DATE__")";

void __stdargs PrintF(char *FormatString,...)

{
 (void)VPrintf(FormatString,(LONG *)&FormatString+1L);
 (void)Flush(Output());
}

LONG __asm __saveds ChunkFunc(register __a1 struct XpkProgress *Prog)

{
 PrintF ("\r%-9s %-12s (%6ld bytes, %3ld%% done, %2ld%% CF) ",
         Prog->Activity,Prog->FileName,Prog->ULen,Prog->Done,Prog->CF);
 if (Prog->Type==XPKPROG_END) PrintF ("\n");

 return (LONG)SetSignal(0L,SIGBREAKF_CTRL_C)&SIGBREAKF_CTRL_C;
}

void __regargs PackFile(char *PathName,struct FileInfoBlock *FIB,char *Method,LONG MinSize)

{
 BPTR Handle;
 LONG *Ptr,Help,Size;
 struct TagItem XpkTags[9];
 char ErrorBuffer[XPKERRMSGSIZE];

 if (FIB->fib_Size<=MinSize)
  {
   PrintF ("Skipping %s (too small)\n",PathName);
   return;
  }
 if ((Handle=Open(FIB->fib_FileName,MODE_READWRITE))==NULL)
  {
   PrintF ("Can't open %s\n",PathName);
   return;
  }
 if (Read(Handle,(char *)&Help,4L)!=4L)
  {
   Close (Handle);

   PrintF ("Can't read %s\n",PathName);
   return;
  }
 if (Help=='XPKF')
  {
   Close (Handle);

   PrintF ("Skipping %s (already crunched)\n",PathName);
   return;
  }
 (void)Seek(Handle,0L,OFFSET_BEGINNING);

 XpkTags[0].ti_Tag=XPK_InFH;
 XpkTags[0].ti_Data=(ULONG)Handle;
 XpkTags[1].ti_Tag=XPK_GetOutBuf;
 XpkTags[1].ti_Data=(ULONG)&Ptr;
 XpkTags[2].ti_Tag=XPK_GetOutLen;
 XpkTags[2].ti_Data=(ULONG)&Size;
 XpkTags[3].ti_Tag=XPK_GetOutBufLen;
 XpkTags[3].ti_Data=(ULONG)&Help;
 XpkTags[4].ti_Tag=XPK_FindMethod;
 XpkTags[4].ti_Data=(ULONG)Method;
 XpkTags[5].ti_Tag=XPK_GetError;
 XpkTags[5].ti_Data=(ULONG)ErrorBuffer;
 XpkTags[6].ti_Tag=XPK_ChunkHook;
 XpkTags[6].ti_Data=(ULONG)&ChunkHook;
 XpkTags[7].ti_Tag=XPK_FileName;
 XpkTags[7].ti_Data=(ULONG)FIB->fib_FileName;
 XpkTags[8].ti_Tag=TAG_DONE;

 Ptr=NULL;
 Size=0L;
 if (XpkPack(XpkTags))
  {
   Close (Handle);

   PrintF ("%s\n",ErrorBuffer);
   return;
  }
#ifdef BUGFIX
 Size=Ptr[1]+8L;
#endif
 (void)Seek(Handle,0L,OFFSET_BEGINNING);
 if (Write(Handle,(char *)Ptr,Size)!=Size)
  {
   FreeMem ((APTR)Ptr,Help);
   Close (Handle);

   PrintF ("Error while writing on %s\n",PathName);
   return;
  }
 FreeMem ((APTR)Ptr,Help);

 if (SetFileSize(Handle,0L,OFFSET_CURRENT)!=Size)
  {
   Close (Handle);

   PrintF ("Error while truncating %s\n",PathName);
   return;
  }
 Close (Handle);
 (void)SetFileDate(FIB->fib_FileName,&FIB->fib_Date);
}
