/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* |_o_o|\\ Copyright (c) 1987 The Software Distillery.  All Rights Reserved */
/* |. o.| || This program may not be distributed without the permission of   */
/* | .  | || the authors:                                          BBS:      */
/* | o  | ||   John Toebes     Dave Baker                                    */
/* |  . |//                                                                  */
/* ======                                                                    */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* File Access:            */
/* ActRead ActWrite ActSeek ActWaitForChar    */
/* ActFindwrite ActFindin ActFindout ActEnd   */

#include "handler.h"

/*---------------------------------------------------------------------------*/
/*                                                                           */
/*                 ActRead( global, pkt )                                    */
/*                                                                           */
/*---------------------------------------------------------------------------*/

void ActRead(global,pkt)
GLOBAL global;
struct DosPacket *pkt;              /* a pointer to the dos packet sent       */
/* Arg1: APTR EFileHandle */
/* Arg2: APTR Buffer      */
/* Arg3: Length           */
{
   EFH efh;
   KEY blkkey;
   long blknum;
   int blkpos;
   char *buffer;
   long len, size, toread;
   char *data;

   efh  =    (EFH)pkt->dp_Arg1;
   data = (char *)pkt->dp_Arg2;
   len  =         pkt->dp_Arg3;

   blkpos = efh->efh_CurPos;
   blknum = efh->efh_CurBlock;
   blkkey = efh->efh_CurKey;

   BUG(("ActRead: efh=%08lx data=%08lx len=%ld\n", efh, data, len));
   BUG(("ActRead: key=%ld pos=%ld #=%ld\n",blkkey,blkpos,blknum));
   /* Make sure we are in a state where we can write to the disk */
   if (unsafe(global, efh, 0))
      return;

   /* Make sure we are allowed to read from the file.  Actually we may     */
   /* wish to disable this in some handlers because many AmigaDos programs */
   /* Do not respect the READ/WRITE bits                                   */
   if (efh->efh_Protect & FIBF_READ)
      {
      /* Let them know how much they read */
      pkt->dp_Res2 = ERROR_READ_PROTECTED;
      return;
      }

   /* Now if there isn't enough bytes in the file, we need to adjust */
   /* down the number of bytes to read to reflect the current length */
   if ((size = GetFileSize(global, efh)) == -1)
      /* Some problem getting the file size so skip it */
      return;

   size -= ((blknum-1)*BLOCKSIZE) + blkpos;

   if (len > size) len = size;
   size = len;

   BUG(("ActRead: Allowing them to read %ld\n", size));

   /* Now fill up as many buffers as we can */
   while(len)
      {
      /* If we don't have a block to write into, get us a new one */
      if ((blkkey == 0) &&
          ((blkkey = BlockKey(global, efh, blknum, 0)) == 0))
         /* Couldn't get the next block for some reason.  Quit trying */
         return;

      /* Find the block so we can write to it */
      if ((buffer = GetBlock(global, blkkey)) == NULL)
         return;

      /* Figure out how much we can put into the buffer */
      toread = len;
      if (toread > (BLOCKSIZE - blkpos))
         toread = BLOCKSIZE - blkpos;

      BUG(("ActRead: toread=%ld len=%ld\n", toread, len));

      /* Now fill the block with the appropriate data */
      GETDATA(data, toread, buffer, blkpos);

      /* Now update our position in the block */
      if (toread != len)
         {
         blkpos = 0;
         blknum++;
         blkkey = 0;  /* Force us to get a new block next time */
         }
      else
         blkpos += toread;

      /* Update our idea of where we are in the file */
      data += toread;
      len -= toread;
      }
  
   BUG(("ActRead: size=%ld pos=%ld #=%ld key=%ld\n", size, blkpos, blknum,blkkey));

   /* Successful read.  Now update the EFH to indicate where we are */
   efh->efh_CurPos   = blkpos;
   efh->efh_CurBlock = blknum;
   efh->efh_CurKey   = blkkey;

   /* Let them know how much they read */
   pkt->dp_Res1 = size;
}

