/*
** $VER: device-handler.c 1.7 (17 Jun 1995)
**
** device-handler - raw device access
**
** (C) Copyright 1995 Marius Gröger
**     All Rights Reserved.
*/

/*F*/ /* history */
/* $HISTORY:
**
** 17 Jun 1995 : 001.007 :  + didn't use SysBase for Exec-Calls
**                          + Improved IoErr(): ERROR_BAD_STREAM_NAME
**                            replaced by ERROR_INVALID_COMPONENT_NAME,
**                            ReadArgs() error passed through to user
** 03 Jun 1995 : 001.006 :  + locyl was broken
**                          + dot conversion starts after first slash
**                          + source code ANSIfied
**                          + added some macros for compiler abstraction
**                          + didn't remove task correctly
** 24 Apr 1995 : 001.005 :  at the end of a media Write()/Read() returned wrong res1,
**                          so multi-volume tar archives shouldn't be a problem any
**                          longer
** 22 Feb 1995 : 001.004 :  some bug-fixing
** 18 Feb 1995 : 001.003 :  made Write() working...
** 20 Dec 1994 : 001.002 :  + one task/multiple FileHandles
**                          + source-code rework
**                          + better stream name scanning by ReadArgs()
**                          + new option MAXLENGTH
**                          + new options LOCYL, HICYL
**                          + highly improved read/write operation
** 18 Dec 1994 : 001.001 :  changed stream name convention
** 08 Dec 1994 : 001.000 :  took over from Matthias Scheler
*/
/*E*/

/*F*/ /* includes */

   /* system protoypes and pragmas */
   /* ensure to use the symbol SysBase and not the hardcoded AbsExecBase */
#include <clib/exec_protos.h>
#include <pragmas/exec_sysbase_pragmas.h>
#include <clib/dos_protos.h>
#include <pragmas/dos_pragmas.h>
#include <clib/utility_protos.h>
#include <pragmas/utility_pragmas.h>

   /* system header files */
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/execbase.h>
#include <devices/trackdisk.h>
#include <dos/dosextens.h>
#include <dos/filehandler.h>
#include <dos/dos.h>

   /* compiler header files */
#include <string.h>

/* fix include files (anyawy I'm not yet using them:
** what about sending me a diff-file ?)
*/
#ifndef ACTION_GET_DISK_FSSM
#define ACTION_GET_DISK_FSSM 4201
#endif
#ifndef ACTION_FREE_DISK_FSSM
#define ACTION_FREE_DISK_FSSM 4202
#endif

#include "device-handler_rev.h"

/*E*/

/*F*/ /* debug */
extern void KPrintF(char *, ...);
/*#define d(x) do { KPrintF("%s:%ld: ", __FUNC__, __LINE__); KPrintF x; } while(0)*/
#define d(x)
/*E*/
/*F*/ /* compiler abstraction */

#ifdef __SASC
#  define ASM     __asm
#  define REG(x)  register __ ## x
#  define INLINE  __inline
#  define REGARGS __regargs
#  define SAVEDS  __saveds
#  define FAR     __far
#  define MIN     __builtin_min
#  define MAX     __builtin_max
#else
#  error Please define the above macros for your compiler
#endif

/*E*/

/*F*/ /* global static storage */

typedef struct GlobalData
{
   struct Library   *gd_SysBase;
   struct Library   *gd_DOSBase;
   struct Library   *gd_UtilityBase;
   struct MsgPort   *gd_Port;
   struct DosList   *gd_DosList;
   struct Process   *gd_We;
} *GD;

#ifdef __SASC
     /* redirect all shared library bases to our device base */
#  define SysBase      gd->gd_SysBase
#  define DOSBase      gd->gd_DOSBase
#  define UtilityBase  gd->gd_UtilityBase
     /* This macro declares a local variable which temporary gets
        SysBase directly from AbsExecBase. */
#  define LOCALSYSBASE struct { VOID *gd_SysBase; } *gd = (VOID*)0x4
#else
#  error Please define library bases for your compiler
#endif
/*E*/
/*F*/ /* handler declarations */

#define STREAMNAME_TEMPLATE "DOSNAME/A,MAXLENGTH,LOCYL/K,HICYL/K"

struct StreamOpts
{
   UBYTE    *so_DOSName;
   UBYTE    *so_MaxLength;
   UBYTE    *so_LoCyl;
   UBYTE    *so_HiCyl;
};

