/*
 *  xfi = xfropen(name,num)
 *   n  = xfrread(xfi)
 *  err = xfrclose(xfi)
 *
 *  RESTRICTIONS:   NO seeking.
 *
 *  Three buffers are used. When the 2nd buffer is 1st accessed,
 *  an asynchronous read is started on the 3rd. The buffers cycle.
 *  If the file has an .lzh extension, it is decompressed 1 buffer
 *  at a time.
 */

#include <exec/types.h>
#include <libraries/dosextens.h>
#include <exec/memory.h>
#include "scan.h"

extern void *AllocMem();
extern FH *Open();
extern void *malloc(), *FindTask();
extern long FillBufWithLZH();
extern long WinSiz;  /* Size of each buffer */
extern char EOBK;    /* Ptr 2 End of buf keywrd 2b put after last wrd in buf */
extern char *BufIdx; /* Ptr 2 buffer user should start using after open/read */
                     /* Note that this is modified at each buffer switch.    */
extern char *EOCB;   /* Ptr 2 End+1 of buf that user is currently reading.   */
                     /* Note that this is modified at each buffer switch.    */
extern char *SOCB;   /* Ptr 2 Strt of buf user is currently reading from     */
                     /* Note that this is modified at each buffer switch.    */
extern XFI *FHandle;
extern int KeyWrdOvlp;
extern int OpenNew;  /* Set to 1 if finished with internal LZH archive file. */
extern int LON;
extern int EnableLZHDecomp;
int FrstAsyncRd;
char *filenm;
char *TmpPtr;
int flen;
int ItsALZH;
int FrstRd;

void *
xfropen(file,num)
char *file;
long *num;

{
    extern long xfrread();
    XFI *xfi;
    FHandle = AllocMem((ULONG)sizeof(XFI), MEMF_CLEAR | MEMF_PUBLIC);
    xfi = FHandle; /* needed for abort/cleanup routine */
    flen = strlen(file) - 1;
    filenm = file;
    ItsALZH = 0;
    if( EnableLZHDecomp ) {
       if( tolower( file[ flen--] ) == 'h' && tolower( file[ flen--] == 'z') &&
           tolower( file[ flen--] ) == 'l' && file[ flen] == '.') ItsALZH = 1;
       flen = strlen(file) - 1;
       if( tolower( file[ flen--] ) == 'a' && tolower( file[ flen--] == 'h') &&
           tolower( file[ flen--] ) == 'l' && file[ flen] == '.') ItsALZH = 1;
    }
    if( ItsALZH ) {
       /* Allocate all 3 buffers with spare for buffer switch */
       xfi->asbuf = (char *)malloc(WinSiz + OVERLAP + 4) + OVERLAP;
       xfi->usbuf = (char *)malloc(WinSiz + OVERLAP + 4) + OVERLAP;
       xfi->zsbuf = (char *)malloc(WinSiz + OVERLAP + 4) + OVERLAP;
       /* Put end of buffer keyword before strt of 1st buffer to prevent */
       /* backing up before data is valid.                               */
       strncpy(xfi->asbuf - 4, &EOBK, 3);
       /* Put newline char right before 1st char of 1st buffer to allow  */
       /* matching article separtor with newline as it's 1st char.       */
       strncpy(xfi->asbuf - 1, "\n", 1);
       /* 0 <= x != buffer size -> end of file                               */
       /* 0 <= x <= buffer size -> x = # of bytes read into buf              */
       /*      x >  buffer size -> # of bytes read into buffer = buf size    */
       /* 0 >  x                -> end of archive                            */
       /* if no file matches the pattern a 0 is returned.                    */
       /* if there are no more files in the archive to test, a -1 is returnd */
       *num = FillBufWithLZH( file, xfi->asbuf);
       OpenNew = 0;
       if( *num >= 0 && *num != WinSiz ) {
          if( *num > WinSiz ) *num = WinSiz;
          OpenNew = 1; /* dont open new internal file in LHA/LZH archive */
       }
       FrstRd = 0;
       LON = 1; /* set so we bypass closing archive */
       if( *num < 0 ) {
          *num = 0;
          LON = 0;
          OpenNew = 1;
       }
       strncpy( xfi->asbuf + *num, &EOBK , 3);
       BufIdx = xfi->asbuf;
       TmpPtr = xfi->asbuf;
       SOCB = TmpPtr;               /* Pointer to start of current buffer */
       EOCB = TmpPtr + *num;        /* Pointer to last byte in buffer + 1 */
       xfi->asbuf = xfi->usbuf;     /* swap buffs */
       xfi->usbuf = xfi->zsbuf;
       xfi->zsbuf = TmpPtr;
       Chk_Abort();
    }
    else {
       OpenNew = 0;
       LON = 0;
       xfi->fh = Open(file, 1005);
       if (xfi->fh) {
          xfi->fh = (FH *)((long)xfi->fh << 2);
          /* Allocate all 3 buffers with spare for buffer switch */
          xfi->asbuf = (char *)malloc(WinSiz + OVERLAP + 4) + OVERLAP;
          xfi->usbuf = (char *)malloc(WinSiz + OVERLAP + 4) + OVERLAP;
          xfi->zsbuf = (char *)malloc(WinSiz + OVERLAP + 4) + OVERLAP;
          /* Copy end of buffer indicator to end of each of the 3 buffers   */
          strncpy(xfi->asbuf + WinSiz, &EOBK, 3);
          strncpy(xfi->usbuf + WinSiz, &EOBK, 3);
          strncpy(xfi->zsbuf + WinSiz, &EOBK, 3);
          /* Put end of buffer keyword before strt of 1st buffer to prevent */
          /* backing up before data is valid.                               */
          strncpy(xfi->asbuf - 4, &EOBK, 3);
          /* Put newline char right before 1st char of 1st buffer to allow  */
          /* matching article separtor with newline as it's 1st char.       */
          strncpy(xfi->asbuf - 1, "\n", 1);
          FrstAsyncRd = 1;
          /* Initialize reply port */
          xfi->rp.mp_Node.ln_Type = NT_MSGPORT;
          xfi->rp.mp_Node.ln_Name = "Async";
          xfi->rp.mp_Flags = PA_SIGNAL;
          xfi->rp.mp_SigBit = AllocSignal(-1);
          if( xfi->rp.mp_SigBit == -1 ) ErrP("Could not allocate signal\n");
          xfi->rp.mp_SigTask = FindTask(NULL);
          NewList(&(xfi->rp.mp_MsgList));        /* Create a new list header */
          /* Fill up 1st buffer and start the read for the 2nd */
          SOCB = NULL;
          EOCB = NULL;
          Chk_Abort();
          xfstartasync(xfi); /* Read into asbuf */
          Chk_Abort();
          *num = xfrread(xfi); /* Wait 4 prev read & then strt rd into usbuf */
          Chk_Abort();
       }
       else {
          FreeMem(FHandle,(ULONG)sizeof(XFI));
          FHandle = 0;
          xfi = NULL;
       }
    }
    return(xfi);
}