/*---------------------------------------------------------------------------*/
/*                                                                           */
/*                      ActWrite( global, pkt )                              */
/*                                                                           */
/*---------------------------------------------------------------------------*/

void ActWrite(global,pkt)
GLOBAL global;
struct DosPacket *pkt;              /* a pointer to the dos packet sent       */
/* Arg1: APTR EFileHandle */
/* Arg2: APTR Buffer */
/* Arg3: Length */
{
   EFH efh;
   KEY blkkey;
   long blknum;
   int blkpos;
   char *buffer;
   int len;
   char *data;
   int towrite;

   efh  =    (EFH) pkt->dp_Arg1;
   data = (char *) pkt->dp_Arg2;
   len  =          pkt->dp_Arg3;

   blkpos = efh->efh_CurPos;
   blknum = efh->efh_CurBlock;
   blkkey = efh->efh_CurKey;

   BUG(("ActWrite:efh=%08lx key=%ld pos=%ld #=%ld\n",efh,blkkey,blkpos,blknum));

   /* Make sure we are in a state where we can write to the disk */
   if (unsafe(global, efh, 1))
      return;

   /* Make sure we are allowed to write to the file.  Actually we may      */
   /* wish to disable this in some handlers because many AmigaDos programs */
   /* Do not respect the READ/WRITE bits                                   */
   if (efh->efh_Protect & FIBF_WRITE)
      {
      /* Let them know how much they read */
      pkt->dp_Res2 = ERROR_WRITE_PROTECTED;
      return;
      }

   /* Now fill up as many buffers as we can */
   while(len)
      {
      /* If we don't have a block to write into, get us a new one */
      if ((blkkey == 0) &&
          ((blkkey = BlockKey(global, efh, blknum, 1)) == 0))
         /* Couldn't get the next block for some reason.  Quit trying */
         return;

      /* Find the block so we can write to it */
      if ((buffer = ModBlock(global, blkkey)) == NULL)
         return;

      /* Figure out how much we can put into the buffer */
      towrite = len;
      if (towrite > (BLOCKSIZE - blkpos))
         towrite = BLOCKSIZE - blkpos;

      /* Now fill the block with the appropriate data */
      PUTDATA(buffer, blkpos, data, towrite);

      /* Now update our position in the block */
      if (towrite != len)
         {
         blkpos = 0;
         blkkey = 0;  /* Force us to get a new block next time */
         blknum++;
         }
      else
         blkpos += towrite;

      /* Update our idea of where we are in the file */
      len -= towrite;
      data += towrite;
      }

   /* Finally update the file header (if we can) to indicate our new length */
   if (!UpdateFile(global, efh, blknum, blkpos))
      /* Something went wrong, they will have to try again */
      return;

   BUG(("Done with the Write at %ld for %ld/%ld\n", blkkey, blknum, blkpos));

   /* Successful write.  Now update the EFH to indicate what we did */
   efh->efh_CurPos   = blkpos;
   efh->efh_CurBlock = blknum;
   efh->efh_CurKey   = blkkey;

   /* Let them know how much we wrote */
   pkt->dp_Res1 = pkt->dp_Arg3;
}

/*---------------------------------------------------------------------------*/
/*                                                                           */
/*                       ActSeek( global, pkt )                              */
/*                                                                           */
/*---------------------------------------------------------------------------*/