#define BCPL_MAXNAME 256
struct PrivateStreamData
{
   struct IOStdReq  *psd_IO;                    /* Exec I/O Block */
   UBYTE             psd_DOSName[BCPL_MAXNAME]; /* Name of AmigaDOS handler */
   UBYTE             psd_Device[32];            /* OpenDevice()-name */
   ULONG             psd_Unit;                  /* OpenDevice()-unit */
   ULONG             psd_Flags;                 /* OpenDevice()-flags */
   ULONG             psd_BufMemType;            /* memory type needed */
   LONG              psd_LoCyl, psd_HiCyl;      /* cylinder boundaries */
   LONG              psd_SectorSize;            /* length of a sector */
   LONG              psd_Offset;                /* starting sector */
   LONG              psd_MaxLength;             /* max-length of "file" */
   LONG              psd_Pos;                   /* current pos. in "file" */
   LONG              psd_Len;                   /* actual length of "file" */
   UBYTE            *psd_Buffer;                /* r/w buffer */
   LONG              psd_Left;                  /* bytes left in r/w buffer */
   UBYTE            *psd_Ptr;                   /* current pos in buffer */
   BOOL              psd_Write;                 /* file is in write mode */
   struct StreamOpts psd_Param;                 /* ReadArgs() argument */
};


/*E*/

/*F*/ /* prototypes */
STATIC struct DosPacket *WaitDosPacket(GD gd, struct MsgPort *port);
STATIC VOID ReplyDosPacket(GD gd, struct MsgPort *port, struct DosPacket *Packet,LONG Res1,LONG Res2);
STATIC BOOL GetXDULONG(GD gd, UBYTE *s, LONG *number);
STATIC VOID Stream2RDArgs(UBYTE *buffer);
STATIC BOOL StreamSpecs(GD gd, struct PrivateStreamData *psd, BPTR streamname, LONG *res);
STATIC BOOL FindDevice(GD gd, struct PrivateStreamData *psd);
STATIC VOID OpenDH(GD gd, struct DosPacket *Pkt,ULONG *OpenCnt);
STATIC VOID CloseDH(GD gd, struct PrivateStreamData *psd,ULONG *OpenCnt);
STATIC LONG REGARGS RawRead(GD gd, struct PrivateStreamData *psd, UBYTE *Buffer, LONG Bytes, LONG *res2);
STATIC LONG REGARGS RawWrite(GD gd, struct PrivateStreamData *psd,UBYTE *Buffer,LONG Bytes, LONG *res2);
STATIC GD creategd(VOID);
STATIC BOOL openres(GD gd);
STATIC VOID closeres(GD gd);
GLOBAL LONG main(VOID);
/*E*/

