#define NAME         "xadUnTar"
#define DISTRIBUTION "(Freeware) "
#define REVISION     "7"
#define DATE	     "12.08.2001"

/* Programmheader

	Name:		xadUnTar
	Author:		SDI
	Distribution:	Freeware
	Description:	dearchives tar archives (also gzipped, bzipped, compressed)
	Compileropts:	-
	Linkeropts:	-gsi -l amiga

 1.0   06.11.00 : first version, based on xadUnFile.c
 1.1   17.12.00 : added fix for octtonum
 1.2   31.03.01 : handles empty file correct now
 1.3   06.05.01 : now first tries for tar and afterwards for XAD type
 1.4   24.05.01 : lots of bug fixes, added support for name extension blocks
 1.5   27.05.01 : listing links works now as expected
 1.6   01.07.01 : added second longname type.
 1.7   12.08.01 : added devices and pipes
*/

#include <proto/xadmaster.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <exec/memory.h>
#include <dos/dosasl.h>
#include <utility/hooks.h>
#include "SDI_version.h"
#include "SDI_compiler.h"
#define SDI_TO_ANSI
#include "SDI_ASM_STD_protos.h"

struct xadMasterBase *	xadMasterBase = 0;
struct DosLibrary *	 DOSBase = 0;
struct ExecBase *	 SysBase  = 0;

#define MINPRINTSIZE	51200	/* 50KB */
#define NAMEBUFSIZE	512
#define PATBUFSIZE	(NAMEBUFSIZE*2+10)

#define PARAM	"FROM/A,DEST=DESTDIR,PASSWORD/K,FILE/M,"	\
		"NAMESIZE/K/N,FFS=OFS/S,SFS/S,"			\
		"INFO=LIST/S,Q=QUIET/S,AM=ASKMAKEDIR/S,"	\
		"OW=OVERWRITE/S,NA=NOABS/S,ND=NODATE/S,"	\
		"NE=NOEXTERN/S,NKP=NOKILLPART/S,NP=NOPROT/S,"	\
		"NT=NOTREE/S,SHORTNAME/S"

#define OPTIONS \
  "FROM       The input archive file (no patterns allowed)\n"		\
  "DESTDIR    The destination directory, not needed with INFO\n"	\
  "PASSWORD   A password for encrypted archives\n"			\
  "FILE       Filename(s) (with patterns) to be extracted\n"		\
  "NAMESIZE   Names with more characters result in rename request\n"	\
  "FFS=OFS    Sets NAMESIZE to 30\n"					\
  "SFS        Sets NAMESIZE to 100\n"					\
  "INFO       Shows archive information without extracting\n"		\
  "QUIET      Turns of progress report and user interaction\n"		\
  "ASKMAKEDIR You get asked before a directory is created\n"		\
  "OVERWRITE  Files are overwritten without asking\n"			\
  "NOABS      Do not extract absolute path name parts\n"		\
  "NODATE     Creation date information gets not extracted\n"		\
  "NOEXTERN   Turns off usage of external clients\n"			\
  "NOKILLPART Do not delete partial or corrupt output files.\n"		\
  "NOPROT     Protection information gets not extracted\n"		\
  "NOTREE     Files are extracted without subdirectories\n"		\
  "SHORTNAME  Do not display path in progress report\n"

struct xHookArgs {
  STRPTR name;
  ULONG size;
  ULONG flags;
  ULONG lastprint;
  ULONG shortname;
};

struct Args {
  STRPTR   from;
  STRPTR   destdir;
  STRPTR   password;
  STRPTR * file;
  LONG *   namesize;
  ULONG    ffs;
  ULONG	   sfs;
  ULONG    info;
  ULONG    quiet;
  ULONG    askmakedir;
  ULONG    overwrite;
  ULONG    noabs;
  ULONG    nodate;
  ULONG    noextern;
  ULONG	   nokillpart;
  ULONG    noprot;
  ULONG    notree;
  ULONG    shortname;

  /* parameter, no ReadArgs part */
  ULONG    directrun;
  ULONG    printerr;
  ULONG    numextract;
};

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 */
};

