#define NAME     "StripHunks"
#define REVISION "4"

/* Programmheader

        Name:           StripHunks
        Author:         SDI
        Distribution:   PD
        Description:    removes debug, symbol and name hunks
        Compileropts:   -
        Linkeropts:     -gsi -l amiga

 1.0   08.02.98 : first version
 1.1   12.02.98 : compiled with SAS
 1.2   10.03.98 : fixed SAS local base system
 1.3   03.06.98 : fixed Enforcer hit
 1.4   18.08.98 : wrote own strip routines and removed xfd use
*/

#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/xfdmaster.h>
#include <exec/memory.h>
#include <dos/doshunks.h>
#include "SDI_defines.h"

#define PARAM           "FILES/M,ONLYSHOW/S,ALL/S"
#define PATHSIZE        256

struct Args {
  STRPTR *files;
  ULONG   onlyshow;
  ULONG   all;
};

#ifdef __SASCpp
  #define DOSBase dosbase
  #define ASSIGN_DOS
  #define ASSIGN_SYS	struct ExecBase * SysBase; \
			SysBase = (*((struct ExecBase **) 4));
#else
  struct DosLibrary *	DOSBase = 0;
  struct ExecBase *	SysBase  = 0;
  #define ASSIGN_DOS	DOSBase = dosbase;
  #define ASSIGN_SYS	SysBase = (*((struct ExecBase **) 4));
#endif

LONG StripHunks(ULONG *buf, ULONG *rl, ULONG l);
void DoCopy(ULONG *buf, ULONG j, ULONG skip, ULONG size);

ULONG start(void)
{
  ULONG ret = RETURN_FAIL;
  struct DosLibrary *dosbase;

  ASSIGN_SYS
  { /* test for WB and reply startup-message */
    struct Process *task;
    if(!(task = (struct Process *) FindTask(0))->pr_CLI)
    {
      WaitPort(&task->pr_MsgPort);
      Forbid();
      ReplyMsg(GetMsg(&task->pr_MsgPort));
      return RETURN_FAIL;
    }
  }

  if((dosbase = (struct DosLibrary *) OpenLibrary("dos.library", 37)))
  {
   STRPTR none = 0;
   struct Args args;
   struct RDArgs *rda;

   ASSIGN_DOS
   args.files = 0;
   args.onlyshow = args.all = 0;

   if((rda = ReadArgs(PARAM, (LONG *) &args, 0)))
   {
    struct AnchorPath *APath;

    /* args.files is cleared, when no arguments passed -- OS bug? */
    if(!args.files)
      args.files = &none;

    if((APath = (struct AnchorPath *) AllocMem(sizeof(struct AnchorPath)+
    PATHSIZE, MEMF_PUBLIC|MEMF_CLEAR)))
    {
     ULONG retval = ERROR_NO_MORE_ENTRIES;

     APath->ap_BreakBits = SIGBREAKF_CTRL_C;
     APath->ap_Strlen = PATHSIZE;

     while(*args.files && retval == ERROR_NO_MORE_ENTRIES)
     {
      for(retval = MatchFirst(*args.files, APath); !retval;
      retval = MatchNext(APath))
      {
       if(APath->ap_Flags & APF_DIDDIR)
        APath->ap_Flags &= ~APF_DIDDIR;
       else if(APath->ap_Info.fib_DirEntryType > 0)
       {
        if(args.all)
         APath->ap_Flags |= APF_DODIR;
       }
       else
       {
        BPTR fh;
        LONG err = 0;
               
        if((fh = Open(APath->ap_Buf, MODE_OLDFILE)))
        {
         struct FileInfoBlock *fib;

         if((fib = (struct FileInfoBlock *) AllocDosObject(DOS_FIB, 0)))
         {
          if(ExamineFH(fh, fib))
          {
           ULONG length;

           if((length = fib->fib_Size) > 4)
           {
            ULONG reslength;

            if(Read(fh, &reslength, 4) == 4)
            {
             if(reslength == HUNK_HEADER)
             {
              ULONG *buf;

              if((buf = (ULONG *) AllocMem(length+3, MEMF_ANY|MEMF_CLEAR)))
              {
               if(Read(fh, buf+1, length-4) == length-4)
               {
                *buf = reslength; /* store HUNK ID */

                if((err = StripHunks(buf, &reslength, length)) > 0)
                {
                 if(reslength < length)
                 {
                  Printf("%ld bytes saved on file '%s'\n",
		  length-reslength, APath->ap_Buf);
		  if(err == 2)
                   Printf("File '%s' has empty hunks\n", APath->ap_Buf);
                  if(!args.onlyshow)
                  {
                   Close(fh);
                   if((fh = Open(APath->ap_Buf, MODE_NEWFILE)))
                   {
                    if(Write(fh, buf, reslength) != reslength)
                     Printf("Write error on file '%s'\n", APath->ap_Buf);
                   }
                   else
                    err = 0;
                  }
                 }
                }
                else if(err == -2)
                {
                 Printf("Unknown hunk type in file '%s'\n", APath->ap_Buf);
                 err = 1;
                }
                else if(err == -1)
                {
                 Printf("Strange hunk structure or overlay in file '%s'\n", APath->ap_Buf);
                 err = 1;
                } /* else StripHunks */
               } /* Read */
               FreeMem(buf, length+3);
              } /* AllocMem */
             } /* HUNK_HEADER ? */
             else
              err = 1;
            } /* Read 4 */
           } /* size > 4 */
           else
            err = 1;
          } /* Examine */
          FreeDosObject(DOS_FIB,fib);
         } /* AlloDosObject */
         if(fh)
          Close(fh);
        }
        if(err < 0)
         Printf("Error on file '%s'\n", APath->ap_Buf);
       }
      }
      MatchEnd(APath);
      ++args.files;
     }
     if(retval == ERROR_NO_MORE_ENTRIES)
      ret = 0;

     FreeMem(APath, sizeof(struct AnchorPath)+PATHSIZE);
    }
    FreeArgs(rda);
   }
   if(ret)
    PrintFault(IoErr(), 0);

   CloseLibrary((struct Library *) dosbase);
  }
  return ret;
}