/*F*/ GLOBAL LONG SAVEDS main(VOID)
/*
** don't remove GLOBAL, if you don't want to the complete
** source to get optimized out of existence :-)
*/
{
   GD gd;
   struct DosPacket *Pkt;
   BOOL Done;
   ULONG OpenCnt;
   LONG rc = RETURN_OK;

   d(("this is the DEV handler, compiled on "__DATE__"\n"));

   if (gd = creategd())
   {
      if ((gd->gd_We = (struct Process *)FindTask(NULL))->pr_CLI) return RETURN_FAIL;

      d(("waiting for startup-packet...\n"));
      Pkt = WaitDosPacket(gd, &gd->gd_We->pr_MsgPort);

      if (openres(gd))
      {
         gd->gd_DosList = (struct DosList *)BADDR(Pkt->dp_Arg3);
         gd->gd_DosList->dol_Task = gd->gd_Port;

         d(("replying startup-packet...\n"));
         ReplyDosPacket(gd, gd->gd_Port, Pkt, DOSTRUE, Pkt->dp_Res2);

         Done=FALSE;
         OpenCnt=0L;

         d(("entering mainloop\n"));

         while (!Done)
         {
            Pkt=WaitDosPacket(gd, gd->gd_Port);

            d(("got packet %ld\n",Pkt->dp_Type));

            Pkt->dp_Res1 = DOSFALSE;
            Pkt->dp_Res2 = ERROR_ACTION_NOT_KNOWN;

            switch (Pkt->dp_Type)
            {
               case ACTION_FINDINPUT:
               case ACTION_FINDOUTPUT:
                  OpenDH (gd, Pkt, &OpenCnt);
                  ReplyDosPacket(gd, gd->gd_Port,Pkt, Pkt->dp_Res1, Pkt->dp_Res2);
               break;

               case ACTION_READ:
               {
                  struct PrivateStreamData *psd = (struct PrivateStreamData *)Pkt->dp_Arg1;

                  if (psd && !psd->psd_Write)
                  {
                     LONG Bytes, error;

                     Bytes = RawRead(gd, (struct PrivateStreamData *)Pkt->dp_Arg1,
                                         (UBYTE *)Pkt->dp_Arg2,Pkt->dp_Arg3, &error);

                     ReplyDosPacket(gd, gd->gd_Port,Pkt, Bytes, error);
                  }
               }
               break;

               case ACTION_WRITE:
               {
                  struct PrivateStreamData *psd = (struct PrivateStreamData *)Pkt->dp_Arg1;

                  if (psd && psd->psd_Write)
                  {
                     LONG Bytes, error;

                     Bytes = RawWrite(gd, (struct PrivateStreamData *)Pkt->dp_Arg1,
                                          (UBYTE *)Pkt->dp_Arg2, Pkt->dp_Arg3, &error);

                     ReplyDosPacket(gd, gd->gd_Port,Pkt, Bytes, error);
                  }
               }
               break;

               case ACTION_IS_FILESYSTEM:
                  ReplyDosPacket(gd, gd->gd_Port,Pkt, DOSFALSE, 0);
               break;

               case ACTION_SEEK:
               case ACTION_SET_FILE_SIZE:
                  ReplyDosPacket(gd, gd->gd_Port,Pkt, -1,ERROR_ACTION_NOT_KNOWN);
               break;

               case ACTION_END:
                  CloseDH (gd, (struct PrivateStreamData *)Pkt->dp_Arg1,&OpenCnt);
                  ReplyDosPacket(gd, gd->gd_Port,Pkt, DOSTRUE, 0);
                  /* FALLTHROUGH */
               case ACTION_DIE:
                  if (OpenCnt==0L)
                  {
                     d(("opencnt = 0\n"));

                     if (IsListEmpty(&gd->gd_Port->mp_MsgList))
                     {
                        gd->gd_DosList->dol_Task = NULL;

                        if (Pkt->dp_Type == ACTION_DIE)
                           ReplyDosPacket(gd, gd->gd_Port,Pkt, DOSTRUE, 0);

                        Done=TRUE;
                     }
                     else
                     {
                        if (Pkt->dp_Type == ACTION_DIE)
                           ReplyDosPacket(gd, gd->gd_Port,Pkt, DOSFALSE,ERROR_OBJECT_IN_USE);
                     }
                  }
                  else
                  {
                     if (Pkt->dp_Type == ACTION_DIE)
                        ReplyDosPacket(gd, gd->gd_Port,Pkt, DOSFALSE,ERROR_OBJECT_IN_USE);
                  }
               break;

               default:
                  ReplyDosPacket(gd, gd->gd_Port, Pkt, DOSFALSE, ERROR_ACTION_NOT_KNOWN);
               break;
            }
         }
      }
      else
      {
         ReplyDosPacket(gd, &gd->gd_We->pr_MsgPort, Pkt, DOSFALSE, ERROR_NO_FREE_STORE);
         rc = RETURN_FAIL;
      }
      closeres(gd);

      d(("returning\n"));
   }
   else rc = RETURN_FAIL;


   return rc;
}
const STATIC UBYTE version[]=VERSTAG;
/*E*/

/*F*/ STATIC struct DosPacket *WaitDosPacket(GD gd, struct MsgPort *port)

{
   WaitPort(port);
   return (struct DosPacket *)(GetMsg(port)->mn_Node.ln_Name);
}
/*E*/
/*F*/ STATIC VOID ReplyDosPacket(GD gd, struct MsgPort *port, struct DosPacket *Packet,LONG Res1,LONG Res2)
{
   struct MsgPort *reply = Packet->dp_Port;

   Packet->dp_Port = port;
   Packet->dp_Link->mn_Node.ln_Name = (char *)Packet;
   Packet->dp_Res1 = Res1;
   Packet->dp_Res2 = Res2;

   PutMsg (reply, Packet->dp_Link);
}
/*E*/

