#ifndef XADMASTER_TAR_C
#define XADMASTER_TAR_C

/* Programmheader

	Name:		Tar.c
	Main:		xadmaster
	Versionstring:	$VER: Tar.c 1.0 (07.09.1998)
	Author:		SDI
	Distribution:	Freeware
	Description:	Tar disk archiver client

 1.0   07.09.98 : first version
*/

#include <proto/xadmaster.h>
#include <proto/exec.h>
#include <dos/dos.h>
#include "SDI_compiler.h"
#define SDI_TO_ANSI
#include "SDI_ASM_STD_protos.h"

#ifndef XADMASTERFILE
#define Tar_Client		FirstClient
#define NEXTCLIENT		0
UBYTE version[] = "$VER: Tar 1.0 (07.09.1998)";
#endif
#define TAR_VERSION		1
#define TAR_REVISION		0

struct TarHeader
{				/* byte offset */
  UBYTE th_Name[100];		/*   0 */
  UBYTE th_Mode[8];		/* 100 */
  UBYTE th_UserID[8];		/* 108 */
  UBYTE th_GroupID[8];		/* 116 */
  UBYTE th_Size[12];		/* 124 */
  UBYTE th_MTime[12];		/* 136 */
  UBYTE th_Checksum[8]; 	/* 148 */
  UBYTE th_Typeflag;		/* 156 */
  UBYTE th_LinkName[100];	/* 157 */
  UBYTE th_Magic[6];		/* 257 */
  UBYTE th_Version[2];		/* 263 */
  UBYTE th_UserName[32];	/* 265 */
  UBYTE th_GroupName[32];	/* 297 */
  UBYTE th_DevMajor[8]; 	/* 329 */
  UBYTE th_DevMinor[8]; 	/* 337 */
  UBYTE th_Prefix[155]; 	/* 345 */
  UBYTE th_Pad[12];		/* 500 */
};

/* Values used in Typeflag field.  */
#define TF_FILE 	'0'  /* Regular file */
#define TF_AFILE	'\0' /* Regular file */
#define TF_LINK 	'1'  /* Link */
#define TF_SYM		'2'  /* Reserved - but GNU tar uses this for links... */
#define TF_CHAR 	'3'  /* Character special */
#define TF_BLOCK	'4'  /* Block special */
#define TF_DIR		'5'  /* Drawer */
#define TF_FIFO 	'6'  /* FIFO special */
#define TF_CONT 	'7'  /* Reserved */

/* Bits used in the mode field, values in octal.  */
#define TM_SUID   	04000 /* Set UID on execution */
#define TM_SGID   	02000 /* Set GID on execution */
#define TM_SVTX   	01000 /* Reserved */
	/* File permissions */
#define TM_UREAD  	00400 /* Read by owner */
#define TM_UWRITE 	00200 /* Write by owner */
#define TM_UEXEC  	00100 /* Execute/search by owner */
#define TM_GREAD  	00040 /* Read by group */
#define TM_GWRITE 	00020 /* Write by group */
#define TM_GEXEC  	00010 /* Execute/search by group */
#define TM_OREAD  	00004 /* Read by other */
#define TM_OWRITE 	00002 /* Write by other */
#define TM_OEXEC 	00001 /* Execute/search by other */

static ULONG octtonum(STRPTR oct, LONG width, LONG *ok)
{
  ULONG i = 0;

  while(width-- && *oct == ' ')
    ++oct;

  while(width-- && *oct >= '0' && *oct <= '7')
   i = (i*8)+*(oct++)-'0';

  if(width > 0 && *oct)	/* an error, set error flag */
    *ok = 0;

  return i;
}

BOOL checktarsum(struct TarHeader *th)
{
  LONG sc, i;
  ULONG uc, checks;
  
  i = 1;
  checks = octtonum(th->th_Checksum, 8, &i);
  if(!i)
    return DOSFALSE;

  for(i = sc = uc = 0; i < 512; ++i)
  {
    sc += ((BYTE *) th)[i];
    uc += ((UBYTE *) th)[i];
  }
  
  for(i = 148; i < 156; ++i)
  {
    sc -= ((BYTE *) th)[i];
    uc -= ((UBYTE *) th)[i];
  }
  sc += 8 * ' ';
  uc += 8 * ' ';
  
  if(checks != uc && checks != (ULONG) sc)
    return DOSFALSE;
  return DOSTRUE;
}

ASM(BOOL) Tar_RecogData(REG(d0, ULONG size), REG(a0, STRPTR data),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  if(data[0] > 0x1F && checktarsum((struct TarHeader *) data))
    return 1;
  else
    return 0;
}

