#define NAME     "StripHunks"
#define REVISION "6"

/* 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
 1.5   04.12.98 : added lots of new options
 1.6   12.12.98 : added sorting for short relocs
*/

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

#define PARAM           "FILES/M,ONLYSHOW/S,ALL/S,SORT=SORTRELOC/S,"	\
			"NOSTRIP/S,NSE=NOSTRIPEMPTY/S,NSN=NOSTRIPNAME/S,"\
			"NSD=NOSTRIPDEBUG/S,NSS=NOSTRIPSYMBOL/S,"	\
			"NSR=NOSTRIPRELOC/S"
#define PATHSIZE        256

struct Args {
  STRPTR *files;
  ULONG   onlyshow;
  ULONG   all;
  ULONG   sortreloc;
  ULONG   nostrip;
  ULONG   nostripempty;
  ULONG   nostripname;
  ULONG   nostripdebug;
  ULONG   nostripsymbol;
  ULONG   nostripreloc;
};

struct ExecBase *SysBase  = 0;

#ifdef __SASC
  #define DOSBase dosbase
  #define ASSIGN_DOS
#else
  struct DosLibrary *	DOSBase = 0;
  #define ASSIGN_DOS	DOSBase = dosbase;
#endif

#define STRIPFLAG_SORTRELOC		(1<<0)
#define STRIPFLAG_NOSTRIPEMPTY		(1<<1)
#define STRIPFLAG_NOSTRIPNAME		(1<<2)
#define STRIPFLAG_NOSTRIPDEBUG		(1<<3)
#define STRIPFLAG_NOSTRIPSYMBOL		(1<<4)
#define STRIPFLAG_NOSTRIPRELOC		(1<<5)

#define STRIPFLAG_SORTFAILED		(1<<10)
#define STRIPFLAG_EMPTY			(1<<11)
#define STRIPFLAG_NAME			(1<<12)
#define STRIPFLAG_DEBUG			(1<<13)
#define STRIPFLAG_SYMBOL		(1<<14)
#define STRIPFLAG_RELOC			(1<<15)
#define STRIPFLAG_RELOCSORT		(1<<16)

#define STRIPFLAG_SORTED		(1<<20)
#define STRIPFLAG_STRIPPEDDEBUG		(1<<21)
#define STRIPFLAG_STRIPPEDEMPTY		(1<<22)
#define STRIPFLAG_STRIPPEDNAME		(1<<23)
#define STRIPFLAG_STRIPPEDSYMBOL	(1<<24)
#define STRIPFLAG_STRIPPEDRELOC		(1<<25)

LONG StripHunks(ULONG *buf, ULONG *rl, ULONG l, ULONG *resflag, ULONG flags);
ULONG DoSort(ULONG *buf, ULONG *resflags, ULONG flags);
ULONG DoSortShort(UWORD *buf, ULONG *resflags, ULONG flags);
LONG SortList(struct RelocData *rd, ULONG size);
void DoCopy(ULONG *buf, ULONG j, ULONG skip, ULONG size);