/*F*/ STATIC BOOL GetXDULONG(GD gd, UBYTE *s, LONG *number)
{

#define IsDigit(c) ((c) >= '0' && (c) <= '9'))
#define IsXDigit(c) (((c) >= '0' && (c) <= '9') || ((c) >= 'A' && (c) <= 'F'))

   BOOL hex, rc = TRUE;
   ULONG num;
   UBYTE c;
   WORD len;

   if (len = strlen(s))
   {
      if (!Strnicmp(s, "0x", 2))
      {
         hex = TRUE;
         s += 2;
      }
      else if (!Strnicmp(s, "$", 2))
      {
         hex = TRUE;
         s++;
      }
      else hex = FALSE;

      for(num=0;*s;s++)
      {
         c = ToUpper((ULONG)*s);
         if (hex)
         {
            if (c >= '0' && c <= '9') num = num * 16 + c - '0';
            else if (c >= 'A' && c <= 'F') num = num * 16 + c - '0';
            else break;
         }
         else
         {
            if (c >= '0' && c <= '9') num = num * 10 + c - '0';
            else break;
         }
      }

      if (*s)
      {
         if (c == 'K') num *= 1024;
         else if (c == 'M') num *= 1024*1024;
         else rc = FALSE;
      }
   }
   else rc = FALSE;

   if (rc) *number = num;

   return rc;
}
/*E*/
/*F*/ STATIC VOID Stream2RDArgs(UBYTE *buffer)
{
   UBYTE *p;
   enum { s_start, s_conv, s_esc, s_quote } s, sb;

   for (p = buffer, sb = s = s_start; *p; p++)
      switch(s)
      {
         case s_start:                     /* no convertion 'til 1st slash */
            if (*p == '"')
            {
               s = s_quote;
               break;
            }
            else if (*p != '/')
               break;
            sb = s = s_conv;
            /* FALLTHROUGH */
         case s_conv:                      /* conversion state */
            if (*p == '.') *p = '=';
            else if (*p == '/') *p = '\040';
            else if (*p == '"') s = s_quote;
         break;
         case s_esc:                       /* (asterisk-)escape state */
            s = s_quote;
         break;
         case s_quote:                     /* quotation state */
            if (*p == '*') s = s_esc;
            if (*p == '"') s = sb;
         break;
      }
}
/*E*/
/*F*/ STATIC BOOL StreamSpecs(GD gd, struct PrivateStreamData *psd, BPTR streamname, LONG *res)
{
   UBYTE *temp, *bstr, *start;
   BOOL rc = FALSE;
   struct RDArgs *inrda, *rda;

   memset(&psd->psd_Param, '\0', (int)sizeof(psd->psd_Param));

   bstr = (UBYTE *)BADDR(streamname);

   if (temp = AllocVec((ULONG)bstr[0]+2, MEMF_ANY))
   {
      if (inrda = AllocDosObject(DOS_RDARGS, NULL))
      {
         strncpy(temp, bstr+1, bstr[0]);
         temp[bstr[0]] = '\n';
         temp[bstr[0]+1] = '\0';

         if (start = (UBYTE*)strchr((char*)temp, ':'))
         {
            Stream2RDArgs(++start);

            inrda->RDA_Source.CS_Buffer = start;
            inrda->RDA_Source.CS_Length = strlen(start);
            inrda->RDA_Source.CS_CurChr = 0;

            psd->psd_MaxLength = 0x7fffffff;
            psd->psd_LoCyl = 0;
            psd->psd_HiCyl = 0x7fffffff;

            d(("readargs(%s)\n",start));
            if (rda = ReadArgs((UBYTE*)STREAMNAME_TEMPLATE, (LONG*)&psd->psd_Param, inrda))
            {
               rc = TRUE;

               strcpy(psd->psd_DOSName, (char*)psd->psd_Param.so_DOSName);

               if (psd->psd_Param.so_MaxLength)
                  rc = GetXDULONG(gd,psd->psd_Param.so_MaxLength, &psd->psd_MaxLength);

               if (rc && psd->psd_Param.so_LoCyl)
                  rc = GetXDULONG(gd,psd->psd_Param.so_LoCyl, &psd->psd_LoCyl);

               if (rc && psd->psd_Param.so_HiCyl)
                  rc = GetXDULONG(gd,psd->psd_Param.so_HiCyl, &psd->psd_HiCyl);

               d(("after ReadArgs(): doshandler %s maxlen %lu locyl %lu hicyl %lu\n",
                  psd->psd_DOSName,psd->psd_MaxLength,psd->psd_LoCyl,psd->psd_HiCyl));

               FreeArgs(rda);
            }
            else
            {
               d(("ReadArgs() failed\n"));
               *res = IoErr();
            }
         }
         else
         {
            d(("no proper name\n"));
            *res = ERROR_INVALID_COMPONENT_NAME;
         }

         FreeDosObject(DOS_RDARGS, inrda);
      }
      else
      {
         d(("no RDArgs\n"));
         *res = ERROR_NO_FREE_STORE;
      }

      FreeVec(temp);
   }
   else
   {
      d(("no more memory\n"));
      *res = ERROR_NO_FREE_STORE;
   }

   return rc;
}
/*E*/

