  /***********************************************************\
  *                                                           *
  *   VFILE consists of 5 functions which simulate a "BASIC   *
  *   RANDOM FILE" structure using fixed length records of    *
  *   varying pre-specified size. A record may be up to 255   *
  *   chars long. It is the programmer's responsibility to    *
  *   keep track of the contents of his record, and VFILE's   *
  *   responsibility to write and read the record to and from *
  *   the correct position in file.  Record numbers are       *
  *   maximum 32767. VFILE will give ERROR if reading past    *
  *   end of file or exceeding legal bounds.  It will pad     *
  *   as-yet-unwritten internal records with nulls. An error  *
  *   is returned if there is not enough disk space for       *
  *   either the record or the padding.  There is a sixth     *
  *   function (vset), which is used internally.              *
  *                                                           *
  *   The structure "_vbuf" must be included in the           *
  *   programme, and each file must reserve a VBUFSIZ area    *
  *   for buffering.  This area will vary depending on NSECTS *
  *   in BDSCIO.H   THE VALUE OF NSECTS MUST BE GREATER       *
  *   THAN TWO.  The definitions of _vbuf and VBUFSIZ         *
  *   should eventually find their way into BDSCIO.H          *
  *   I have included them in this source for clarity.        *
  *                                                           *
  *   telend finds the end of any sequential file and returns *
  *   its record number.  Only sequential extents are searched*
  *   (i.e. it may not work on random files)                  *
  *                                                           *
  \************************************************************




	Version 1.0  (c) 1980 	Greg Lister

				Software Source
				P.O. Box 364
				Edgecliff   2027

				Australia


		19th November, 1980			    */




#include "bdscio.h"
#define VBUFSIZ NSECTS * SECSIZ + 14

struct _vbuf
{   int fd;	/* File Descriptor */
    int eof;	/* Actual EOF of file */
    int pos;	/* Current record's position in buffer */
    int len;	/* Record length */
    int sta;	/* Start sector of buffer */
    int cnt;	/* Number of sectors in buffer */
    int wflag;	/* flag set if buffer written to */
    char buffer[NSECTS * SECSIZ];
};


vset(recno,vbuf)
    int recno;
    struct _vbuf *vbuf;
    /************************************************\
    *                                                *
    *   Sets up buffer for random reads and writes.  *
    *   Flushes buffer if necessary and fills buffer *
    *   with required data. Sets pointer to beginning*
    *   of r/w record.  Fills unwritten buffer with  *
    *   nulls.  Returns ERROR if cannot flush or     *
    *   if hard read error.  Returns NULL if         *
    *   required buffer is not yet written. Returns  *
    *   -2 if required sector (128 byte) is not yet  *
    *   written. Else returns valid sectors in buf.  *
    *                                                *
    \************************************************/
{
   int tmp,blkno,bss,blkoff,secno,bufsec,endrec;

   blkno=recno>>7;		/* Block Number */

   bss=blkno*vbuf->len;		/* Block start Sector */

   blkoff=(recno%128)*vbuf->len;	/* Offset into Block */

   secno=bss+(blkoff>>7);	/* Sector # of start of rec */

   endrec=bss+((blkoff+vbuf->len-1)>>7);/* Sec# END of rec */

   bufsec=(secno/(NSECTS-2))*(NSECTS-2); /* Start sec of buf */

   vbuf->pos=vbuf->buffer+((secno-bufsec)<<7)+(blkoff%128);

   if(bufsec!=vbuf->sta)
   {    if(vbuf->wflag)
	{   seek(vbuf->fd,vbuf->sta,0);
           if(write(vbuf->fd,vbuf->buffer,vbuf->cnt)<vbuf->cnt)
		return ERROR;
	    if((tmp=tell(vbuf->fd))>vbuf->eof)
		vbuf->eof=tmp;
	    vbuf->wflag=0;
	}
	if((vbuf->sta=bufsec)>vbuf->eof) return NULL;
	seek(vbuf->fd,bufsec,0);
	if((vbuf->cnt=read(vbuf->fd,vbuf->buffer,NSECTS))<0)
		return ERROR;
	if(vbuf->cnt!=NSECTS)
		fillb(vbuf->buffer+(vbuf->cnt<<7),
		vbuf->buffer+(NSECTS<<7),0,0);
   }

   if(endrec-bufsec>=vbuf->cnt) return -2;
   return vbuf->cnt;
}


