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

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

#include <xpk/xpk.h>

#include <pragma/dos_lib.h>
#include <pragma/exec_lib.h>
#include <pragma/xpkmaster_lib.h>

#include <string.h>

#define XFH_ID "XFH A"

void PrintF(char *,...);
void __regargs xScan(struct DosLibrary *,struct Library *,
                     struct FileInfoBlock *,LONG);

struct xScanArgs
 {
  char **Files;
  LONG Remove,All;
 };

char *VersionString = "$VER: xScan 1.2 "__AMIGADATE__;
char *Template = "FILE/M/A,REMOVE/S,ALL/S";

LONG __saveds main(void)

{
 struct DosLibrary *DOSBase;
 struct Library *XpkBase;
 struct Process *MyProc;
 struct RDArgs *RDArgs;
 char ProgName[32];
 LONG Result;
 struct xScanArgs Args;
 char **Files;
 struct AnchorPath *AnchorPath;

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

   return 0L;
  }

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

   return 20L;
  }
 if (DOSBase->dl_lib.lib_Version<37L)
  {
   (void)Write(Output(),"This program requires OS 2.04 or newer.\n",40L);

   CloseLibrary (&DOSBase->dl_lib);
   return 20L;
  }

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

 if ((XpkBase=OpenLibrary(XPKNAME,2L))==NULL)
  {
   PrintF ("%s: %s V2 or newer required !\n",ProgName,XPKNAME);

   goto Close1;
  }

 Args.Remove=Args.All=FALSE;
 if ((RDArgs=ReadArgs(Template,(LONG *)&Args,NULL))==NULL)
  {
   PrintFault (IoErr(),ProgName);

   goto Close2;
  }

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

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

 Files=Args.Files;
 while (*Files)
  {
   LONG RetVal;

   for (RetVal=MatchFirst(*Files,AnchorPath); RetVal==0L; RetVal=MatchNext(AnchorPath))
    {
     if (AnchorPath->ap_Info.fib_DirEntryType<0L)
      {
       BPTR DirLock;

       DirLock=CurrentDir(AnchorPath->ap_Current->an_Lock);
       xScan (DOSBase,XpkBase,&AnchorPath->ap_Info,Args.Remove);
       (void)CurrentDir(DirLock);
      }
     else
      if (AnchorPath->ap_Info.fib_DirEntryType!=ST_SOFTLINK)
       {
        if (((AnchorPath->ap_Flags&APF_DIDDIR)==0L)&&Args.All)
         AnchorPath->ap_Flags|=APF_DODIR;
        AnchorPath->ap_Flags&=~APF_DIDDIR;
       }
    }
   MatchEnd (AnchorPath);

   if (RetVal!=ERROR_NO_MORE_ENTRIES)
    {
     PrintF ("%s: %s - ",ProgName,*Files);
     PrintFault (RetVal,NULL);
     
     goto Close4;
    }
   else Files++;
  }
 Result=0L;

Close4: /* I hate "goto"s, but it's the only way to keep it short. */
 FreeVec ((APTR)AnchorPath);
Close3:
 FreeArgs (RDArgs);
Close2:
 CloseLibrary (XpkBase);
Close1:
 CloseLibrary (&DOSBase->dl_lib);
 return Result;
}

void PrintF(char *FormatString,...)

{
 struct DosLibrary *DOSBase;

 if (DOSBase=(struct DosLibrary *)OpenLibrary("dos.library",37L))
  {
   (void)VPrintf(FormatString,(LONG *)&FormatString+1L);
   (void)Flush(Output());

   CloseLibrary (&DOSBase->dl_lib);
  }
}

char __regargs *ULTA(char *Ptr,ULONG LW)

{
 UWORD Index;

 *Ptr++=' ';
 for (Index=0; Index<8; Index++, LW>>=4) Ptr[7-Index]='A'+(char)(LW&15);
 return &Ptr[8];
}

void __regargs xScan(struct DosLibrary *DOSBase,struct Library *XpkBase,
                     struct FileInfoBlock *FIB,LONG Remove)

{
 struct XpkFib XpkFib;
 char ErrorBuffer[XPKERRMSGSIZE];
 LONG Error;

 if(FIB->fib_Comment[0])
  {
   if(strncmp(FIB->fib_Comment,XFH_ID,strlen(XFH_ID))) return;
  }
 else
  if (Remove) return;

 if (XpkExamineTags(&XpkFib,
                    XPK_GetError,ErrorBuffer,
                    XPK_InName,FIB->fib_FileName,TAG_DONE))
  {
   PrintF ("%s\n",ErrorBuffer);
   return;
  }

 if (Remove)
  {
   if (!SetComment(FIB->fib_FileName,""))
    {
     Error=IoErr();
     PrintF ("Can't erase comment for %s: ");
     PrintFault (Error,NULL);
     return;
    }
  }
 else
  {
   char Comment[80],*Ptr;

   Ptr=strcpy(Comment,XFH_ID)+strlen(XFH_ID);
   Ptr=ULTA(Ptr,FIB->fib_Date.ds_Days);
   Ptr=ULTA(Ptr,FIB->fib_Date.ds_Minute);
   Ptr=ULTA(Ptr,FIB->fib_Date.ds_Tick);
   Ptr=ULTA(Ptr,FIB->fib_Size);
   *ULTA(Ptr,(XpkFib.xf_Type==XPKTYPE_PACKED)?XpkFib.xf_ULen:FIB->fib_Size)='\0';
   if (!SetComment(FIB->fib_FileName,Comment))
    {
     Error=IoErr();
     PrintF ("Can't set comment for %s: ");
     PrintFault (Error,NULL);
     return;
    }
  }
}