/*F*/ STATIC BOOL FindDevice(GD gd, struct PrivateStreamData *psd)
{
   struct DosList *dol;
   struct FileSysStartupMsg *fssm;
   BOOL rc = FALSE;
   UBYTE *name;
   UBYTE n;
   UBYTE pat[40];

   d(("entered: %s\n", psd->psd_DOSName));

   n = strlen(psd->psd_DOSName);

   if (dol = LockDosList(LDF_READ | LDF_DEVICES))
   {
      while(dol = NextDosEntry(dol, LDF_DEVICES))
      {
         name = (UBYTE*)BADDR(dol->dol_Name);

         memset(pat,0,40);
         strncpy(pat,name+1,name[0]);
         d(("%s\n",pat));

         if ((n == name[0]) && !Strnicmp((char*)(name+1), psd->psd_DOSName, (LONG)n))
         {
            d(("found\n"));

            fssm = (struct FileSysStartupMsg*)BADDR(dol->dol_misc.dol_handler.dol_Startup);

            if (fssm)
            {
               struct DosEnvec *de = (struct DosEnvec *)BADDR(fssm->fssm_Environ);

               d(("found fssm\n"));

                  /* note: fssm->fssm_Device is a NUL-terminated BSTR!! :-) */
               strcpy((char*)psd->psd_Device, (char*)BADDR(fssm->fssm_Device)+1);

               psd->psd_Flags = fssm->fssm_Flags;
               psd->psd_Unit = fssm->fssm_Unit;
               psd->psd_BufMemType = de->de_BufMemType;
               psd->psd_SectorSize = 4 * de->de_SizeBlock;
               if (!psd->psd_Param.so_HiCyl) psd->psd_HiCyl = de->de_HighCyl;
               if (!psd->psd_Param.so_LoCyl) psd->psd_LoCyl = de->de_LowCyl;
               psd->psd_LoCyl = (LONG)MIN((int)psd->psd_LoCyl, (int)psd->psd_HiCyl);
               d(("de->de_BlocksPerTrack %ld, psd->psd_LoCyl %ld, psd->psd_HiCyl %ld, de->de_Surfaces %ld, psd->psd_SectorSize %ld\n",
                   de->de_BlocksPerTrack,     psd->psd_LoCyl,     psd->psd_HiCyl,     de->de_Surfaces,     psd->psd_SectorSize));
               psd->psd_Offset = de->de_BlocksPerTrack * psd->psd_LoCyl * de->de_Surfaces * psd->psd_SectorSize;

               psd->psd_Len = de->de_BlocksPerTrack * psd->psd_SectorSize * (psd->psd_HiCyl - psd->psd_LoCyl + 1) * de->de_Surfaces;
               psd->psd_Len = (LONG)MIN((int)psd->psd_MaxLength, (int)psd->psd_Len);
               d(("len %lu\n",psd->psd_Len));

               d(("device %s, unit %ld, flags %ld, secsiz %ld, off %ld\n",
                  psd->psd_Device,psd->psd_Unit,psd->psd_Flags,psd->psd_SectorSize,psd->psd_Offset));

               rc = TRUE;
            }
            else
               d(("no fssm\n"));

            break;
         }
      }

      UnLockDosList(LDF_READ | LDF_DEVICES);
   }

   return(rc);
}
/*E*/