struct TarBlockInfo {
  struct Args *		Args;
  struct xadArchiveInfo *ArchiveInfo;
  UBYTE			Block[512];
  UBYTE                 LongName[512*2];
  struct TarHeader	Header;
  ULONG			CurSize;
  ULONG			NumFile;
  ULONG			NumDir;
  ULONG                 NumSpecial;
  LONG			NeedToSkip; /* bytes */
  LONG			NeedToSave; /* bytes */
  LONG                  EndBytes;
  struct Hook		Hook;
  struct xHookArgs	HookArgs;
  UBYTE                 Filename[NAMEBUFSIZE];
  UBYTE                 EndMode;
  UBYTE                 LongLinkMode;
  UBYTE                 LongNameMode;
};

/* 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 */
#define TF_LONGNAME     'L'  /* longname block, preceedes the full block */
#define TF_LONGLINK     'K'  /* longlink block, preceedes the full block */

ASM(ULONG) progrhook(REG(a0, struct Hook *), REG(a1, struct xadProgressInfo *));
ASM(ULONG) workhook(REG(a0, struct Hook *), REG(a1, struct xadHookParam *));
static ULONG octtonum(STRPTR oct, LONG width, LONG *ok);
static BOOL checktarsum(struct TarHeader *th);
static LONG handleblock(struct TarBlockInfo *t);
static void ShowProt(ULONG i);
static LONG CheckNameSize(STRPTR name, ULONG size);
static LONG CheckName(STRPTR *pat, STRPTR name);

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)))
  {
    LONG err = 0;
    struct xadMasterBase *xadmasterbase;

    DOSBase = dosbase;
    if((xadmasterbase = (struct xadMasterBase *)
    OpenLibrary("xadmaster.library", 9)))
    {
      struct Args args;
      struct RDArgs *rda;
      
      memset(&args, 0, sizeof(struct Args));
      xadMasterBase = xadmasterbase;

      if((rda = (struct RDArgs *) AllocDosObject(DOS_RDARGS, 0)))
      {
        rda->RDA_ExtHelp = OPTIONS;

        if(ReadArgs(PARAM, (LONG *) &args, rda))
        {
          LONG namesize = 0;

          if(args.namesize && *args.namesize > 0)
            namesize = *args.namesize;
          else if(args.ffs)
            namesize = 30;
          else if(args.sfs)
            namesize = 100;
	  args.namesize = &namesize;

	  if(args.destdir || args.info)
	  {
	    struct xadArchiveInfo *ai;

	    if((ai = (struct xadArchiveInfo *) xadAllocObjectA(XADOBJ_ARCHIVEINFO, 0)))
	    {
	      struct Hook outhook;
	      struct xadFileInfo *fi;

	      memset(&outhook, 0, sizeof(struct Hook));
	      outhook.h_Entry = (ULONG (*)()) workhook;
	      outhook.h_Data = &args;

	      args.directrun = 1;
              /* Try as normal archive (plain tar). */
	      if(!(err = xadGetHookAccess(ai, XAD_OUTHOOK, &outhook, XAD_INFILENAME, args.from, TAG_DONE)))
	      {
	        err = xadHookAccess(XADAC_COPY, ai->xai_InSize, 0, ai);
                xadFreeHookAccess(ai, err ? XAD_WASERROR : TAG_DONE, err, TAG_DONE);
	      }

	      if(!args.numextract && !(SetSignal(0L,0L) & SIGBREAKF_CTRL_C))
	      {
	        args.directrun = 0;
	        if(!(err = xadGetInfo(ai, XAD_INFILENAME, args.from, XAD_NOEXTERN, args.noextern,
	        args.password ? XAD_PASSWORD : TAG_IGNORE, args.password, TAG_DONE)))
	        {
	          fi = ai->xai_FileInfo;

	          if(fi && ai->xai_Client->xc_Identifier != XADCID_TAR)
                    err = xadFileUnArc(ai, XAD_OUTHOOK, &outhook, XAD_ENTRYNUMBER, fi->xfi_EntryNumber, TAG_DONE);
                  xadFreeInfo(ai);
                }
              }

              if(!args.numextract)
                ret = RETURN_FAIL;
              else if(err)
                ret = RETURN_ERROR;
              else
                ret = 0;

	      xadFreeObjectA(ai, 0);
            } /* xadAllocObject */
          }
          else
            SetIoErr(ERROR_REQUIRED_ARG_MISSING);

          FreeArgs(rda);
        } /* ReadArgs */
        FreeDosObject(DOS_RDARGS, rda);
      } /* AllocDosObject */

      if(SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
        SetIoErr(ERROR_BREAK);

      if(!args.quiet)
      {
        if(err && !args.printerr)
	  Printf("\r\033[KAn error occured: %s\n", xadGetErrorText(err));
        else if(ret)
          PrintFault(IoErr(), 0);
      }

      CloseLibrary((struct Library *) xadmasterbase);
    } /* OpenLibrary xadmaster */
    else
      Printf("Could not open xadmaster.library\n");
    CloseLibrary((struct Library *) dosbase);
  } /* OpenLibrary dos */
  return ret;
}

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

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

  if(!*oct)
    *ok = 0;
  else
  {
    while(width-- && *oct >= '0' && *oct <= '7')
     i = (i*8)+*(oct++)-'0';

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

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

  return i;
}

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

  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 0;
  return 1;
}