LONG StripHunks(ULONG *buf, ULONG *rl, ULONG l)
{
  LONG i, j, empty = 0, skipend = 0, ret = 1;

  if(l & 3 || buf[1] || buf[2] != buf[4]+1 || buf[3])
    return -1;

  l >>= 2;
  i = buf[2];
  
  for(j = 0; j < i; ++j)	/* empty hunks */
  {
    if(!buf[5+j])
      ++empty;
  }

  if(empty)
  {
    if(buf[4+j] || empty > 1)
    {
      ret = 2; empty = 0;
    }
    else			/* skip empty hunks at file end */
    {
      DoCopy(buf, 4+(i--), 1, l--);
      --buf[2];
      --buf[4];
    }
  }

  j = (5+i);
  while(j < l)
  {
    if(buf[j] == HUNK_DEBUG || buf[j] == HUNK_NAME)
    {
      i = 2+buf[j+1];
      DoCopy(buf, j, i, l);
      l -= i;
    }
    else if(buf[j] == HUNK_SYMBOL)
    {
      i = j+1;
      while(buf[i])
	i += buf[i]+2;
      i = i-j+1;
      DoCopy(buf, j, i, l);
      l -= i;
    }
    else if((buf[j]&0xFFFFFF) == HUNK_CODE || (buf[j]&0xFFFFFF) == HUNK_DATA)
    {
      if(empty && !buf[j+1])
      {
        DoCopy(buf, j, 2, l);
        l -= 2;
        skipend = 1;
      }
      else
        j += 2+buf[j+1];
    }
    else if((buf[j]&0xFFFFFF) == HUNK_BSS)
    {
      if(empty && !buf[j+1])
      {
        DoCopy(buf, j, 2, l);
        l -= 2;
        skipend = 1;
      }
      else
        j += 2;
    }
    else if(buf[j] == HUNK_END)
    {
      if(skipend)
      {
        DoCopy(buf, j, 1, l--);
        skipend = 0;
      }
      else
        ++j;
    }
    else if(buf[j] == HUNK_RELOC32)
    {
      if(!buf[j+1])			/* empty reloc */
      {
        DoCopy(buf, j, 2, l);
        l -= 2;
      }
      else
      {
        ++j;
        while(buf[j])
        {
	  j += 2+buf[j];
        }
        ++j;
        if(skipend)
          return -1;
      }
    }
    else if(buf[j] == HUNK_DREL32 || buf[j] == HUNK_RELOC32SHORT)
    {
      UWORD *b;

      b = (UWORD *) (buf+j+1);

      if(*b == 0)			/* empty reloc */
      {
        DoCopy(buf, j, 2, l);
        l -= 2;
      }
      else
      {
        while(*b)
        {
	  b += 2+(*b);
        }
        j = (((STRPTR) (b+2)) - ((STRPTR) buf))>>2;
        if(skipend)
          return -1;
      }
    }
    else
      return -2;
  }

  *rl = l<<2;
  return ret;
}

void DoCopy(ULONG *buf, ULONG j, ULONG skip, ULONG size)
{
  ULONG i;

  buf += j;
  size -= j;

  for(i = 0; i < size; ++i)
  {
    buf[i] = buf[i+skip];
  }
}