void ActSeek(global,pkt)
GLOBAL global;
struct DosPacket *pkt;              /* a pointer to the dos packet sent      */
/* Arg1: APTR EFileHandle */
/* Arg2: Position */
/* Arg3: Mode */
{
   register EFH efh;
   register long      bytepos;   /* New position relative to begin of file */
   KEY blkkey;
   long blknum;
   int blkpos;
   long size;

   efh = (EFH )pkt->dp_Arg1;
   blkpos = efh->efh_CurPos;
   blknum = efh->efh_CurBlock;
   blkkey = efh->efh_CurKey;

   BUG(("ActSeek:efh=%08lx key=%ld pos=%ld #=%ld\n",efh,blkkey,blkpos,blknum));

   /* Make sure we are in a state where we can write to the disk */
   if (unsafe(global, efh, 0))
      return;

   /* Now if there isn't enough bytes in the file, we need to adjust */
   /* down the number of bytes to read to reflect the current length */
   if ((size = GetFileSize(global, efh)) == -1)
      /* Some problem getting the file size so skip it */
      return;

   /* Figure out the real byte offset we're seeking to. */
   bytepos = (long)pkt->dp_Arg2;
   /* Calculate the current position to be returned                         */
   pkt->dp_Res1 = (efh->efh_CurBlock * BLOCKSIZE) + efh->efh_CurPos;

   switch( (int) pkt->dp_Arg3 ) {
      case OFFSET_BEGINNING:         /* Already a byte position */
         break;

      case OFFSET_CURRENT:           /* Add in the current position */
         bytepos += pkt->dp_Res1;    /* Remember we just calculated it above */
         break;

      case OFFSET_END:               /* Offset from end of file == filesize */
         bytepos = size - bytepos;
         break;

      default:
         BUG(("ActSeek: Invalid seek mode %ld\n", pkt->dp_Arg3));
         /* Note: put an error in the pkt here */
         return;
      }

   /* If we're trying to seek past end of file, silently adjust the offset */
   /* so that we do seek to end of file.                                   */
   if (bytepos > size) bytepos = size;

   /* Now calculate the offset information */
   /* Note that we don't really have to locate the block because it will be */
   /* Automatically located by the read and write routines.  This is a      */
   /* improvement when the seek is not followed by any I/O but not any      */
   /* penalty in the other case.                                            */
   blknum = (bytepos / BLOCKSIZE)+1;

   /* If we are still on the same block we can skip the formality of        */
   /* Refiguring out where the block was.                                   */
   if (blknum != efh->efh_CurBlock)
      efh->efh_CurKey = 0;

   efh->efh_CurBlock = blknum;
   efh->efh_CurPos   = bytepos % BLOCKSIZE;

   BUG(("ActSeek: Successful to %ld/%ld\n", blknum, efh->efh_CurPos));
}

/*---------------------------------------------------------------------------*/
/*                                                                           */
/*                    ActFindwrite( global, pkt )                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

void ActFindwrite(global,pkt)
GLOBAL global;
struct DosPacket *pkt;              /* a pointer to the dos packet sent      */
/* ARG1: FileHandle to fill in */
/* ARG2: Lock for file relative to */
/* Arg3: Name of file */
{
   BUG(("ActFindwrite\n"));
   /* Code 1004 - 
      If file does not exist, open should fail.
      If file does exist, open with an exclusive lock */
 
   pkt->dp_Res1 = initbuf(global, EXCLUSIVE_LOCK, OLDFILE);
}

/*---------------------------------------------------------------------------*/
/*                                                                           */
/*                    ActFindin( global, pkt )                               */
/*                                                                           */
/*---------------------------------------------------------------------------*/

void ActFindin(global,pkt)
GLOBAL global;
struct DosPacket *pkt;              /* a pointer to the dos packet sent      */
/* ARG1: FileHandle to fill in */
/* ARG2: Lock for file relative to */
/* Arg3: Name of file */
{
   BUG(("ActFindin\n"));
   /* Code 1005 -
      Open existing file to READ/WRITE at beginning
      If file doesn't exist, fail the open */

   pkt->dp_Res1 = initbuf(global, SHARED_LOCK, OLDFILE);
}

/*---------------------------------------------------------------------------*/
/*                                                                           */
/*                    ActFindout( global, pkt )                              */
/*                                                                           */
/*---------------------------------------------------------------------------*/

