#include "exec/types.h"
#include "exec/ports.h"
#include "exec/libraries.h"
#include "exec/io.h"
#include "exec/tasks.h"
#include "exec/execbase.h"
#include "libraries/dos.h"
#include "libraries/dosextens.h"
#include "workbench/workbench.h"
#include "workbench/startup.h"
#include "devices/scsidisk.h"

#include "stdio.h"
#include "time.h"
#include "functions.h"

#define RZ_BMAGIC 0x525a4d42
#define RZ_CMAGIC 0x525a4d43
#define RZ_SCSI "HardFrame.device"
#define RZ_DISK 000
#define RZ_TAPE 001
#define RZ_BLKS 128
#define RZ_BYTS 512
#define RZ_IOR struct IORequest

extern struct ExecBase *SysBase;
struct Library *IconBase;

void dsk2tap();
void tapelst();
void tap2dsk();
long bk_create(char *name);
void bk_d2t();
void bk_extract();
void bk_t2d();
long bk_write(struct FileInfoBlock *fib);
long dk_capacity();
long dk_close();
long dk_init();
long dk_read(long lba,long len);
long dk_write(long lba,long len);
void tmconv(struct DateStamp *date,char *dstr,char *tstr);
long tp_close();
long tp_eof();
long tp_init();
long tp_read(long len);
long tp_rewind();
long tp_sense();
long tp_write(long len);

long disk = RZ_DISK;
long tape = RZ_TAPE;
char scsi[] = RZ_SCSI;

long nest;
char refname[256];
UBYTE dkcmd[32];
UBYTE tpcmd[32];
UWORD data[RZ_BLKS * RZ_BYTS / 2];
ULONG *ldata = (ULONG *)data;
UBYTE *bdata = (UBYTE *)data;
UBYTE *dptr;

struct MsgPort dkmp = {{0,0,NT_MSGPORT,0,0},0,SIGB_SINGLE,0,{0,0,0,0,0}};
struct IOStdReq dkior = {{{0,0,NT_MESSAGE,0,0},&dkmp,0},0,0,0,0,0,0,0,0,0};
struct SCSICmd dksc;
struct MsgPort tpmp = {{0,0,NT_MSGPORT,0,0},0,SIGB_SINGLE,0,{0,0,0,0,0}};
struct IOStdReq tpior = {{{0,0,NT_MESSAGE,0,0},&tpmp,0},0,0,0,0,0,0,0,0,0};
struct SCSICmd tpsc;
struct WBStartup *wbsm;
struct WBArg *wba;
struct DiskObject *dobj;

union FHeader
{
   struct FHBlock
   {
      ULONG magic;
      long nest;
      struct FileInfoBlock fib;
   } fhb;
   UBYTE fhbuf[sizeof(struct FHBlock)];
} fhdr;
struct FileInfoBlock *gfib = &(fhdr.fhb.fib);