/*F*/ STATIC VOID OpenDH(GD gd, struct DosPacket *Pkt,ULONG *OpenCnt)
{
   struct FileHandle *FH;
   struct PrivateStreamData *psd;
   struct MsgPort *IOPort;
   BOOL ok = FALSE;

   Pkt->dp_Res1 = DOSFALSE;
   FH = (struct FileHandle *)BADDR(Pkt->dp_Arg1);
   FH->fh_Port = (struct MsgPort *)FALSE;
   FH->fh_Arg1 = 0L;

   d(("allocing psd\n"));
   if (psd = AllocVec(sizeof(*psd),MEMF_PUBLIC|MEMF_CLEAR))
   {
      if (StreamSpecs(gd, psd, (BPTR)Pkt->dp_Arg3, &Pkt->dp_Res2))
      {
         d(("creating msgport\n"));
         if (IOPort=CreateMsgPort())
         {
            if (psd->psd_IO=CreateIORequest(IOPort, sizeof(struct IOStdReq)))
            {
               if (FindDevice(gd, psd))
               {
                  if (OpenDevice(psd->psd_Device, psd->psd_Unit, (struct IORequest *)psd->psd_IO, psd->psd_Flags)==0L)
                  {
                     if (psd->psd_Buffer = AllocVec(psd->psd_SectorSize, psd->psd_BufMemType))
                     {
                        if (Pkt->dp_Type == ACTION_FINDOUTPUT)
                        {
                           psd->psd_Write = TRUE;
                        }
                        else
                        {
                           psd->psd_Write = FALSE;
                           psd->psd_IO->io_Command = CMD_READ;
                        }
                        psd->psd_Ptr = psd->psd_Buffer;
                        psd->psd_Pos = 0;
                        psd->psd_Left = 0;

                        FH->fh_Arg1 = (LONG)psd;

                        Pkt->dp_Res1 = DOSTRUE;
                        Pkt->dp_Res2 = 0L;

                        (*OpenCnt)++;
                        ok = TRUE;
                     }
                     else Pkt->dp_Res2 = ERROR_NO_FREE_STORE;

                     if (!ok) CloseDevice ((struct IORequest *)psd->psd_IO);
                  }
                  else
                  {
                     d(("could not open device >%s<\n",psd->psd_Device));
                     Pkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND;
                  }
               }
               else
               {
                  d(("no device\n"));
                  Pkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND;
               }

               if (!ok) DeleteIORequest(psd->psd_IO);
            }
            else
            {
               d(("no iorequest\n"));
               Pkt->dp_Res2 = ERROR_NO_FREE_STORE;
            }

            if (!ok) DeleteMsgPort (IOPort);
         }
         else
         {
            d(("no msgport\n"));
            Pkt->dp_Res2 = ERROR_NO_FREE_STORE;
         }
      }
      else
      {
         d(("no StreamSpecs()\n"));
      }
      if (!ok) FreeVec (psd);
   }
   else
   {
       d(("no psd\n"));
       Pkt->dp_Res2 = ERROR_NO_FREE_STORE;
   }

   d(("leaving OpenDH() with Res2 = %ld\n",Pkt->dp_Res2));
}
/*E*/
/*F*/ STATIC VOID CloseDH(GD gd, struct PrivateStreamData *psd,ULONG *OpenCnt)
{
   d(("CloseDH()\n"));
   if (psd->psd_Write)
   {
      if (psd->psd_Left < psd->psd_SectorSize)
      {
         d(("DoIO(CMD_WRITE)\n"));

         memset(psd->psd_Ptr,'\0',(size_t)psd->psd_Left);
         psd->psd_IO->io_Length  = psd->psd_SectorSize;
         psd->psd_IO->io_Data    = psd->psd_Buffer;
         psd->psd_IO->io_Offset  = psd->psd_Pos + psd->psd_Left-psd->psd_SectorSize;
         psd->psd_IO->io_Command = CMD_WRITE;
         DoIO((struct IORequest *)psd->psd_IO);
      }
      psd->psd_IO->io_Command=CMD_UPDATE;

      d(("DoIO(CMD_UPDATE)\n"));
      DoIO((struct IORequest *)psd->psd_IO);
   }

   d(("FreeVec() buffer\n"));

   FreeVec(psd->psd_Buffer);

   d(("stopping motor off\n"));
   psd->psd_IO->io_Command = TD_MOTOR;
   psd->psd_IO->io_Length = 0L;
   DoIO((struct IORequest *)psd->psd_IO);
   d(("closing device\n"));
   CloseDevice ((struct IORequest *)psd->psd_IO);

   d(("deleting msg port\n"));
   DeleteMsgPort (psd->psd_IO->io_Message.mn_ReplyPort);

   d(("deleting stdio\n"));
   DeleteIORequest (psd->psd_IO);

   d(("FreeVec() rh\n"));
   FreeVec (psd);

   (*OpenCnt)--;

   d(("leaving closedh()\n"));

}
/*E*/