void ActFindout(global,pkt)
GLOBAL global;
struct DosPacket *pkt;              /* a pointer to the dos packet sent      */
/* ARG1: FileHandle to fill in */
/* ARG2: Lock for file relative to */
/* Arg3: Name of file */
{
   BUG(("ActFindout\n"));
   /* code 1006 -
      Open a new file for read/write.
      If file exists, truncate contents.
      If file does not exist, create a new one
      If case of new name is different then change it in place */

   pkt->dp_Res1 = initbuf(global, SHARED_LOCK, NEWFILE);
}

/*---------------------------------------------------------------------------*/
/*                                                                           */
/*                       ActEnd( global, pkt )                               */
/*                                                                           */
/*---------------------------------------------------------------------------*/

void ActEnd( global, pkt )
GLOBAL global;
struct DosPacket *pkt;              /* a pointer to the dos packet sent      */
{
   EFH efh;
   BUG(("ActEnd\n"));

   efh = (EFH )pkt->dp_Arg1;

   if (unsafe(global, efh, 0))
      return;

   /* empty out anything left in the buffer */
   termbuf(global, efh);

   pkt->dp_Res1 = DOS_TRUE;
}

/*---------------------------------------------------------------------------*/
/*                                                                           */
/*            unsafe( global, efh, flag )                                    */
/*                                                                           */
/*---------------------------------------------------------------------------*/
int unsafe(global, efh, forwrite)
GLOBAL global;
register EFH efh;
int forwrite;
{
   register EFH lookefh;

   for (lookefh = global->AllHandles;
        lookefh && (lookefh != efh);
        lookefh = lookefh->efh_Next);

   /* Did we find it as a valid file handle ? */
   if (!lookefh)
      {
      /* Not valid, need to give them an error message and quit */
      BUG(("unsafe: Not find %08lx\n", efh));
      global->pkt->dp_Res1 = DOS_FALSE;
      global->pkt->dp_Res2 = ERROR_INVALID_LOCK;
      return(1);
      }

   /* Ok, it is a good EFH, make sure that we have the right disk in the */
   /* drive to work with it                                              */
   if ((global->volume == NULL) ||
       (efh->efh_Lock &&
       (efh->efh_Lock->fl_Volume != MKBADDR(global->volume))))
      {
      /* Sorry, not in the drive */
      BUG(("unsafe: want %08lx vol is %08lx\n", efh->efh_Lock->fl_Volume,MKBADDR(global->volume)));
      global->pkt->dp_Res1 = DOS_FALSE;
      global->pkt->dp_Res2 = ERROR_DEVICE_NOT_MOUNTED;
      return(1);
      }

   /* Just to make sure, is this really a good disk to be working with ?     */
   if (global->diskstatus != ID_DOS_DISK)
      {
      BUG(("unsafe: Not a dos disk %08lx\n", global->diskstatus));
      global->pkt->dp_Res1 = DOS_FALSE;
      global->pkt->dp_Res2 = ERROR_NOT_A_DOS_DISK;
      return(1);
      }

   /* Perhaps we might be in a bad state (like validating?)                  */
   /* Just to make sure, is this really a good disk to be working with ?     */
   if (global->diskstate != ID_VALIDATED &&
       global->diskstate != ID_WRITE_PROTECTED)
      {
      BUG(("unsafe: bad state %ld\n", global->diskstate));
      global->pkt->dp_Res1 = DOS_FALSE;
      global->pkt->dp_Res2 = ERROR_DISK_NOT_VALIDATED;
      return(1);
      }

   /* How about write protected disks when we want to write to them?         */
   if (forwrite && (global->diskstate == ID_WRITE_PROTECTED))
      {
      BUG(("unsafe: disk is write protected\n"));
      global->pkt->dp_Res1 = DOS_FALSE;
      global->pkt->dp_Res2 = ERROR_DISK_WRITE_PROTECTED;
      return(1);
      }

   /* Well, it is right and we have the correct volume mounted so it must be */
   /* A good thing to play with.                                             */
   return(0);
}
