/*************************************************************************
*                               unpUnpack                                *
*                                                                        *
*              With this program you can unpack your files.              *
*                                                                        *
*************************************************************************/

// First we include a lot of things

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

#include <exec/types.h>
#include <dos/rdargs.h>
#include <dos/dosextens.h>
#include <libraries/unpack.h>

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


// Global Variables
struct  Library *UnpackBase;
struct  UnpackInfo *cinfo=NULL;
struct  RDArgs *rda=NULL;
LONG    arguments[4] = {0,0,0,0};
BPTR    oldlock;
LONG    DecrunchLen;


#ifdef LATTICE
void __regargs _CXBRK(void)               // Catch CTRL-C Handling
{
  fprintf(stderr,"*** User breaked\n");   // Print out break message
  CurrentDir(oldlock);                    // and clean up after us
  upFreeCInfo(cinfo);
  CloseLibrary(UnpackBase);
  FreeArgs(rda);
  exit(0);                                // then we exit our program.
}
#endif


// Prototypes
void ScanDir(struct UnpackInfo *ui, BPTR mylock);
void UnpackFile(struct UnpackInfo *info, char *name);
void PrintName(char *name);
__asm void Saver(register __a1 ULONG userdata, register __a2 struct UnpackInfo *ui);

// Main program
void main (void)
{
extern  struct ExecBase *SysBase;
struct  Process *mytask;
struct  FileInfoBlock fib;

BPTR    dirlock;

char   *name;
int     i=0;

  if( ((struct Library*) DOSBase)->lib_Version < 37 )
  {
    fprintf(stderr,"unpUnpack requires at least Kickstart 2.04!\n");
    exit(0);
  }

  if (rda = ReadArgs("FILE=DIR/M,P=PATH/K,TO=DEST/K,PROTECT/S",arguments,NULL))
  {
    if (!((arguments[1] == NULL) && (arguments[2] == NULL)))
    {
      if (UnpackBase = OpenLibrary("unpack.library",42L))
      {
        if (cinfo = upAllocCInfo())
        {
          printf("unpUnpack 1.0 (5/10-1995) by Thomas Neumann\n\n");
          mytask = (struct Process *)FindTask(NULL);
          oldlock = mytask->pr_CurrentDir;

          cinfo->ui_Path = (char *)arguments[1]; // Setup path to archive unpackers
          cinfo->ui_Jump = (void (*)(void *, void *))Saver;
          cinfo->ui_Flag = UFN_NoA4;
          if (arguments[3] != 0)
            cinfo->ui_Flag |= UFN_Protect;

          if (arguments[0] == NULL) // If no files are given, scan
          {                         // current directory.
            dirlock = (oldlock);
            ScanDir(cinfo, dirlock);
          }
          else
          {
            // It this complex or what? :)
            while ((name = (char *)((LONG *)arguments[0])[i++]))
            {
              // Lock given name
              if ((dirlock = Lock(name, ACCESS_READ)) != NULL)
              {
                Examine(dirlock, &fib);
                if (fib.fib_DirEntryType > 0)
                  ScanDir(cinfo, dirlock);    // It's a directory
                else
                  UnpackFile(cinfo, name);    // It's a file
                UnLock(dirlock);
              }
              else
                UnpackFile(cinfo, name);      // Couldn't lock
            }
          }
          upFreeCInfo(cinfo);
        }
        CloseLibrary(UnpackBase);
      }
      else
        fprintf(stderr,"You need the unpack.library version 42 or higher\n");
    }
    else
      fprintf(stderr,"You MUST use either the PATH or the TO/DEST keyword\n");

  FreeArgs(rda);
  }
  else
    PrintFault(IoErr(),NULL);
}


void ScanDir (struct UnpackInfo *ui, BPTR mylock)
{
struct FileInfoBlock fib;

  oldlock = CurrentDir(mylock);         // Set current directory

  if (Examine(mylock, &fib))
    while (ExNext(mylock, &fib))        // Test all files in directory
    {
      if (fib.fib_DirEntryType < 0)
        UnpackFile(ui, fib.fib_FileName); // We got a file
      else
      {
        PrintName(fib.fib_FileName);    // Print out directory name
        printf(" (DIR)\n");
      }
    }

  CurrentDir(oldlock);                  // Set current directory back
}


void UnpackFile (struct UnpackInfo *info, char *name)
{
LONG CrunchLen;
int j;
BPTR winhd;

  winhd = Output();
  PrintName(name);

  if (upDetermineFile(info,name))
  {
    CrunchLen=info->ui_CrunchLen;
    if ((info->ui_CrunchType & 0x7f) == CRU_Track)
      printf("Can't unpack track crunched files\n");
    else
    {
      if ((info->ui_CrunchType & 0x7f) == CRU_Archive)
        if (arguments[1] == NULL)
        {
          printf("No PATH argument given, can't unpack!!\n");
          return;
        }

      if (upUnpack(info) != 0)
      {
        printf("%s\n",info->ui_CruncherName);
        for (j=0;j<30;j++)
          Write(winhd," ",1);
        printf("%6ld -> %6ld\n",CrunchLen,DecrunchLen);
      }
      else
        printf("%s\n",info->ui_ErrorMsg);
    }
  }
  else
    printf("%s\n",info->ui_ErrorMsg);
}


void PrintName(char *name)
{
int j,k;
BPTR winhd;

  winhd = Output();
  k = strlen(name);

  Write(winhd,name,k);
  if ((k = 30-k) < 0)
    k=1;

  for (j=0;j<k;j++)
    Write(winhd," ",1);
}

__asm void Saver(register __a1 ULONG userdata, register __a2 struct UnpackInfo *ui)
{
char *filename;
char realname[2*108];
int  namelen;
BPTR fh;

  // This if statement shouldn't you have if you make a virus scanner
  // routine, because your function will be called for each file in an
  // archive, so you just get the filename or whatever you want to
  // scan the file. Note that the unpack library will read the file
  // into memory, so you just have to look at UI_DecrunchAdr and
  // UI_DecrunchLen to get the filedata.
  if ((ui->ui_CrunchType & 0x7f) != CRU_Archive)  // Only Save if the
  {                                               // file wasn't an archive.
    /* The next lines is very important!!!!!
       This demostrates how you will get the
       right filename. You has to do this if
       you want to handle recursive files,
       like a powerpacked file in a lha archive. */

    filename = ui->ui_LoadNamePoi;
    if (ui->ui_UseFilenamePointer == 0)
    {
      filename = realname;

      if (arguments[1] != NULL)   // Check to see if the user has the PATH keyword
      {
        namelen = stccpy(realname,(const char *)arguments[1],2*108) - 1;
        if ((realname[namelen-1] != ':') && (realname[namelen-1] != '/'))
        {
          realname[namelen++] = '/';
          realname[namelen] = NULL;
        }
        stccpy(realname+namelen,(const char *)FilePart((STRPTR)ui->ui_Filename),2*108-namelen);
      }
      else    // Well, the user has the DEST keyword, so we just copy the filename
        stccpy(realname,(const char *)arguments[2],2*108) - 1;
    }

  // Now we have to save the file.
  if ((fh = Open(filename,MODE_NEWFILE)) != NULL)
  {
    Write(fh,ui->ui_DecrunchAdr,ui->ui_DecrunchLen);
    Close(fh);
    DecrunchLen=ui->ui_DecrunchLen;
  }
  }
}