/*F*/ STATIC LONG REGARGS RawRead(GD gd, struct PrivateStreamData *psd, UBYTE *Buffer, LONG Bytes, LONG *res2)
{
   LONG LatchBytes, RDBytes;
   BOOL error = FALSE;

      /* check bouunds */
   if ((psd->psd_Pos + Bytes) > psd->psd_Len) Bytes = psd->psd_Len - psd->psd_Pos;

   LatchBytes = Bytes;

   d(("read %ld bytes\n",Bytes));

   /*
   ** first, read what's left in buffer
   */
   if (psd->psd_Left)
   {
      RDBytes = (LONG)MIN((int)psd->psd_Left, (int)Bytes);
      CopyMem(psd->psd_Ptr, Buffer, RDBytes);

      Buffer += RDBytes;
      Bytes -= RDBytes;

      psd->psd_Pos += RDBytes;   /* incr. position in file */
      psd->psd_Ptr += RDBytes;
      psd->psd_Left -= RDBytes;
   }

   /*
   ** second, try to read as much blocks as possible directly to
   ** user buffer, aVOIDing CopyMem()
   */
   if (!error && (Bytes > psd->psd_SectorSize))
   {
      RDBytes = (Bytes / psd->psd_SectorSize) * psd->psd_SectorSize;

      psd->psd_IO->io_Data = Buffer;
      psd->psd_IO->io_Length = RDBytes;
      psd->psd_IO->io_Offset = psd->psd_Pos + psd->psd_Offset;
      if (DoIO((struct IORequest *)psd->psd_IO))
      {
         RDBytes = psd->psd_IO->io_Actual;
         error = TRUE;
         *res2 = psd->psd_IO->io_Error;
      }
      Buffer += RDBytes;
      Bytes -= RDBytes;

      psd->psd_Pos += RDBytes;         /* incr. position in file */
      psd->psd_Left = 0;               /* for safety, initialise buffer */
      psd->psd_Ptr = psd->psd_Buffer;
   }

   if (!error && Bytes)
   {
      /*
      ** now there are only some bytes < SectorSize left, so they fit into one buffer
      ** So, read a buffer and copy some bytes from it to user-buffer.
      */
      RDBytes = Bytes;
      psd->psd_IO->io_Length = psd->psd_SectorSize;
      psd->psd_IO->io_Data = psd->psd_Buffer;
      psd->psd_IO->io_Offset = psd->psd_Pos + psd->psd_Offset;

      if (DoIO((struct IORequest *)psd->psd_IO))
      {
         *res2 = psd->psd_IO->io_Error;
         RDBytes = psd->psd_IO->io_Actual;
      }

      CopyMem(psd->psd_Buffer, Buffer, RDBytes);

      psd->psd_Pos += RDBytes;
      psd->psd_Ptr = psd->psd_Buffer + RDBytes;
      psd->psd_Left = psd->psd_SectorSize - RDBytes;

      Bytes -= RDBytes;
   }

   return LatchBytes - Bytes;
}
/*E*/
/*F*/ STATIC LONG REGARGS RawWrite(GD gd, struct PrivateStreamData *psd,UBYTE *Buffer,LONG Bytes, LONG *res2)
{
   LONG LatchBytes,WRBytes;
   BOOL error = FALSE;

      /* check bounds */
   if ((psd->psd_Pos + Bytes) > psd->psd_Len) Bytes = psd->psd_Len-psd->psd_Pos;

   LatchBytes = Bytes;

   d(("write %ld bytes\n",Bytes));

   /*
   ** first, take care for outstanding data
   */
   if (Bytes && psd->psd_Left)
   {
      WRBytes = (LONG)MIN((int)psd->psd_Left, (int)Bytes);
      CopyMem(Buffer, psd->psd_Ptr, WRBytes);

      psd->psd_Ptr += WRBytes;      /* incr. position in buffer */
      psd->psd_Left -= WRBytes;     /* decr. number of unused bytes */

      if (psd->psd_Left == 0)       /* Buffer full ? */
      {                             /* then write it out */
         psd->psd_IO->io_Data = psd->psd_Buffer;
         psd->psd_IO->io_Length = psd->psd_SectorSize;
         psd->psd_IO->io_Offset = psd->psd_Pos + psd->psd_Offset;
         psd->psd_IO->io_Command = CMD_WRITE;
         if (DoIO((struct IORequest *)psd->psd_IO))
         {
            *res2 = psd->psd_IO->io_Error;
            WRBytes = psd->psd_IO->io_Actual;
            error = TRUE;
            d(("td-error %ld\n",psd->psd_IO->io_Error));
         }

         psd->psd_Ptr = psd->psd_Buffer;  /* reset buffer pointers */
         psd->psd_Left = 0;
      }

      Buffer += WRBytes;
      Bytes -= WRBytes;
      psd->psd_Pos += WRBytes;      /* incr. position in file */
   }

   if (!error && Bytes)
   {
      /*
      ** second, try to write as much blocks as possible directly from
      ** user buffer in one go, aVOIDing CopyMem() and multiple I/O-Requests
      */
      if (Bytes > psd->psd_SectorSize)
      {
            /* I don't like to use bit masking at this point */
         WRBytes = (Bytes / psd->psd_SectorSize) * psd->psd_SectorSize;
         d(("chunk %ld/%lx bytes\n",WRBytes,WRBytes));
         psd->psd_IO->io_Data = Buffer;
         psd->psd_IO->io_Length = WRBytes;
         psd->psd_IO->io_Offset = psd->psd_Pos + psd->psd_Offset;
         psd->psd_IO->io_Command = CMD_WRITE;
         if (DoIO((struct IORequest *)psd->psd_IO))
         {
            *res2 = psd->psd_IO->io_Error;
            WRBytes = psd->psd_IO->io_Actual;
            error = TRUE;
            d(("td-error %ld\n",psd->psd_IO->io_Error));
         }

         Buffer += WRBytes;
         Bytes -= WRBytes;
         psd->psd_Pos += WRBytes;   /* incr. position in file */

         psd->psd_Ptr = psd->psd_Buffer;  /* for safety, reset buffer pointers */
         psd->psd_Left = 0;
      }
   }

   if (!error && Bytes)
   {
      /*
      ** now there are only some bytes < SectorSize left, and they fit into one
      ** buffer.
      ** NOTE: the actual writing is delayed to Close() or further Write()'s
      */
      WRBytes = Bytes;
      CopyMem(Buffer, psd->psd_Buffer, WRBytes);

      psd->psd_Pos += WRBytes;     /* incr. position in file */
      Bytes -= WRBytes;

      psd->psd_Ptr = psd->psd_Buffer + WRBytes;     /* set position in buffer */
      psd->psd_Left = psd->psd_SectorSize - WRBytes; /* set number of unused bytes */
   }

   return LatchBytes - Bytes;
}
/*E*/