xfrclose(xfi)
XFI *xfi;
{
    extern long *afp;
    int err = 1;
    if (xfi) {
       if( ItsALZH ) {
          err = 0;
          if( afp != NULL ) { fclose( afp ); afp = NULL; }
          if( xfi->asbuf != NULL ) free( xfi->asbuf - OVERLAP);
          xfi->asbuf = NULL;
          if( xfi->usbuf != NULL ) free( xfi->usbuf - OVERLAP);
          xfi->usbuf = NULL;
          if( xfi->zsbuf != NULL ) free( xfi->zsbuf - OVERLAP);
          xfi->zsbuf = NULL;
          FreeMem(xfi,(ULONG)sizeof(XFI));
          FHandle = 0;
       }
       else {
          if (xfi->pend) {
             /* Wait for pending read to complete */
             xfi->pend = 0;
             WaitPort (&xfi->rp);
             GetMsg   (&xfi->rp);
          }
          err = xfi->err;
          if( xfi->fh != NULL ) Close((long)xfi->fh >> 2);
          if( xfi->asbuf != NULL ) free( xfi->asbuf - OVERLAP);
          xfi->asbuf = NULL;
          if( xfi->usbuf != NULL ) free( xfi->usbuf - OVERLAP);
          xfi->usbuf = NULL;
          if( xfi->zsbuf != NULL ) free( xfi->zsbuf - OVERLAP);
          xfi->zsbuf = NULL;
          if( xfi->rp.mp_SigBit > 0 ) FreeSignal(xfi->rp.mp_SigBit);
          xfi->rp.mp_SigBit = 0;
          FreeMem(xfi,(ULONG)sizeof(XFI));
          FHandle = 0;
       }
    }
    return(err);
}

/* Called to make sure a previous read is complete before swapping buffers */
/* and then starting another async read.                                   */