struct RelocData {
  UWORD num;
  UWORD hunk;
  ULONG data;
};

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

  SysBase = (*((struct ExecBase **) 4));
  { /* 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 = args.sortreloc = args.nostripname =
   args.nostripempty = args.nostripdebug = args.nostripsymbol =
   args.nostrip = args.nostripreloc = 0;

   if((rda = ReadArgs(PARAM, (LONG *) &args, 0)))
   {
    struct AnchorPath *APath;
    ULONG flags = 0;
    
    if(args.sortreloc)		flags |= STRIPFLAG_SORTRELOC;
    if(args.nostripname)	flags |= STRIPFLAG_NOSTRIPNAME;
    if(args.nostripempty)	flags |= STRIPFLAG_NOSTRIPEMPTY;
    if(args.nostripdebug)	flags |= STRIPFLAG_NOSTRIPDEBUG;
    if(args.nostripsymbol)	flags |= STRIPFLAG_NOSTRIPSYMBOL;
    if(args.nostripreloc)	flags |= STRIPFLAG_NOSTRIPRELOC;
    if(args.nostrip)		flags |= STRIPFLAG_NOSTRIPEMPTY|
        STRIPFLAG_NOSTRIPNAME|STRIPFLAG_NOSTRIPDEBUG|
        STRIPFLAG_NOSTRIPSYMBOL|STRIPFLAG_NOSTRIPRELOC;

    /* 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)
               {
                ULONG resflags = 0;
                *buf = reslength; /* store HUNK ID */

                if((err = StripHunks(buf, &reslength, length, &resflags, flags)) > 0)
                {
                 if(reslength < length || (resflags & STRIPFLAG_SORTED))
                 {
                  Printf("File '%s': ", APath->ap_Buf);
                  if(resflags & STRIPFLAG_STRIPPEDNAME)
                    Printf("NAME, ");
                  if(resflags & STRIPFLAG_STRIPPEDDEBUG)
                    Printf("DEBUG, ");
                  if(resflags & STRIPFLAG_STRIPPEDSYMBOL)
                    Printf("SYMBOL, ");
                  if(resflags & STRIPFLAG_STRIPPEDRELOC)
                    Printf("RELOC, ");
                  if(resflags & STRIPFLAG_STRIPPEDEMPTY)
                    Printf("EMPTY ");
                  if(length-reslength)
                    Printf("total %ld bytes stripped", length-reslength);
                  if(resflags & STRIPFLAG_SORTED)
                    Printf("%sRELOC sorted", length-reslength ? ", " : "");
		  Printf("\n");
                  if(!args.onlyshow)
                  {
                   if(ChangeMode(CHANGE_FH,fh,EXCLUSIVE_LOCK))
                   {
                    if(!SetFileSize(fh,0,OFFSET_BEGINNING))
                    {
                     if(Write(fh, buf, reslength) != reslength)
                      Printf("Write error on file '%s'\n", APath->ap_Buf);
                    }
                    else
                      err = 0;
                   }
                   else
                    err = 0;
                  }
                 }
                }
                if(err < 0 || (resflags&(STRIPFLAG_EMPTY|STRIPFLAG_NAME|
                STRIPFLAG_DEBUG|STRIPFLAG_SYMBOL|STRIPFLAG_RELOC|
                STRIPFLAG_SORTFAILED|STRIPFLAG_RELOCSORT)))
                {
                  Printf("File '%s': ", APath->ap_Buf);
                  if(err == -2)
                  {
                    Printf("Unknown hunk type");
                    err = 1;
                  }
                  else if(err == -1)
                  {
                    Printf("Strange hunk structure or overlay");
                    err = 1;
                  } /* else StripHunks */
                  else
                  {
                    if(resflags & STRIPFLAG_EMPTY)
                      Printf("EMPTY, ");
                    if(resflags & STRIPFLAG_NAME)
                      Printf("NAME, ");
                    if(resflags & STRIPFLAG_DEBUG)
                      Printf("DEBUG, ");
                    if(resflags & STRIPFLAG_SYMBOL)
                      Printf("SYMBOL, ");
                    if(resflags & (STRIPFLAG_RELOC|STRIPFLAG_RELOCSORT))
                      Printf("RELOC, ");
                    if(resflags & STRIPFLAG_SORTFAILED)
                      Printf("UNSORTED, ");
                    Printf("still exists");
                  }
                  Printf("\n");
                }
               } /* 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, ULONG *resflags, ULONG flags)
{
  LONG i, j, empty = 0, skipend = 0, ret = 1;
  /* skipend -- should last empty hunk be skipped */

  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 || (flags & STRIPFLAG_NOSTRIPEMPTY))
    {
      *resflags |= STRIPFLAG_EMPTY; empty = 0;
    }
    else			/* skip empty hunks at file end */
    {
      *resflags |= STRIPFLAG_STRIPPEDEMPTY;
      DoCopy(buf, 4+(i--), 1, l--);
      --buf[2];
      --buf[4];
    }
  }

  j = (5+i);
  while(j < l)
  {
    if(buf[j] == HUNK_DEBUG)
    {
      i = 2+buf[j+1];
      if(flags & STRIPFLAG_NOSTRIPDEBUG)
      {
        j = i; *resflags |= STRIPFLAG_DEBUG;
      }
      else
      {
	*resflags |= STRIPFLAG_STRIPPEDDEBUG;
        DoCopy(buf, j, i, l);
        l -= i;
      }
    }
    else if(buf[j] == HUNK_NAME)
    {
      i = 2+buf[j+1];
      if(flags & STRIPFLAG_NOSTRIPNAME)
      {
        j = i; *resflags |= STRIPFLAG_NAME;
      }
      else
      {
	*resflags |= STRIPFLAG_STRIPPEDNAME;
        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;
      if(flags & STRIPFLAG_NOSTRIPSYMBOL)
      {
        j = i; *resflags |= STRIPFLAG_SYMBOL;
      }
      else
      {
	*resflags |= STRIPFLAG_STRIPPEDSYMBOL;
        DoCopy(buf, j, i, l);
        l -= i;
      }
    }
    else if((buf[j]&0xFFFFFF) == HUNK_CODE || (buf[j]&0xFFFFFF) == HUNK_DATA)
    {
      if(empty && !buf[j+1])
      {
	*resflags |= STRIPFLAG_STRIPPEDEMPTY;
        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])
      {
	*resflags |= STRIPFLAG_STRIPPEDEMPTY;
        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 */
      {
        if(flags & STRIPFLAG_NOSTRIPRELOC)
        {
          j += 2; *resflags |= STRIPFLAG_RELOC;
        }
        else
        {
	  *resflags |= STRIPFLAG_STRIPPEDRELOC;
          DoCopy(buf, j, 2, l);
          l -= 2;
        }
      }
      else
      {
        ++j;
        i = DoSort(buf + j, resflags, flags);
        while(buf[j])
        {
	  j += 2+buf[j];
        }
        ++j;
        if(i)
        {
	  *resflags |= STRIPFLAG_STRIPPEDRELOC;
          DoCopy(buf, j, i, l);
          l -= i;
        }
        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 */
      {
        if(flags & STRIPFLAG_NOSTRIPRELOC)
        {
          j += 2; *resflags |= STRIPFLAG_RELOC;
        }
        else
        {
	  *resflags |= STRIPFLAG_STRIPPEDRELOC;
          DoCopy(buf, j, 2, l);
          l -= 2;
        }
      }
      else
      {
        i = DoSortShort(b, resflags, flags);
        while(*b)
        {
	  b += 2+(*b);
        }
        j = (((STRPTR) (b+2)) - ((STRPTR) buf))>>2;
        if(i)
        {
	  *resflags |= STRIPFLAG_STRIPPEDRELOC;
          DoCopy(buf, j, i>>1, l);
          l -= i;
        }
        if(skipend)
          return -1;
      }
    }
    else
      return -2;
  }

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

ULONG DoSort(ULONG *buf, ULONG *resflags, ULONG flags)
{
  ULONG num, size;
  ULONG k, i, j, l;
  struct RelocData *rd;

  if(!(flags & STRIPFLAG_SORTRELOC))
    return 0;

  k = num = 0;
  while(buf[k])
  {
    num += buf[k];
    k += 2+buf[k];
  }

  if(!(rd = (struct RelocData *) AllocMem(sizeof(struct RelocData)*num,MEMF_ANY)))
  {
    *resflags |= STRIPFLAG_SORTFAILED; return 0;
  }

  k = j = l = 0;
  while(buf[k])
  {
    for(i = 0; i < buf[k]; ++i)
    {
      rd[j].hunk = buf[k+1];
      rd[j].num = (flags & STRIPFLAG_NOSTRIPRELOC) ? buf[k+1]*256+l : buf[k+1];
      rd[j++].data = buf[k+2+i];
    }
    k += 2+buf[k];
    ++l;
  }

  size = ++k;
  if(SortList(rd, num))
    *resflags |= STRIPFLAG_SORTED;
  for(l = k = 0; l < num;)
  {
    for(j = 0; rd[l+j].num == rd[l].num && l+j<num; ++j)
      ;
    if(rd[l].hunk == rd[l+j].hunk)
      *resflags |= STRIPFLAG_RELOCSORT;
    buf[k++] = j;
    buf[k++] = rd[l].hunk;
    while(j--)
      buf[k++] = rd[l++].data;
  }
  buf[k++] = 0;

  FreeMem(rd, sizeof(struct RelocData)*num);

  return size-k;
}

ULONG DoSortShort(UWORD *buf, ULONG *resflags, ULONG flags)
{
  ULONG num, size;
  ULONG k, i, j, l;
  struct RelocData *rd;

  if(!(flags & STRIPFLAG_SORTRELOC))
    return 0;

  k = num = 0;
  while(buf[k])
  {
    num += buf[k];
    k += 2+buf[k];
  }

  if(!(rd = (struct RelocData *) AllocMem(sizeof(struct RelocData)*num,MEMF_ANY)))
  {
    *resflags |= STRIPFLAG_SORTFAILED; return 0;
  }

  k = j = l = 0;
  while(buf[k])
  {
    for(i = 0; i < buf[k]; ++i)
    {
      rd[j].hunk = buf[k+1];
      rd[j].num = (flags & STRIPFLAG_NOSTRIPRELOC) ? buf[k+1]*256+l : buf[k+1];
      rd[j++].data = buf[k+2+i];
    }
    k += 2+buf[k];
    ++l;
  }

  if((size = ++k) & 1)
    ++size;
  if(SortList(rd, num))
    *resflags |= STRIPFLAG_SORTED;
  for(l = k = 0; l < num;)
  {
    for(j = 0; rd[l+j].num == rd[l].num && l+j<num; ++j)
      ;
    if(rd[l].hunk == rd[l+j].hunk)
      *resflags |= STRIPFLAG_RELOCSORT;
    buf[k++] = j;
    buf[k++] = rd[l].hunk;
    while(j--)
      buf[k++] = rd[l++].data;
  }
  buf[k++] = 0;
  if(k & 1)
    buf[k++] = 0;

  FreeMem(rd, sizeof(struct RelocData)*num);

  return size-k;
}

LONG SortList(struct RelocData *rd, ULONG size)
{
  ULONG i, j, k;
  LONG res = 0;
  struct RelocData s;

  for(i = 0; i < size; ++i)
  {
    for(j = k = i; j < size; ++j)
    {
      if(rd[j].num < rd[k].num || (rd[j].num == rd[k].num && rd[j].data < rd[k].data))
        k = j;
    }
    if(k != i)
    {
      s = rd[i];
      rd[i] = rd[k];
      rd[k] = s;
      res = 1;
    }
  }
  return res;
}

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];
  }
}