static void FinishFile(struct TarBlockInfo *t)
{
  struct DateStamp d;
  LONG a, ok;

  if(!t->Args->nodate)
  {
    xadConvertDates(XAD_DATEUNIX, octtonum(t->Header.th_MTime, 12, &ok),
    XAD_MAKELOCALDATE, 1, XAD_GETDATEDATESTAMP, &d, TAG_DONE);
    SetFileDate(t->Filename, &d);
  }
  if(!t->Args->noprot)
  {
    xadConvertProtection(XAD_PROTUNIX, octtonum(t->Header.th_Mode, 8, &ok),
    XAD_GETPROTAMIGA, &a, TAG_DONE);
    SetProtection(t->Filename, a);
  }
}

static LONG handleblock(struct TarBlockInfo *t)
{
  LONG err = 0;

  if(t->CurSize != 512)
  {
    if(t->EndMode)
      t->EndBytes += t->CurSize;
    if(t->ArchiveInfo)
    {
      xadFreeHookAccess(t->ArchiveInfo, XAD_WASERROR, XADERR_INPUT, TAG_DONE);
      xadFreeObjectA(t->ArchiveInfo, 0);
      t->ArchiveInfo = 0;
    }
    if(!t->Args->info && !(SetSignal(0L,0L) & SIGBREAKF_CTRL_C))
    {
      if(!t->Args->directrun || t->NumFile || t->NumDir)
      {
        Printf("Processed");
        if(t->NumFile)
          Printf(" %ld file%s%s", t->NumFile, t->NumFile == 1 ? "" : "s", t->NumDir ? " and" : "");
        if(t->NumDir)
          Printf(" %ld director%s", t->NumDir, t->NumDir == 1 ? "y" : "ies");
        if(!t->NumFile && !t->NumDir)
          Printf(" nothing");
        Printf(".\n");
        if(t->EndBytes)
          Printf("There are %ld additional tar-bytes at fileend.\n", t->EndBytes);
      }
    }
  }
  else if(t->NeedToSkip)
  {
    if(t->NeedToSkip > 512)
      t->NeedToSkip -= 512;
    else
      t->NeedToSkip = 0;
  }
  else if(t->NeedToSave)
  {
    if(t->LongNameMode)
    {
      if(t->LongNameMode == 1)
        CopyMem(t->Block, t->LongName, 512);
      else if(t->LongNameMode == 2)
      {
        CopyMem(t->Block, t->LongName+512, 511);
      }
      ++t->LongNameMode;
      if(t->NeedToSave > 512)
        t->NeedToSave -= 512;
      else
        t->NeedToSave = 0;
    }
    else if(t->NeedToSave > 512)
    {
      t->NeedToSave -= 512;
      if((err = xadHookAccess(XADAC_WRITE, 512, t->Block, t->ArchiveInfo)))
      {
        xadFreeHookAccess(t->ArchiveInfo, XAD_WASERROR, err, TAG_DONE);
        xadFreeObjectA(t->ArchiveInfo, 0);
        t->NeedToSkip = t->NeedToSave;
        t->NeedToSave = 0;
        t->ArchiveInfo = 0;
      }
    }
    else
    {
      err = xadHookAccess(XADAC_WRITE, t->NeedToSave, t->Block, t->ArchiveInfo);
      xadFreeHookAccess(t->ArchiveInfo, err ? XAD_WASERROR : TAG_DONE, err, TAG_DONE);
      xadFreeObjectA(t->ArchiveInfo, 0);
      t->NeedToSave = 0;
      t->ArchiveInfo = 0;
      if(!err)
        FinishFile(t);
    }
  }
  else if(t->EndMode)
  {
    t->EndBytes += 512;
  }
  else /* a file header block */
  {
    xadCopyMem(t->Block, &t->Header, 512);

    if(t->Header.th_Name[0])
    {
      LONG ok, a;
      STRPTR name;

      t->HookArgs.lastprint = 0;
      t->HookArgs.shortname = t->Args->shortname;
      ok = checktarsum(&t->Header); /* check checksum and init ok */
      t->HookArgs.size = octtonum(t->Header.th_Size, 12, &ok);

      if(ok && (t->Header.th_Typeflag == TF_LONGLINK))
      {
        t->LongLinkMode = 1;
        t->NeedToSkip = t->HookArgs.size;
      }
      else if(ok && (t->Header.th_Typeflag == TF_LONGNAME))
      {
        t->LongNameMode = 1;
        t->NeedToSave = t->HookArgs.size;
      }
      else if(ok && (t->Header.th_Typeflag == TF_FILE || t->Header.th_Typeflag == TF_AFILE ||
      t->Header.th_Typeflag == TF_DIR || t->Header.th_Typeflag == TF_SYM || t->Header.th_Typeflag == TF_LINK
      || t->Header.th_Typeflag == TF_CHAR || t->Header.th_Typeflag == TF_BLOCK || t->Header.th_Typeflag == TF_FIFO))
      {
        if(!t->LongNameMode && t->Header.th_Prefix[0])
        {
          STRPTR s, u;

          name = u = t->LongName; s = t->Header.th_Prefix;
          while(*s)
            *(u++) = *(s++);
          if(*(u-1) != '/')
            *(u++) = '/';
          s = t->Header.th_Name;
          while(*s)
            *(u++) = *(s++);
          if(*(u-1) == '/')
            --u;
          *u = 0;
        }
        else
        {
          name = t->LongNameMode ? t->LongName : t->Header.th_Name;
          t->LongNameMode = 0;
        }
        a = strlen(name) + 1;
        if(name[a-2] == '/')
        {
          if(t->Header.th_Typeflag == TF_AFILE || t->Header.th_Typeflag == TF_FILE || t->Header.th_Typeflag == TF_DIR)
	  {
            name[--a-1] = 0;
            t->Header.th_Typeflag = TF_DIR;
          }
        }
        if(t->Args->notree)
          name = FilePart(name);
        if(t->Args->noabs)
        {
          STRPTR f;
	  while(*name == '/' || *name == ':')
            ++name;

          for(f = name; *f; ++f)
            *f = (*f == ':') ? '/' : *f;
        }
        ++t->Args->numextract;
        if(t->Args->info)
        {
	  struct xadDate xd;

          xadConvertDates(XAD_DATEUNIX, octtonum(t->Header.th_MTime, 12, &ok),
          XAD_MAKELOCALDATE, 1, XAD_GETDATEXADDATE, &xd, TAG_DONE);
          xadConvertProtection(XAD_PROTUNIX, octtonum(t->Header.th_Mode, 8, &ok), XAD_GETPROTAMIGA, &a, TAG_DONE);

          if(!(t->NumFile + t->NumDir + t->NumSpecial))
            Printf("Size     Date       Time     Protection       Name\n");

          if(t->Header.th_Typeflag == TF_DIR)
          {
            Printf("   <dir>");
            ++t->NumDir;
          }
          else if(t->Header.th_Typeflag == TF_SYM || t->Header.th_Typeflag == TF_LINK)
          {
            Printf("  <link>");
            ++t->NumSpecial;
          }
          else if(t->Header.th_Typeflag == TF_CHAR || t->Header.th_Typeflag == TF_BLOCK)
          {
            printf("   <dev>");
            ++t->NumSpecial;
          }
          else if(t->Header.th_Typeflag == TF_FIFO)
          {
            printf("  <pipe>");
            ++t->NumSpecial;
          }
          else
          {
            Printf("%8ld", t->HookArgs.size);
            t->NeedToSkip = t->HookArgs.size;
            ++t->NumFile;
          }
          Printf(" %02ld.%02ld.%04ld %02ld:%02ld:%02ld ", xd.xd_Day, xd.xd_Month, xd.xd_Year,
          xd.xd_Hour, xd.xd_Minute, xd.xd_Second);
          ShowProt(a);

	  Printf("%s\n", t->Args->shortname ? FilePart(name): name);
          if(t->Header.th_LinkName[0])
	    Printf("link: %s%s\n", t->Header.th_LinkName, t->LongLinkMode ? "..." : "");
        }
        else
        {
          if(!t->Args->file || CheckName(t->Args->file, name))
          {
            CopyMem(t->Args->destdir, t->Filename, strlen(t->Args->destdir)+1);
            AddPart(t->Filename, name, NAMEBUFSIZE);
            if(t->Header.th_Typeflag == TF_SYM || t->Header.th_Typeflag == TF_LINK)
            {
              if(!t->Args->quiet)
              Printf("Skipped Link\n");
              ++t->NumSpecial;
            }
            else if(t->Header.th_Typeflag == TF_CHAR || t->Header.th_Typeflag == TF_BLOCK)
            {
              if(!t->Args->quiet)
              Printf("Skipped Device\n");
              ++t->NumSpecial;
            }
            else if(t->Header.th_Typeflag == TF_FIFO)
            {
              if(!t->Args->quiet)
              Printf("Skipped Pipe\n");
              ++t->NumSpecial;
            }
            else if(t->Header.th_Typeflag == TF_DIR)
            {
              if(!t->Args->notree)
	      {
	        BPTR a;
                LONG i = 0;
                UBYTE r;
	        ++t->NumDir;
                while(t->Filename[i] && !err)
    		{
      		  for(;t->Filename[i] && t->Filename[i] != '/'; ++i)
                    ;
                  r = t->Filename[i];
      		  t->Filename[i] = 0;
                  if((a = Lock(t->Filename, SHARED_LOCK)))
                    UnLock(a);
        	  else if((a = CreateDir(t->Filename)))
                    UnLock(a);
                  else
            	    err = 1;
            	  t->Filename[i++] = r;
	        }
	        if(!t->Args->quiet)
	        {
	          if(err)
	            Printf("failed to create directory '%s'\n", t->Args->shortname ? FilePart(t->Filename) : t->Filename);
		  else
		    Printf("Created directory   : %s\n", t->Args->shortname ? FilePart(t->Filename) : t->Filename);
		}
	        if(!err)
	        {
	          struct DateStamp d;
	          LONG a;

	          if(!t->Args->nodate)
	          {
	            xadConvertDates(XAD_DATEUNIX, octtonum(t->Header.th_MTime, 12, &ok),
	            XAD_MAKELOCALDATE, 1, XAD_GETDATEDATESTAMP, &d, TAG_DONE);
	            SetFileDate(t->Filename, &d);
	          }
	          if(!t->Args->noprot)
	          {
	            xadConvertProtection(XAD_PROTUNIX, octtonum(t->Header.th_Mode, 8, &ok),
	            XAD_GETPROTAMIGA, &a, TAG_DONE);
	            SetProtection(t->Filename, a);
	          }
	        }
	      }
	    }
	    else
	    {
	      t->NeedToSave = t->HookArgs.size;
	      ++t->NumFile;
	      if(*t->Args->namesize && CheckNameSize(FilePart(t->Filename), *t->Args->namesize))
	        err = XADERR_BREAK;
	      else if((t->ArchiveInfo = xadAllocObjectA(XADOBJ_ARCHIVEINFO, 0)))
	      {
	        if((err = xadGetHookAccess(t->ArchiveInfo, XAD_OUTFILENAME, t->Filename,
		XAD_MAKEDIRECTORY, !t->Args->askmakedir, XAD_OVERWRITE, t->Args->overwrite,
	        XAD_NOKILLPARTIAL, t->Args->nokillpart,  t->Args->quiet ? TAG_IGNORE :
	        XAD_PROGRESSHOOK, &t->Hook, TAG_DONE)))
		{
		  LONG r;

		  xadFreeObjectA(t->ArchiveInfo, 0);
		  t->ArchiveInfo = 0;

		  if(err == XADERR_SKIP)
		    err = 0;
		  else
		  {
                    Printf("An error occured. Continue? (\033[1mY\033[0m|N): ");
                    Flush(Output());
                    SetMode(Input(), TRUE);
                    r = FGetC(Input());
                    SetMode(Input(), FALSE);
                    switch(r)
                    {
                    case 'n': case 'N': case 'q': case 'Q': break;
                    default: err = 0;
                    }
                  }

		  if(!err)
		  {
		    t->NeedToSkip = t->NeedToSave;
		    t->NeedToSave = 0;
		  }
		}
	        else if(!t->NeedToSave)
		{
                  xadFreeHookAccess(t->ArchiveInfo, TAG_DONE);
                  xadFreeObjectA(t->ArchiveInfo, 0);
		  t->ArchiveInfo = 0;
                  FinishFile(t);
		}
	      }
	      else
                err = XADERR_NOMEMORY;
	    }
          }
          else if(t->Header.th_Typeflag == TF_AFILE || t->Header.th_Typeflag == TF_FILE)
            t->NeedToSkip = t->HookArgs.size;
        }
        t->LongLinkMode = 0;
      }
      else
      {
        if(!(t->NumFile + t->NumDir))
        {
          if(!t->Args->directrun)
          {
            Printf("This is no Tar file.\n");
            t->Args->printerr = 1;
          }
        }
        err = XADERR_ILLEGALDATA;
      }
    }
    else
      t->EndMode = 1;
  }

  return err;
}