/* ---------------------------------------------------------------------- */
main(long argc, char **argv)
{
   long i;
   long err;
   long func;
   char *cptr;
   struct FileLock *olddir;

   fprintf(stderr,"\ntabu -  QIC tape backup utility\n");
   fprintf(stderr,"Copyright 1990 - Roy C. Sigsbey\n\n");
   if (argc == 0)
   {
      if ((IconBase = OpenLibrary("icon.library",0)) == NULL)
      {
         fprintf(stderr,"tabu: Icon library open failed, bye\n");
         fprintf(stderr,"tabu finished, Press RETURN when ready.\n");
         getchar();
         exit(1);
      }
      else
      {
         wbsm = (struct WBStartup *)argv;
         wba = wbsm->sm_ArgList;
         olddir = CurrentDir((struct FileLock *)wba->wa_Lock);
         dobj = GetDiskObject(wba->wa_Name);
         err = 0;
         if ((cptr = FindToolType(dobj->do_ToolTypes,"OPER")) != NULL)
         {
            switch(cptr[0])
            {
               case 'B':
               case 'b':
                  func = 1;
                  break;
               case 'C':
               case 'c':
                  func = 2;
                  break;
               case 'L':
               case 'l':
                  func = 3;
                  break;
               case 'R':
               case 'r':
                  func = 4;
                  break;
               case 'X':
               case 'x':
                  func = 5;
                  break;
               default:
                  err = 1;
                  break;
            }
         }
         else err = 1;
         if ((cptr = FindToolType(dobj->do_ToolTypes,"NAME")) != NULL)
            strcpy(refname,cptr);
         else refname[0] = '\0';
         if ((cptr = FindToolType(dobj->do_ToolTypes,"LIST")) != NULL)
         {
            if (freopen(cptr,"w",stdout) == NULL) err = 1;
         }
         if ((cptr = FindToolType(dobj->do_ToolTypes,"SCSI")) != NULL)
         {
            strcpy(scsi,cptr);
         }
         if ((cptr = FindToolType(dobj->do_ToolTypes,"DISK")) != NULL)
         {
            if (sscanf(cptr,"%3d",&disk) != 1) err = 1;
         }
         if ((cptr = FindToolType(dobj->do_ToolTypes,"TAPE")) != NULL)
         {
            if (sscanf(cptr,"%3d",&tape) != 1) err = 1;
         }
         CurrentDir(olddir);
         CloseLibrary(IconBase);
      }
   }
   else
   {
      err = 0;
      func = 0;
      refname[0] = '\0';
      for (i = 1; i < argc; i++)
      {
         if (argv[i][0] == '-')
         {
            switch (argv[i][1])
            {
               case 'B':
               case 'b':
                  func = 1;
                  break;
               case 'C':
               case 'c':
                  func = 2;
                  break;
               case 'D':
               case 'd':
                  if (sscanf(&(argv[i][2]),"%3d",&disk) != 1) err = 1;
                  break;
               case 'L':
               case 'l':
                  func = 3;
                  break;
               case 'R':
               case 'r':
                  func = 4;
                  break;
               case 'S':
               case 's':
                  strcpy(scsi,&(argv[i][2]));
                  break;
               case 'T':
               case 't':
                  if (sscanf(&(argv[i][2]),"%3d",&tape) != 1) err = 1;
                  break;
               case 'X':
               case 'x':
                  func = 5;
                  break;
               default:
                  err = 1;
                  break;
            }
         }
         else strcpy(refname,argv[i]);
      }
   }
   if (err != 0) func = 0;
   switch (func)
   {
      case 1:
         bk_d2t();
         break;
      case 2:
         dsk2tap();
         break;
      case 3:
         tapelst();
         break;
      case 4:
         bk_t2d();
         break;
      case 5:
         tap2dsk();
         break;
      default:
         fprintf(stderr,
            "usage: tabu [-ddsk] [-sscsi] [-ttap] -b|c|l|r|x [name]\n");
         fprintf(stderr,"     -b = backup by blocks to tape\n");
         fprintf(stderr,"     -c = create backup to tape\n");
         fprintf(stderr,"     -d = dsk is scsi address for disk\n");
         fprintf(stderr,"     -l = list tape contents\n");
         fprintf(stderr,"     -r = restore by blocks from tape\n");
         fprintf(stderr,"     -s = scsi is scsi device name\n");
         fprintf(stderr,"     -t = tap is scsi address for tape\n");
         fprintf(stderr,"     -x = extract from tape\n");
         fprintf(stderr,"   name = reference name (req'd - c or x)\n");
         fprintf(stderr,"tabu.info ToolTypes may be set as follows:\n");
         fprintf(stderr,"   OPER=B      backup\n");
         fprintf(stderr,"   OPER=C      create\n");
         fprintf(stderr,"   OPER=L      list\n");
         fprintf(stderr,"   OPER=R      restore\n");
         fprintf(stderr,"   OPER=X      extract\n");
         fprintf(stderr,"   NAME=name   reference name (req'd - C or X)\n");
         fprintf(stderr,"   LIST=name   list to file or printer\n");
         fprintf(stderr,"   DISK=nnn    disk scsi address\n");
         fprintf(stderr,"   TAPE=nnn    tape scsi address\n");
         fprintf(stderr,"   SCSI=name   scsi device name\n");
   }
   fprintf(stderr,"tabu finished, Press RETURN when ready.\n");
   getchar();
   exit(0);
}
/* ---------------------------------------------------------------------- */
void dsk2tap()
{
   time_t timer;

   if (strlen(refname) == 0)
   {
      fprintf(stderr,"tabu: reference name required for create\n");
      return;
   }
   nest = 0;
   dptr = bdata;
   if (tp_init()) return;
   if (bdata[8] & 0x10)
   {
      fprintf(stderr,"tabu: tape is write protected\n");
      tp_close();
      return;
   }
   time(&timer);
   printf("\ntabu: backup %s\n",ctime(&timer));
   if (bk_create(refname))
   {
      tp_close();
      return;
   }
   if (dptr != bdata)
   {
      if (tp_write(((long)(dptr - bdata) / RZ_BYTS)))
      {
         tp_close();
         return;
      }
   }
   tp_eof();
   tp_close();
   return;
}
/* ---------------------------------------------------------------------- */
void tapelst()
{
   long i;
   long first_time;
   char datestr[16];
   char timestr[16];

   if (tp_init()) return;
   first_time = 1;
   tpsc.scsi_Actual = RZ_BYTS * RZ_BLKS;
   while (tpsc.scsi_Actual == (RZ_BYTS * RZ_BLKS))
   {
      if (tp_read(RZ_BLKS))
      {
         tp_close();
         return;
      }
      if (first_time)
      {
         if ((bdata[0] != 'R') || (bdata[1] != 'Z') || (bdata[2] != 'M'))
         {
            fprintf(stderr,"tabu: tape is not a tabu backup\n");
            break;
         }
         if (bdata[3] == 'B')
         {
            fprintf(stderr,"tabu: tape is a tabu disk image backup\n");
            fprintf(stderr,"   backup date = %s\n",&(bdata[4]));
            break;
         }
         first_time = 0;
      }
      for (dptr = bdata; dptr < (bdata + tpsc.scsi_Actual); dptr += RZ_BYTS)
      {
         if ((dptr[0] == 'R') && (dptr[1] == 'Z') && (dptr[2] == 'M') &&
             (dptr[3] == 'C'))
         {
            for (i = 0; i < sizeof(struct FHBlock); i++)
               fhdr.fhbuf[i] = dptr[i];
            tmconv(&(gfib->fib_Date),datestr,timestr);
            printf("l - %08x %s %s %9d ",gfib->fib_Protection,datestr,
               timestr,gfib->fib_Size);
            for (i = 0; i < fhdr.fhb.nest; i++)
               printf("|  ");
            printf("%s\n",gfib->fib_FileName);
         }
      }
   }
   tp_close();
   return;
}
/* ---------------------------------------------------------------------- */
void tap2dsk()
{
   struct FileInfoBlock *fib;
   struct FileLock *fl,*ofl;

/* access refname which must be a directory or disk */
   if (strlen(refname) == 0)
   {
      fprintf(stderr,"tabu: reference name required for extract\n");
      return;
   }
   fib = (struct FileInfoBlock *)AllocMem(sizeof(struct FileInfoBlock),0);
   if (fib == NULL)
   {
      fprintf(stderr,"tabu: tap2dsk, fib allocation failed\n");
      return;
   }
   if ((fl = Lock(refname,ACCESS_READ)) == NULL)
   {
      FreeMem(fib,sizeof(struct FileInfoBlock));
      fprintf(stderr,"tabu: tap2dsk, Lock of %s failed\n",refname);
      return;
   }
   if (!Examine(fl,fib))
   {
      UnLock(fl);
      FreeMem(fib,sizeof(struct FileInfoBlock));
      fprintf(stderr,"tabu: tap2dsk, Examine of %s failed\n",refname);
      return;
   }
   if(fib->fib_DirEntryType <= 0)
   {
      UnLock(fl);
      FreeMem(fib,sizeof(struct FileInfoBlock));
      fprintf(stderr,"tabu: reference for extract must be a directory\n");
      return;
   }

/* initialize tape */
   if (tp_init())
   {
      UnLock(fl);
      FreeMem(fib,sizeof(struct FileInfoBlock));
      return;
   }

/* cd to refname and ready to extract tape contents into it */
   ofl = CurrentDir(fl);
   tpsc.scsi_Actual = RZ_BYTS * RZ_BLKS;
   dptr = bdata + tpsc.scsi_Actual;

/* extract tape contents */
   nest = 0;
   bk_extract();

/* done - cd back to where we were before */
   ofl = CurrentDir(ofl);
   UnLock(fl);
   FreeMem(fib,sizeof(struct FileInfoBlock));
   tp_close();
   return;
}
/* ---------------------------------------------------------------------- */
long bk_create(char *name)
{
   long i;
   long fin;
   struct FileInfoBlock *fib;
   struct FileLock *fl,*ofl;
   char datestr[16];
   char timestr[16];

/* access the named object */
   fib = (struct FileInfoBlock *)AllocMem(sizeof(struct FileInfoBlock),0);
   if (fib == NULL)
   {
      fprintf(stderr,"tabu: bk_create, fib allocation failed\n");
      return 1;
   }
   if ((fl = Lock(name,ACCESS_READ)) == NULL)
   {
      fprintf(stderr,"tabu: bk_create, Lock of %s failed\n",name);
      FreeMem(fib,sizeof(struct FileInfoBlock));
      return 1;
   }
   if (!Examine(fl,fib))
   {
      fprintf(stderr,"tabu: bk_create, Examine of %s failed\n",name);
      UnLock(fl);
      FreeMem(fib,sizeof(struct FileInfoBlock));
      return 1;
   }

/* Don't write disk device block to tape */
   if (name[strlen(name) - 1] == ':') nest--;
   else
   {

/* print line describing current object name */
      tmconv(&(fib->fib_Date),datestr,timestr);
      printf("c - %08x %s %s %9d ",fib->fib_Protection,datestr,timestr,
         fib->fib_Size);
      for (i = 0; i < nest; i++)
         printf("|  ");
      printf("%s\n",fib->fib_FileName);

/* build tape image of FileInfoBlock and write it to tape */
      fhdr.fhb.magic = RZ_CMAGIC;
      fhdr.fhb.nest = nest;
      gfib->fib_DiskKey = fib->fib_DiskKey;
      gfib->fib_DirEntryType = fib->fib_DirEntryType;
      strcpy(gfib->fib_FileName,fib->fib_FileName);
      for (i = (strlen(gfib->fib_FileName) + 1); i < 108; i++)
         gfib->fib_FileName[i] = '\0';
      gfib->fib_Protection = fib->fib_Protection;
      gfib->fib_EntryType = fib->fib_EntryType;
      gfib->fib_Size = fib->fib_Size;
      gfib->fib_NumBlocks = fib->fib_NumBlocks;
      gfib->fib_Date.ds_Days = fib->fib_Date.ds_Days;
      gfib->fib_Date.ds_Minute = fib->fib_Date.ds_Minute;
      gfib->fib_Date.ds_Tick = fib->fib_Date.ds_Tick;
      for (i = 0; i < 80; i++)
         gfib->fib_Comment[i] = fib->fib_Comment[i];
      for (i = 0; i < 36; i++)
         gfib->fib_Reserved[i] = fib->fib_Reserved[i];
      for (i = 0; i < RZ_BYTS; i++)
      {
         if (i < sizeof(struct FHBlock)) dptr[i] = fhdr.fhbuf[i];
         else dptr[i] = 0x00;
      }
      dptr += RZ_BYTS;
      if (dptr >= (bdata + (RZ_BLKS * RZ_BYTS)))
      {
         dptr = bdata;
         if (tp_write(RZ_BLKS))
         {
            UnLock(fl);
            FreeMem(fib,sizeof(struct FileInfoBlock));
            return 1;
         }
      }
   }

/* if object is a directory: */
   if(fib->fib_DirEntryType > 0)
   {
      nest++;
      ofl = CurrentDir(fl);
      fin = 0;
      while (fin == 0)
      {
         if (ExNext(fl,fib))
         {
            if (bk_create(fib->fib_FileName)) fin = 2;
         }
         else
         {
            if ((fin = IoErr()) != ERROR_NO_MORE_ENTRIES)
            {
               fprintf(stderr,"tabu: ExNext error = %d\n",fin);
               fin = 2;
            }
            else fin = 1;
         }
      }
      ofl = CurrentDir(ofl);
      nest--;
   }

/* else it is a file: */
   else
   {
      fin = 1;
      if (bk_write(fib)) fin++;
   }

/* done with this object */
   UnLock(fl);
   FreeMem(fib,sizeof(struct FileInfoBlock));
   return --fin;
}
/* ---------------------------------------------------------------------- */
void bk_d2t()
{
   long size;
   time_t timer;

   if (dk_init()) return;
   if (tp_init())
   {
      dk_close();
      return;
   }
   ldata[0] = RZ_BMAGIC;
   time(&timer);
   strcpy(&(bdata[4]),ctime(&timer));
   if (tp_write(1))
   {
      tp_close();
      dk_close();
      return;
   }
   dksc.scsi_Actual = RZ_BYTS * RZ_BLKS;
   for (size = 0; dksc.scsi_Actual == (RZ_BYTS * RZ_BLKS);
      size += dksc.scsi_Actual)
   {
      if (dk_read((size / RZ_BYTS),RZ_BLKS)) break;
      if (tp_write(dksc.scsi_Actual / RZ_BYTS)) break;
   }
   fprintf(stderr,"tabu: backup bytes = %d\n",size);
   tp_eof();
   tp_close();
   dk_close();
}
/* ---------------------------------------------------------------------- */
void bk_extract()
{
   long i;
   long wrsz;
   long timer;
   char namestr[256];
   char datestr[16];
   char timestr[16];
   struct FileLock *fl,*ofl;
   struct FileHandle *fhndl;

/* normal exit is nest level change or end of tape */
   while (1)
   {

/* ready next data block from tape */
      if (dptr >= bdata + tpsc.scsi_Actual)
      {
         if (tpsc.scsi_Actual != (RZ_BYTS * RZ_BLKS)) return;
         if (tp_read(RZ_BLKS)) return;
         dptr = bdata;
      }

/* copy tape block to header area */
      for (i = 0; i < sizeof(struct FHBlock); i++)
         fhdr.fhbuf[i] = dptr[i];

/* this had better be header block, not data block */
      if (fhdr.fhb.magic != RZ_CMAGIC)
      {
         fprintf(stderr,"tabu: tape is not tabu file image backup\n");
         return;
      }

/* this item is a sibling to a higher nest level (lower number) */
      if (fhdr.fhb.nest < nest) return;

/* we should never have a nest jump greater than one */
      if (fhdr.fhb.nest > nest)
      {
         fprintf(stderr,"tabu: bk_extract, header nest error\n");
         return;
      }

/* List header block being processed */
      tmconv(&(gfib->fib_Date),datestr,timestr);
      printf("x - %08x %s %s %9d ",gfib->fib_Protection,datestr,timestr,
         gfib->fib_Size);
      for (i = 0; i < fhdr.fhb.nest; i++)
         printf("|  ");
      printf("%s\n",gfib->fib_FileName);

/* set up name for SetDate fexecl call */
      sprintf(namestr,"\"%s\"",gfib->fib_FileName);

/* Directory header block processing */
      if(gfib->fib_DirEntryType > 0)
      {
         dptr += RZ_BYTS;
         if ((fl = Lock(gfib->fib_FileName,ACCESS_READ)) == NULL)
         {
            if ((fl = CreateDir(gfib->fib_FileName)) == NULL)
            {
               fprintf(stderr,"tabu: CreateDir failed for %s\n",
                  gfib->fib_FileName);
               dptr = bdata;
               tpsc.scsi_Actual = 0;
               return;
            }
         }
         ofl = CurrentDir(fl);
         nest++;
         bk_extract();
         nest--;
         ofl = CurrentDir(ofl);
         UnLock(fl);
         SetProtection(gfib->fib_FileName,gfib->fib_Protection);
         SetComment(gfib->fib_FileName,gfib->fib_Comment);
         fexecl("SetDate","SetDate",namestr,datestr,timestr,NULL);
      }

/* File block(s) processing */
      else
      {

/* File header block */
         if ((dptr += RZ_BYTS) >= bdata + tpsc.scsi_Actual)
         {
            if (tpsc.scsi_Actual != (RZ_BYTS * RZ_BLKS))
            {
               fprintf(stderr,
                  "tabu: extract, tape eof before file size satisfied\n");
               return;
            }
            if (tp_read(RZ_BLKS)) return;
            dptr = bdata;
         }
         if ((fl = Lock(gfib->fib_FileName,ACCESS_WRITE)) == NULL)
         {
            if ((fhndl = Open(gfib->fib_FileName,MODE_NEWFILE))
               == NULL)
            {
               fprintf(stderr,"tabu: bk_extract, Open failed for %s\n",
                  gfib->fib_FileName);
               dptr = bdata;
               tpsc.scsi_Actual = 0;
               return;
            }
         }
         else
         {
            UnLock(fl);
            if ((fhndl = Open(gfib->fib_FileName,MODE_READWRITE))
               == NULL)
            {
               fprintf(stderr,"tabu: bk_extract, Open failed for %s\n",
                  gfib->fib_FileName);
               dptr = bdata;
               tpsc.scsi_Actual = 0;
               return;
            }
         }

/* File data block(s) */
         for (i = gfib->fib_Size; i > 0; i -= RZ_BYTS)
         {
            if (i >= RZ_BYTS) wrsz = RZ_BYTS;
            else wrsz = i;
            if (Write(fhndl,(char *)dptr,wrsz) != wrsz)
            {
               fprintf(stderr,"tabu: bk_extract, file data write failed\n");
               Close(fhndl);
               SetProtection(gfib->fib_FileName,gfib->fib_Protection);
               SetComment(gfib->fib_FileName,gfib->fib_Comment);
               fexecl("SetDate","SetDate",namestr,datestr,timestr,NULL);
               dptr = bdata;
               tpsc.scsi_Actual = 0;
               return;
            }
            if ((dptr += RZ_BYTS) >= (bdata + tpsc.scsi_Actual))
            {
               if (tpsc.scsi_Actual != (RZ_BYTS * RZ_BLKS))
               {
                  if ((i - RZ_BYTS) > 0) fprintf(stderr,
                     "tabu: extract, tape eof before file size satisfied\n");
                  Close(fhndl);
                  SetProtection(gfib->fib_FileName,gfib->fib_Protection);
                  SetComment(gfib->fib_FileName,gfib->fib_Comment);
                  fexecl("SetDate","SetDate",namestr,datestr,timestr,NULL);
                  return;
               }
               if (tp_read(RZ_BLKS))
               {
                  Close(fhndl);
                  SetProtection(gfib->fib_FileName,gfib->fib_Protection);
                  SetComment(gfib->fib_FileName,gfib->fib_Comment);
                  fexecl("SetDate","SetDate",namestr,datestr,timestr,NULL);
                  return;
               }
               dptr = bdata;
            }
         }
         Close(fhndl);
         SetProtection(gfib->fib_FileName,gfib->fib_Protection);
         SetComment(gfib->fib_FileName,gfib->fib_Comment);
         fexecl("SetDate","SetDate",namestr,datestr,timestr,NULL);
      }
   }
}
/* ---------------------------------------------------------------------- */
void bk_t2d()
{
   long size;

   if (tp_init()) return;
   if (tp_read(1))
   {
      tp_close();
      return;
   }
   if (ldata[0] != RZ_BMAGIC)
   {
      fprintf(stderr,"tabu: tape is not a tabu disk image backup\n");
      tp_close();
      return;
   }
   fprintf(stderr,"tabu: backup date = %s\n",&(bdata[4]));
   if (dk_init())
   {
      tp_close();
      return;
   }
   tpsc.scsi_Actual = RZ_BYTS * RZ_BLKS;
   for (size = 0; tpsc.scsi_Actual == (RZ_BYTS * RZ_BLKS);
      size += tpsc.scsi_Actual)
   {
      if (tp_read(RZ_BLKS)) break;
      if (dk_write((size / RZ_BYTS),(tpsc.scsi_Actual / RZ_BYTS))) break;
   }
   fprintf(stderr,"tabu: restore bytes = %d\n",size);
   dk_close();
   tp_close();
}
/* ---------------------------------------------------------------------- */
long bk_write(struct FileInfoBlock *fib)
{
   long recd;
   long remn;
   long totl;
   struct FileHandle *fhndl;

   if ((fhndl = Open(fib->fib_FileName,MODE_OLDFILE)) == NULL)
   {
      fprintf(stderr,"tabu: file Open error = %d\n",IoErr());
      return 1;
   }
   totl = 0;
   remn = (RZ_BYTS * RZ_BLKS) - (long)(dptr - bdata);
   while ((recd = Read(fhndl,(char *)dptr,remn)) == remn)
   {
      dptr = bdata;
      if (tp_write(RZ_BLKS))
      {
         Close(fhndl);
         return 1;
      }
      totl += recd;
      remn = (RZ_BYTS * RZ_BLKS);
   }
   if (recd != 0)
   {
      if (recd > 0)
      {
         totl += recd;
         for (; (recd % RZ_BYTS) != 0; recd++)
            dptr[recd] = 0x00;
         dptr += recd;
         if (dptr >= (bdata + (RZ_BLKS * RZ_BYTS)))
         {
            dptr = bdata;
            if (tp_write(RZ_BLKS))
            {
               Close(fhndl);
               return 1;
            }
         }
      }
      else
      {
         fprintf(stderr,"tabu: file Read error = %d\n",IoErr());
         Close(fhndl);
         return 1;
      }
   }
   Close(fhndl);
   if (totl != fib->fib_Size)
   {
      fprintf(stderr,"tabu: size error, expected %d, received %d\n",
         fib->fib_Size,totl);
      return 1;
   }
   return 0;
}
/* ---------------------------------------------------------------------- */
long dk_capacity()
{
   dksc.scsi_Data = data;
   dksc.scsi_Length = 8;
   dksc.scsi_Command = dkcmd;
   dksc.scsi_CmdLength = 10;
   dksc.scsi_Flags = SCSIF_READ;
   dkcmd[0] = 0x25;   /* read capacity command */
   dkcmd[1] = 0x00;
   dkcmd[2] = 0x00;
   dkcmd[3] = 0x00;
   dkcmd[4] = 0x00;
   dkcmd[5] = 0x00;
   dkcmd[6] = 0x00;
   dkcmd[7] = 0x00;
   dkcmd[8] = 0x00;
   dkcmd[9] = 0x00;
   if (DoIO((RZ_IOR *)&dkior))
   {
      fprintf(stderr,"tabu: dk_capacity, DoIO error = %d\n",dkior.io_Error);
      return 1;
   }
   return 0;
}
/* ---------------------------------------------------------------------- */
long dk_close()
{
   CloseDevice((RZ_IOR *)&dkior);
}
/* ---------------------------------------------------------------------- */
long dk_init()
{
   long i;

   if (FindName(&SysBase->DeviceList,scsi) == NULL)
   {
      fprintf(stderr,"tabu: Device %s not found\n",scsi);
      return 1;
   }
   dkmp.mp_SigTask = (struct Task *)FindTask(NULL);
   NewList(&dkmp.mp_MsgList);
   if (OpenDevice(scsi,disk,(RZ_IOR *)&dkior,0))
   {
      fprintf(stderr,"tabu: Device %s open failed.\n",scsi);
      return 1;
   }
   dkior.io_Command = HD_SCSICMD;
   dkior.io_Length = sizeof(dksc);
   dkior.io_Data = (APTR) &dksc;
   return 0;
}
/* ---------------------------------------------------------------------- */
long dk_read(long lba,long len)
{
   dksc.scsi_Data = data;
   dksc.scsi_Length = len * RZ_BYTS;
   dksc.scsi_Command = dkcmd;
   dksc.scsi_CmdLength = 6;
   dksc.scsi_Flags = SCSIF_READ;
   dkcmd[0] = 0x08;   /* read command */
   dkcmd[1] = (lba & 0x1f0000) >> 16;   /* MSB logical block address */
   dkcmd[2] = (lba & 0xff00) >> 8;      /*     logical block address */
   dkcmd[3] = lba & 0xff;               /* LSB logical block address */
   dkcmd[4] = len & 0xff;               /* number of blocks */
   dkcmd[5] = 0x00;
   if (DoIO((RZ_IOR *)&dkior))
   {
      fprintf(stderr,"tabu: dk_read, DoIO error = %d\n",dkior.io_Error);
      return 1;
   }
   return 0;
}
/* ---------------------------------------------------------------------- */
long dk_write(long lba,long len)
{
   dksc.scsi_Data = data;
   dksc.scsi_Length = len * RZ_BYTS;
   dksc.scsi_Command = dkcmd;
   dksc.scsi_CmdLength = 6;
   dksc.scsi_Flags = SCSIF_WRITE;
   dkcmd[0] = 0x0a;                     /* write command */
   dkcmd[1] = (lba & 0x1f0000) >> 16;   /* MSB logical block address */
   dkcmd[2] = (lba & 0xff00) >> 8;      /*     logical block address */
   dkcmd[3] = lba & 0xff;               /* LSB logical block address */
   dkcmd[4] = len & 0xff;               /* number of blocks */
   dkcmd[5] = 0x00;
   if (DoIO((RZ_IOR *)&dkior))
   {
      fprintf(stderr,"tabu: dk_write, DoIO error = %d\n",dkior.io_Error);
      return 1;
   }
   return 0;
}
/* ---------------------------------------------------------------------- */
void tmconv(struct DateStamp *date,char *dstr,char *tstr)
{
   long timer;
   struct tm *tptr;

   timer = (date->ds_Days + 2922) * 86400;
   timer += date->ds_Minute * 60;
   timer += date->ds_Tick / TICKS_PER_SECOND;
   tptr = localtime((time_t *)(&timer));
   switch (tptr->tm_mon)
   {
      case 0:
         strcpy(tstr,"Jan");
         break;
      case 1:
         strcpy(tstr,"Feb");
         break;
      case 2:
         strcpy(tstr,"Mar");
         break;
      case 3:
         strcpy(tstr,"Apr");
         break;
      case 4:
         strcpy(tstr,"May");
         break;
      case 5:
         strcpy(tstr,"Jun");
         break;
      case 6:
         strcpy(tstr,"Jul");
         break;
      case 7:
         strcpy(tstr,"Aug");
         break;
      case 8:
         strcpy(tstr,"Sep");
         break;
      case 9:
         strcpy(tstr,"Oct");
         break;
      case 10:
         strcpy(tstr,"Nov");
         break;
      case 11:
         strcpy(tstr,"Dec");
         break;
   }
   sprintf(dstr,"%02d-%s-%02d",tptr->tm_mday,tstr,tptr->tm_year);
   sprintf(tstr,"%02d:%02d:%02d",tptr->tm_hour,tptr->tm_min,tptr->tm_sec);
   return;
}
/* ---------------------------------------------------------------------- */
long tp_close()
{
   tp_rewind();
   CloseDevice((RZ_IOR *)&tpior);
   fprintf(stderr,"tabu finished, Press RETURN when ready.\n");
   getchar();
   exit(1);
}
/* ---------------------------------------------------------------------- */
long tp_eof()
{
   tpsc.scsi_Data = NULL;
   tpsc.scsi_Length = 0;
   tpsc.scsi_Command = tpcmd;
   tpsc.scsi_CmdLength = 6;
   tpsc.scsi_Flags = 0;
   tpcmd[0] = 0x10;                         /* write filemark command */
   tpcmd[1] = 0;
   tpcmd[2] = 0;                            /* MSB Number of filemarks */
   tpcmd[3] = 0;                            /*     Number of filemarks */
   tpcmd[4] = 2;                            /* LSB Number of filemarks */
   tpcmd[5] = 0;
   if (DoIO((RZ_IOR *)&tpior))
   {
      fprintf(stderr,"tabu: tp_eof, DoIO error = %d\n",tpior.io_Error);
      return 1;
   }
   return 0;
}
/* ---------------------------------------------------------------------- */
long tp_init()
{
   if (FindName(&SysBase->DeviceList,scsi) == NULL)
   {
      fprintf(stderr,"tabu: Device %s not found\n",scsi);
      return 1;
   }
   tpmp.mp_SigTask = (struct Task *)FindTask(NULL);
   NewList(&tpmp.mp_MsgList);
   if (OpenDevice(scsi,tape,(RZ_IOR *)&tpior,0))
   {
      fprintf(stderr,"tabu: Device %s open failed.\n",scsi);
      return 1;
   }
   tpior.io_Command = HD_SCSICMD;
   tpior.io_Length = sizeof(tpsc);
   tpior.io_Data = (APTR) &tpsc;
   if (tp_sense())
   {
      tp_close();
      return 1;
   }
   if (bdata[8] & 0x40)
   {
      fprintf(stderr,"tabu: No tape in drive!\n");
      return 1;
   }
   if (tp_rewind())
   {
      tp_close();
      return 1;
   }
   return 0;
}
/* ---------------------------------------------------------------------- */
long tp_read(long len)
{
   tpsc.scsi_Data = data;
   tpsc.scsi_Length = len * RZ_BYTS;
   tpsc.scsi_Command = tpcmd;
   tpsc.scsi_CmdLength = 6;
   tpsc.scsi_Flags = SCSIF_READ;
   tpcmd[0] = 0x08;   /* read command */
   tpcmd[1] = 0x01;
   tpcmd[2] = (len & 0xff0000) >> 16;   /* MSB number of blocks */
   tpcmd[3] = (len & 0xff00) >> 8;      /*     number of blocks */
   tpcmd[4] = len & 0xff;               /* LSB number of blocks */
   tpcmd[5] = 0x00;
   if (DoIO((RZ_IOR *)&tpior))
   {
      fprintf(stderr,"tabu: tp_read, DoIO error = %d\n",tpior.io_Error);
      return 1;
   }
   return 0;
}
/* ---------------------------------------------------------------------- */
long tp_rewind()
{
   tpsc.scsi_Data = NULL;
   tpsc.scsi_Length = 0;
   tpsc.scsi_Command = tpcmd;
   tpsc.scsi_CmdLength = 6;
   tpsc.scsi_Flags = 0;
   tpcmd[0] = 0x01;                         /* rewind command */
   tpcmd[1] = 0;
   tpcmd[2] = 0;
   tpcmd[3] = 0;
   tpcmd[4] = 0;
   tpcmd[5] = 0;
   if (DoIO((RZ_IOR *)&tpior))
   {
      fprintf(stderr,"tabu: tp_rewind, DoIO error = %d\n",tpior.io_Error);
      return 1;
   }
   return 0;
}
/* ---------------------------------------------------------------------- */
long tp_sense()
{
   tpsc.scsi_Data = data;
   tpsc.scsi_Length = 14;
   tpsc.scsi_Command = tpcmd;
   tpsc.scsi_CmdLength = 6;
   tpsc.scsi_Flags = SCSIF_READ;
   tpcmd[0] = 0x03;   /* sense command */
   tpcmd[1] = 0x00;
   tpcmd[2] = 0x00;
   tpcmd[3] = 0x00;
   tpcmd[4] = 0x0e;   /* number of sense bytes to fetch */
   tpcmd[5] = 0x00;
   if (DoIO((RZ_IOR *)&tpior))
   {
      fprintf(stderr,"tabu: tp_sense, DoIO error = %d\n",tpior.io_Error);
      return 1;
   }
   return 0;
}
/* ---------------------------------------------------------------------- */
long tp_write(long len)
{
   tpsc.scsi_Data = data;
   tpsc.scsi_Length = len * RZ_BYTS;
   tpsc.scsi_Command = tpcmd;
   tpsc.scsi_CmdLength = 6;
   tpsc.scsi_Flags = SCSIF_WRITE;
   tpcmd[0] = 0x0a;                         /* write command */
   tpcmd[1] = 0x01;
   tpcmd[2] = (len & 0xff0000) >> 16;   /* MSB number of blocks */
   tpcmd[3] = (len & 0xff00) >> 8;      /*     number of blocks */
   tpcmd[4] = len & 0xff;               /* LSB number of blocks */
   tpcmd[5] = 0x00;
   if (DoIO((RZ_IOR *)&tpior))
   {
      fprintf(stderr,"tabu: tp_write, DoIO error = %d\n",tpior.io_Error);
      return 1;
   }
   return 0;
}
/* end of code ---------------------------------------------------------- */