ASM(LONG) Tar_GetInfo(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  struct TarHeader th;
  struct xadFileInfo *fi = 0, *fi2;
  LONG err, size, ok, a, b, d, i, pos, num = 1;
  struct ExecBase * SysBase = xadMasterBase->xmb_SysBase;

  while(!(err = xadHookAccess(XADAC_READ, sizeof(struct TarHeader), &th, ai)))
  {
    if(!th.th_Name[0])
      break;
    ok = checktarsum(&th); /* check checksum and init ok */
    size = octtonum(th.th_Size, 12, &ok);

    pos = ai->xai_InPos;
    if(ok && (th.th_Typeflag == TF_FILE || 
    th.th_Typeflag == TF_AFILE) &&
    err != xadHookAccess(XADAC_INPUTSEEK, (size+511)&~511, 0, ai))
      ok = 0;
    if(ok && (th.th_Typeflag == TF_FILE || th.th_Typeflag == TF_AFILE ||
    th.th_Typeflag == TF_DIR || th.th_Typeflag == TF_SYM ||
    th.th_Typeflag == TF_LINK))
    {
      a = strlen(th.th_Name) + 1;
      if(th.th_Name[a-2] == '/')
      {
	if(th.th_Typeflag == TF_AFILE || th.th_Typeflag == TF_FILE)
	{
	  th.th_Name[--a-1] == 0;
          th.th_Typeflag = TF_DIR;
        }
      }
      
      b = th.th_LinkName[0] ? 1 + strlen(th.th_LinkName) : 0;
      i = th.th_UserName[0] ? 1 + strlen(th.th_UserName) : 0;
      d = th.th_GroupName[0] ? 1 + strlen(th.th_GroupName) : 0;
      
      if(!(fi2 = (struct xadFileInfo *) xadAllocObject(XADOBJ_FILEINFO,
      XAD_OBJNAMESIZE, a+b+i+d, TAG_DONE)))
      {
        err = XADERR_NOMEMORY; break;
      }
      else
      {
        fi2->xfi_PrivateInfo = (APTR) pos;
	if(th.th_Typeflag == TF_LINK || th.th_Typeflag == TF_SYM)
	  fi2->xfi_Flags |= XADFIF_LINK;
	else if(th.th_Typeflag == TF_DIR)
	{
	  fi2->xfi_Flags |= XADFIF_DIRECTORY;
	  size = 0;
	}
        fi2->xfi_CrunchSize = fi2->xfi_Size = size;
        CopyMem(th.th_Name, fi2->xfi_FileName, a-1);
        if(b)
        {
          fi2->xfi_LinkName = fi2->xfi_FileName + a;
          CopyMem(th.th_LinkName, fi2->xfi_LinkName, b-1);
        }
        if(i)
        {
          fi2->xfi_UserName = fi2->xfi_FileName + a + b;
          CopyMem(th.th_UserName, fi2->xfi_UserName, i-1);
        }
        if(d)
        {
          fi2->xfi_GroupName = fi2->xfi_FileName + a + b + i;
          CopyMem(th.th_GroupName, fi2->xfi_GroupName, d-1);
        }
        fi2->xfi_OwnerUID = octtonum(th.th_UserID, 8, &ok);
        fi2->xfi_OwnerGID = octtonum(th.th_GroupID, 8, &ok);

	i = octtonum(th.th_Mode, 8, &ok);
	if(!(i & TM_UREAD))	fi2->xfi_Protection |= FIBF_READ;
	if(!(i & TM_UWRITE))	fi2->xfi_Protection |= FIBF_WRITE;
	if(!(i & TM_UEXEC))	fi2->xfi_Protection |= FIBF_EXECUTE;
	if(i & TM_GREAD)	fi2->xfi_Protection |= FIBF_GRP_READ;
	if(i & TM_GWRITE)	fi2->xfi_Protection |= FIBF_GRP_WRITE;
	if(i & TM_GEXEC)	fi2->xfi_Protection |= FIBF_GRP_EXECUTE;
	if(i & TM_OREAD)	fi2->xfi_Protection |= FIBF_OTR_READ;
	if(i & TM_OWRITE)	fi2->xfi_Protection |= FIBF_OTR_WRITE;
	if(i & TM_OEXEC)	fi2->xfi_Protection |= FIBF_OTR_EXECUTE;

        err = xadConvertDates(XAD_DATEUNIXUTC, octtonum(th.th_MTime, 12, &ok),
        XAD_GETDATEXADDATE, &fi2->xfi_Date, TAG_DONE);

	if(ok && !err)
	{
	  fi2->xfi_EntryNumber = num++;
          if(fi)
            fi->xfi_Next = fi2;
          else
            ai->xai_FileInfo = fi2;
          fi = fi2;
        }
        else
          xadFreeObjectA(fi2, 0);
      }
    }
    if(!ok)
      ai->xai_Flags |= XADAIF_FILECORRUPT;
  }

  return err;
}

ASM(LONG) Tar_UnArchive(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  LONG i;

  if((i = ((LONG) (ai->xai_CurFile->xfi_PrivateInfo)) - ai->xai_InPos))
    if((i = xadHookAccess(XADAC_INPUTSEEK, i, 0, ai)))
      return i;

  return xadHookAccess(XADAC_COPY, ai->xai_CurFile->xfi_Size, 0, ai);
}

ASM(void) Tar_Free(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  struct xadFileInfo *fi, *fi2;

  fi = ai->xai_FileInfo;
  while(fi)
  {
    fi2 = fi->xfi_Next;
    xadFreeObjectA(fi, 0);
    fi = fi2;
  }
  ai->xai_FileInfo = 0;
}

struct xadClient Tar_Client = {
NEXTCLIENT, XADCLIENT_VERSION, 1, TAR_VERSION, TAR_REVISION,
512, XADCF_FILEARCHIVER, XADCID_TAR, "Tar",
(BOOL (*)()) Tar_RecogData, (LONG (*)()) Tar_GetInfo,
(LONG (*)()) Tar_UnArchive, (void (*)()) Tar_Free};

#endif /* XADASTER_TAR_C */