vwrite(recno,vbuf,str)
    int recno;
    struct _vbuf *vbuf;
    char *str;

    /********************************************************\
    *                                                        *
    *   Writes data at str to file vbuf at recno.  Pads file *
    *	with NULLS if skipping unwritten sections of file.   *
    *	Returns ERROR if full disk, unopened file etc        *
    *	Returns ERROR if bad record #;  else returns OK      *
    *                                                        *
    \********************************************************/

{   int tmp,tmp1,tmp2;

    if(recno<0) return ERROR;
    if((tmp=vset(recno,vbuf))==-1) return ERROR;
      if(tmp==0)
      { fillb(vbuf->buffer,NSECTS<<7,0,1);
	seek(vbuf->fd,vbuf->eof,0);
	while((vbuf->eof=tell(vbuf->fd))<vbuf->sta)
	if(write(vbuf->fd,vbuf->buffer,1)>1) return ERROR;
      }
      movmem(str,vbuf->pos,vbuf->len);
      tmp2=vbuf->buffer;
      tmp1=(127+vbuf->pos-tmp2+vbuf->len)>>7;
      if(tmp1>vbuf->cnt)
	vbuf->cnt=tmp1;
      vbuf->wflag=1;
    return OK;
}


vread(recno,vbuf,str)
    int recno;
    struct _vbuf *vbuf;
    char str[];

    /************************************************\
    *                                                *
    *   Reads record 'recno' from file to 'str'.     *
    *   Returns ERROR if hard error (unopened        *
    *   file etc) or if attempt is made to read      *
    *   past current end of file.  Returns no of     *
    *   secs read in.   ERROR if bad recno.          *
    *                                                *
    \************************************************/

{   int tmp;
    if(recno<0) return ERROR;
    tmp=vbuf->sta;
    if(vset(recno,vbuf)<1)
    {
	vbuf->sta=tmp;
	return ERROR;
    }
    movmem(vbuf->pos,str,vbuf->len);
    return vbuf->cnt;
}


vclose(vbuf)
   struct _vbuf *vbuf;

    /************************************************\
    *                                                *
    *   Closes file using vbuf.  Returns OK if       *
    *   closed successfully.  ERROR if error.        *
    *                                                *
    \************************************************/

{   if(vbuf->wflag)
    {	seek(vbuf->fd,vbuf->sta,0);
	if(write(vbuf->fd,vbuf->buffer,vbuf->cnt)<0)
	    return ERROR;
    }
    return close(vbuf->fd);
}



vopen(filename,vbuf,reclen)
    int reclen;
    char filename[];
    struct _vbuf *vbuf;

    /************************************************\
    *                                                *
    *   Opens filename for v-format buffered io      *
    *   Reclen is the specified record length        *
    *   (may be up to 255 chars).  Vbuf is a         *
    *   VBUFSIZ buffer ((128 * NSECTS)+14)           *
    *   **NOTE** NSECTS must be 3 or more for this   *
    *   file structure to work.                      *
    *   Returns ERROR if reclen is out of range      *
    *   or if open error occurs (ie file doesn't     *
    *   exist - use vcreat)                          *
    *                                                *
    \************************************************/

{   if((vbuf->len=reclen)>255)return ERROR;
    if(reclen<0) return ERROR;
    if((vbuf->fd=open(filename,2))<0) return ERROR;
    vbuf->sta=vbuf->cnt=-1;
    vbuf->wflag=0;
    vbuf->pos=vbuf->buffer;
    vbuf->eof=telend(filename);
    return vbuf->fd;
}


vcreat(filename,vbuf,reclen)
    int reclen;
    char filename[];
    struct _vbuf *vbuf;
    /************************************************\
    *                                                *
    *   Creates a file as vopen (see above)          *
    *                                                *
    \************************************************/
{   fabort(creat(filename));
    return vopen(filename,vbuf,reclen);
}
















telend(filename)
char *filename;
    /************************************************\
    *                                                *
    *   returns number of last 128-byte record in    *
    *   file 'filename'.  Returns NULL if file       *
    *   is either nonexistent or empty.              *
    *                                                *
    \************************************************/
{
    int recnt;
    char fcb[38],ex;
    setfcb(fcb,filename);
    ex=recnt=0;
    while(1)
    {	fcb[12]=ex++;
	if((bdos(15,fcb))==255) break;
	recnt+=fcb[15];
    }
    return recnt;
}