/* Because of SAS-err, this cannot be SAVEDS */
ASM(ULONG) workhook(REG(a0, struct Hook *hook),
REG(a1, struct xadHookParam *hp))
{
  ULONG err = 0;
  /* This hook gets the data and instead of saving, it calls the handleblock()
  function with 512 byte blocks (tar block size). It is an XAD output hook and
  used by the main routine as output. The hook structure contains an pointer
  to the argument information. output seeking is not supported. */

  switch(hp->xhp_Command)
  {
  case XADHC_WRITE:
    {
      ULONG i, j, k = 0;
      struct TarBlockInfo *t = (struct TarBlockInfo *) hp->xhp_PrivatePtr;

      for(j = hp->xhp_BufferSize; j && !err; j -= i)
      {
        if((i = 512-t->CurSize) > j)
          i = j;
        xadCopyMem(((STRPTR)hp->xhp_BufferPtr)+k, t->Block+t->CurSize, i);
        k += i; t->CurSize += i;
        if(t->CurSize == 512)
        {
          err = handleblock(t);
          t->CurSize = 0;
        }
      }
      hp->xhp_DataPos += hp->xhp_BufferSize;
    }
    break;
  case XADHC_INIT:
    if(!(hp->xhp_PrivatePtr = xadAllocVec(sizeof(struct TarBlockInfo), MEMF_CLEAR)))
      err = XADERR_NOMEMORY;
    else
    {
      struct TarBlockInfo *t = (struct TarBlockInfo *) hp->xhp_PrivatePtr;

      t->Args = (struct Args *) hook->h_Data;
      t->Hook.h_Entry = (ULONG (*)()) progrhook;
      t->Hook.h_Data = &t->HookArgs;
      t->HookArgs.name = t->Filename;
    }
    break;
  case XADHC_FREE:
    if(hp->xhp_PrivatePtr)
    {
      handleblock((struct TarBlockInfo *) hp->xhp_PrivatePtr); /* cleanup call */
      xadFreeObjectA(hp->xhp_PrivatePtr, 0);
      hp->xhp_PrivatePtr = 0;
    }
    /* break; */
  case XADHC_ABORT: /* do nothing */
    break;
  default: err = XADERR_NOTSUPPORTED;
  }

  if(!err && ((SetSignal(0L,0L) & SIGBREAKF_CTRL_C)))
    err = XADERR_BREAK;

  return err;
}