long xfrread(xfi)
XFI *xfi;
{
   long NumRead;
   if( ItsALZH ) {
      /* 0 <= x != buffer size -> end of file                               */
      /* 0 <= x <= buffer size -> x = # of bytes read into buf              */
      /*      x >  buffer size -> # of bytes read into buffer = buf size    */
      /* 0 >  x                -> end of archive                            */
      /* If an internal file name does not match the pattern, 0 is returned.*/
      /* If there are no more internal files, -1 is returned.               */
      if( !OpenNew ) {
         if( FrstRd ) {
            FrstRd = 0;
            /* Put end of buffer keyword before strt of 1st buffer to prevent*/
            /* backing up before data is valid.                              */
            strncpy(xfi->asbuf - 4, &EOBK, 3);
            /* Put newline char right before 1st char of 1st buffer to allow */
            /* matching article separtor with newline as it's 1st char.      */
            strncpy(xfi->asbuf - 1, "\n", 1);
            BufIdx = xfi->asbuf;           /* Init pntr 2 new user workspace */
         }
         else {
            /* if last read did not get an EOF, provide buffer overlap       */
            memcpy(xfi->asbuf - OVERLAP,xfi->zsbuf + WinSiz - OVERLAP,OVERLAP);
            BufIdx = xfi->asbuf - KeyWrdOvlp; /* Init pntr 2 new user wrkspc */
         }
         NumRead = FillBufWithLZH( filenm, xfi->asbuf);
         OpenNew = 0;
         if( NumRead >= 0 && NumRead != WinSiz ) { /* if EOF */
            if( NumRead > WinSiz ) NumRead = WinSiz;
            OpenNew = 1; /* finished with internal LZH/LHA archive file */
         }
         strncpy(xfi->asbuf + NumRead, &EOBK , 3);
         if( NumRead < 0 ) LON = 0;
      }
      else { /* Comes here on xfrread after the last xfrread with >=0 retrnd.*/
         /* Return -1 so EOF processing will be done.                        */
         FrstRd = 1;
         NumRead = -1;
         OpenNew = 0;
         BufIdx = xfi->asbuf;           /* Init pntr 2 new user workspace */
         strncpy(xfi->asbuf, &EOBK , 3);
      }
      TmpPtr = xfi->asbuf;
      SOCB = TmpPtr;               /* Pointer to start of current buffer */
      EOCB = TmpPtr + NumRead;     /* Pointer to last byte in buffer + 1 */
      xfi->asbuf = xfi->usbuf;     /* swap buffs */
      xfi->usbuf = xfi->zsbuf;
      xfi->zsbuf = TmpPtr;
      Chk_Abort();
   }
   else {
      if (xfi->pend != 0) {        /* if pending read has not finished */
          WaitPort (&xfi->rp);     /* Wait for it */
          GetMsg   (&xfi->rp);
          xfi->pend = 0;
      }
      /* Copy last OVERLAP bytes of currently used buf to start of next buf */
      if (!FrstAsyncRd)
         memcpy(xfi->asbuf - OVERLAP, xfi->zsbuf + WinSiz - OVERLAP, OVERLAP);
      if ((NumRead = xfi->sp.sp_Pkt.dp_Res1) <= 0) {              /* EOF    */
         return(0);
      }
      /* Last read may not completely fill the buffer */
      if ( NumRead < WinSiz ) {
         /* copy end of buffer keyword after last byte in buffer */
         /* No need to worry about overwriting EOB keyword after buffer */
         /* because it is not needed anymore. The next file opened will */
         /* call xfropen which will reinitialize the EOB's.             */
         strncpy(xfi->asbuf + NumRead, &EOBK , 3);
      }
      /* On 1st read, start at 1st data read, on next reads, start a         */
      /* little before data just read in to handle keywords spanning buffers */
      if (FrstAsyncRd) {
         BufIdx = xfi->asbuf;
         FrstAsyncRd = 0;
      }
      else {
         BufIdx = xfi->asbuf - KeyWrdOvlp; /* Init pntr 2 new user workspace */
      }
      TmpPtr = xfi->asbuf;
      SOCB = TmpPtr;               /* Pointer to start of current buffer */
      EOCB = TmpPtr + NumRead;     /* Pointer to last byte in buffer + 1 */
      xfi->asbuf = xfi->usbuf;     /* swap buffs */
      xfi->usbuf = xfi->zsbuf;
      xfi->zsbuf = TmpPtr;
      Chk_Abort();
      xfstartasync(xfi);           /* new async read */
   }
   return(NumRead);
}

static
xfstartasync(xfi)
XFI *xfi;
{
    xfi->sp.sp_Msg.mn_Node.ln_Name = (char *)&(xfi->sp.sp_Pkt);
    xfi->sp.sp_Pkt.dp_Link = &(xfi->sp.sp_Msg);
    xfi->sp.sp_Pkt.dp_Port = &xfi->rp;
    xfi->sp.sp_Pkt.dp_Type = ACTION_READ;
    xfi->sp.sp_Pkt.dp_Arg1 = xfi->fh->fh_Arg1;
    xfi->sp.sp_Pkt.dp_Arg2 = (long)xfi->asbuf;
    xfi->sp.sp_Pkt.dp_Arg3 = WinSiz;
    PutMsg (xfi->fh->fh_Type, &xfi->sp);
    xfi->pend = 1;
}