/*F*/ STATIC GD creategd(VOID)
{
   GD new;
   LOCALSYSBASE;

   if (new = AllocVec(sizeof(struct GlobalData), MEMF_ANY | MEMF_CLEAR))
      new->gd_SysBase = SysBase;

   return new;
}
/*E*/
/*F*/ STATIC BOOL openres(GD gd)
{
   BOOL rc = FALSE;

   d(("entered\n"));

   DOSBase = NULL;
   UtilityBase = NULL;
   gd->gd_Port = NULL;

   if (SysBase->lib_Version >= 37)
   {
      if (DOSBase = OpenLibrary("dos.library", 37))
      {
         if (UtilityBase = OpenLibrary("utility.library", 37))
         {
            if (gd->gd_Port = CreateMsgPort())
            {
               rc = TRUE;
            }
            else d(("no handler port\n"));
         }
         else d(("no utility V37\n"));
      }
      else d(("no dos V37\n"));
   }
   else d(("no exec V37\n"));

   d(("left: %ld\n",rc));

   return rc;
}
/*E*/
/*F*/ STATIC VOID closeres(GD gd)
{
   d(("freeing port\n"));
   if (gd->gd_Port) DeleteMsgPort(gd->gd_Port);

   d(("closing dos\n"));
   if (DOSBase) CloseLibrary(DOSBase);

   d(("closing utility\n"));
   if (UtilityBase) CloseLibrary(UtilityBase);

   FreeVec(gd);
}
/*E*/