ASM(ULONG) progrhook(REG(a0, struct Hook *hook),
REG(a1, struct xadProgressInfo *pi))
{
  ULONG ret = 0;
  struct xHookArgs *h;
  STRPTR name;
  
  h = (struct xHookArgs *) (hook->h_Data);
  name = h->name;

  switch(pi->xpi_Mode)
  {
  case XADPMODE_ASK:
    ret |= ((struct xHookArgs *) (hook->h_Data))->flags;
    if((pi->xpi_Status & XADPIF_OVERWRITE) && !(ret & (XADPIF_OVERWRITE|XADPIF_SKIP)))
    {
      LONG r;

      Printf("\r\033[KFile '%s' already exists, overwrite? (Y|A|S|\033[1mN\033[0m|Q|R): ",
      pi->xpi_FileName);
      Flush(Output());
      SetMode(Input(), TRUE);
      r = FGetC(Input());
      SetMode(Input(), FALSE);
      switch(r)
      {
      case 'a': case 'A':
	((struct xHookArgs *) (hook->h_Data))->flags |= XADPIF_OVERWRITE;
      case 'y': case 'Y': ret |= XADPIF_OVERWRITE; break;
      default:
      case 's': case 'S': ((struct xHookArgs *) (hook->h_Data))->flags |= XADPIF_SKIP;
      case 'n': case 'N':
        ret |= XADPIF_SKIP; break;
      case 'q': case 'Q': SetSignal(SIGBREAKF_CTRL_C, SIGBREAKF_CTRL_C); break;
      case 'r': case 'R':
        Printf("\r\033[KEnter new (full) name for '%s':", pi->xpi_FileName);
        Flush(Output());
        FGets(Input(), name, NAMEBUFSIZE-1); /* 1 byte less to correct bug before V39 */
        r = strlen(name);
        if(name[r-1] == '\n') /* skip return character */
          name[--r] = 0;
        Printf("\033[1F\033[K"); /* go up one line and clear it */
        if((pi->xpi_NewName = xadAllocVec(++r, MEMF_PUBLIC)))
        {
          while(r--)
            pi->xpi_NewName[r] = name[r];
          ret |= XADPIF_RENAME;
        }
        else
          Printf("No memory to store new name\n");
      }
    }
    if((pi->xpi_Status & XADPIF_ISDIRECTORY))
    {
      LONG r;

      Printf("File '%s' exists as directory, rename? (R|S|\033[1mN\033[0m|Q): ",
      pi->xpi_FileName);
      Flush(Output());
      SetMode(Input(), TRUE);
      r = FGetC(Input());
      SetMode(Input(), FALSE);
      switch(r)
      {
      case 's': case 'S': ret |= XADPIF_SKIP; break;
      case 'q': case 'Q': SetSignal(SIGBREAKF_CTRL_C, SIGBREAKF_CTRL_C); break;
      case 'r': case 'R':
        Printf("\r\033[KEnter new (full) name for '%s':", pi->xpi_FileName);
        Flush(Output());
        FGets(Input(), name, NAMEBUFSIZE-1); /* 1 byte less to correct bug before V39 */
        r = strlen(name);
        if(name[r-1] == '\n') /* skip return character */
          name[--r] = 0;
        Printf("\033[1F\033[K"); /* go up one line and clear it */
        if((pi->xpi_NewName = xadAllocVec(++r, MEMF_PUBLIC)))
        {
          while(r--)
            pi->xpi_NewName[r] = name[r];
          ret |= XADPIF_RENAME;
        }
        else
          Printf("No memory to store new name\n");
      }
    }
    if((pi->xpi_Status & XADPIF_MAKEDIRECTORY) &&
    !(ret & XADPIF_MAKEDIRECTORY))
    {
      Printf("\r\033[KDirectory of file '%s' does not exist, create? (Y|A|S|\033[1mN\033[0m|Q): ",
      name);
      Flush(Output());
      SetMode(Input(), TRUE);
      switch(FGetC(Input()))
      {
      case 'a': case 'A':
	((struct xHookArgs *) (hook->h_Data))->flags |= XADPIF_MAKEDIRECTORY;
      case 'y': case 'Y': ret |= XADPIF_MAKEDIRECTORY; break;
      case 'q': case 'Q': SetSignal(SIGBREAKF_CTRL_C, SIGBREAKF_CTRL_C); break;
      default:
      /*case 's': case 'S': case 'n': case 'N':*/ ret |= XADPIF_SKIP; break;
      }
      SetMode(Input(), FALSE);
    }
    break;
  case XADPMODE_PROGRESS:
    if(pi->xpi_CurrentSize - ((struct xHookArgs *) (hook->h_Data))->lastprint >= MINPRINTSIZE)
    {
      Printf("\r\033[KWrote %8ld of %8ld bytes: %s",
      pi->xpi_CurrentSize, ((struct xHookArgs *) (hook->h_Data))->size, h->shortname ? FilePart(name) : name);
      Flush(Output());
      ((struct xHookArgs *) (hook->h_Data))->lastprint = pi->xpi_CurrentSize;
    }
    break;
  case XADPMODE_END: Printf("\r\033[KWrote %8ld bytes: %s\n", pi->xpi_CurrentSize, h->shortname ? FilePart(name) : name);
    break;
  case XADPMODE_ERROR: Printf("\r\033[K%s: %s\n", h->shortname ? FilePart(name) : name, xadGetErrorText(pi->xpi_Error));
    break;
  }

  if(!(SetSignal(0L,0L) & SIGBREAKF_CTRL_C)) /* clear ok flag */
    ret |= XADPIF_OK;

  return ret;
}

static void ShowProt(ULONG i)
{
  LONG j;
  UBYTE buf[16], *b = "rwedrwedhsparwed";
  
  for(j = 0; j <= 11; ++j)
    buf[j] = (i & (1<<(15-j))) ? b[j] : '-';
  for(; j <= 15; ++j)
    buf[j] = (i & (1<<(15-j))) ? '-' : b[j];

  Printf("%.16s ", buf);
}

static LONG CheckNameSize(STRPTR name, ULONG size)
{
  LONG ret = 0;
  LONG r;

  if((r = strlen(name)) > size)
  {
    UBYTE buf[NAMEBUFSIZE];

    Printf("\r\033[KFilename '%s' exceeds name limit of %ld by %ld, rename? (Y|\033[1mN\033[0m|Q): ", name, size, r-size);

    Flush(Output());
    SetMode(Input(), TRUE);
    r = FGetC(Input());
    SetMode(Input(), FALSE);
    switch(r)
    {
    case 'q': case 'Q': SetSignal(SIGBREAKF_CTRL_C, SIGBREAKF_CTRL_C); ret = 1; break;
    case 'y': case 'Y':
      Printf("\r\033[KEnter new name for '%s':", name);
      Flush(Output());
      FGets(Input(), buf, NAMEBUFSIZE-1); /* 1 byte less to correct bug before V39 */
      r = strlen(buf);
      if(buf[r-1] == '\n') /* skip return character */
        buf[--r] = 0;
      Printf("\033[1F\033[K"); /* go up one line and clear it */
      if(!(ret = CheckNameSize(buf, size)))
      {
        for(r = 0; buf[r]; ++r)
          *(name++) = buf[r];
        *name = 0;
      }
      break;
    }
  }
  return ret;
}

/* would be better to store the pattern parse stuff and do it only once,
but so it is a lot easier */
static LONG CheckName(STRPTR *pat, STRPTR name)
{
  UBYTE buf[PATBUFSIZE];
  while(*pat)
  {
    if(ParsePatternNoCase(*(pat++), buf, PATBUFSIZE) >= 0)
    {
      if(MatchPatternNoCase(buf, name))
        return 1;
    } /* A scan failure means no recognition, should be an error print here */
  }
  return 0;
}
