#
/*
*	SCCS File Type - 
*
* 	What identification string
*/
static char *_S_nio =	"@(#)NKbio.c	1.17";
#include "emuniv.h"
#include "EDI.h"

/*
 * LIBRARIES
 *              /kr/sunix/emulator/include/KERcalls.h
 *
 * KERNEL CALLS
 *              K_open
 *              K_close
 *              K_create
 *              K_read_block
 *              K_write_block
 *              K_get_file_status
 *              K_device_function
 */

/*****************************************************************************

COPYRIGHT:  1980, by Ford Aerospace and Communications Corp.
ADDRESS:    Ford Aerospace and Communications Corp.
			Western Development Laboratories
			3939 Fabian Way
			Palo Alto, California 94303
			Attention: Software Technology Dept.
			Mail Stop: V02 

CPCI:  KSOS NKSR

CP:   

TAG:	BIO

MODULE NAME:
			NKSR BUFFERED IO

REFERENCES:
  A SPECS:  None
  B5 SPECS: None

HISTORY:
  AUTHOR:  Nanette E. Harter
  VERSION:  1.17 of 7/23/82
  MODULE TYPE: 

******************************************************************************

PURPOSE
	{
		Provide buffered IO for NKSR routines
	}

EXPORT
	{
		IO_error_num, num_read, num_written, opendescriptor
	}

IMPORT
	{
		N_open:   pathname, modecode
		N_create: pathname, modecode, da
		N_seid_open:  oSeid, openmode, VAR opendescriptor
		N_seid_create: seid, openmode, da, VAR fSeid, VAR od
		N_flush: opendescriptor
		N_close: opendescriptor
		N_read:  opendescriptor, buffer, nchar
		N_write: opendescriptor, buffer, nchar
		N_seek:  opendescriptor, offset, origin
		N_tell_seid: opendescriptor, VAR fSeid
	}

*****************************************************************************/


/*   
*    make 0's in K_calls and all others defines 
*
*/

#define OFF 0
#define ON  1
/*#define NULL 0

#define READ 0
#define WRITE 1*/
#define RW  2
#define RXW 3
#define XU  4

#define BLKSIZE  512
#define LBLKSIZE 512L
#define ZERO 0
#define LZERO 0L
#define LONE  1L
#define MAX_FILES 4
#define LOWBUFF -1
#define HIGHBUFF 511

#define IO_FAIL -1
#define IO_OK 0
#define IO_EOF 0
#define END_OF_FILE 10108

p_block null_Pblk;
openDescriptor null_od = null_open_descriptor;
char first_time = TRUE;

typedef struct 
{
	seid          bseid;    /* file seid */
	open_mode     om;       /* (read,write,xread,xwrite,etc.) */
	openDescriptor opend;   /* opendescriptor */
	fBlockNumber  currBlk;  /* current blk in buff */
	int     buffindex;      /* last location accessed */
	char    buff[BLKSIZE];  /* block of file data */
	boolean written;        /* flag that buff has been written on */
	boolean outofrange;     /* flags that a RW file is pointing
				   past the end of the present file */
} buffstruct;
buffstruct buffinfo[MAX_FILES];

char modecov(mode)
int mode;
{
    switch (mode)
    {
	case READ:  return(om_read);
	case WRITE: return(om_write);
	case RW:    return(om_read | om_write);
	case RXW:   return(om_read | om_exclusive_write);
	case XU:    return(om_exclusive_read | om_exclusive_write);
	default:    return(FALSE);
    }
}


N_open(pathname,modecode)
char	*pathname;
int	modecode;
{
    extern buffstruct buffinfo[];
    extern char first_time;
    K_err_num result = 0;       /* kernel error number */
    ioStatus IO_errs;		/* error structure */
    int    filenum = 0;		/* buff index */
    int	   ix;			/* index through buff.od */
    open_mode    openmode;      /* openmode for K_open call */
    p_block      pblk;		/* for io Kernel calls */
    cardinal     numread;	/* number of bytes read */
    seid         parent;        /* parent directory seid */
    seid         fSeid;         /* entry seid */
    char         entry_name[SIZE_NAME]; /* entry name */
    openDescriptor od;

    IO_errs.devIndep = XOK;

    if ( first_time )   /* nulls open descriptors */
    {
	first_time = FALSE;
	for ( ix = 0; ix < MAX_FILES; ix++) 
	   buffinfo[ix].opend = null_od;

	null_Pblk.location.domain  = null_domain;
	null_Pblk.location.space   = null_space;
	null_Pblk.location.address = 0;
	null_Pblk.b_size           = 0;
    }

    /* check if there is a buffer available */
    for ( filenum = 0;filenum < MAX_FILES && buffinfo[filenum].opend != null_od; filenum++ );
    if ( filenum == MAX_FILES )
	return ( IO_FAIL );

    /* convert modecode to the kernel openmode */
    if ((openmode = modecov(modecode)) == FALSE)
	return ( IO_FAIL );

    if (UDM_find (ROOT, pathname, po_LOCATE, &parent, &fSeid, &entry_name)
	!= UDM_no_error)
	return ( IO_FAIL );

    if (K_open (fSeid, openmode, null_od, &od) != IO_OK)
	return ( IO_FAIL );

    buffinfo[filenum].bseid = fSeid;
    buffinfo[filenum].opend = od;
    buffinfo[filenum].om = openmode;
    buffinfo[filenum].currBlk = LZERO;
    buffinfo[filenum].buffindex = LOWBUFF;
    buffinfo[filenum].outofrange = OFF;
    buffinfo[filenum].written = OFF;

    pblk.location.domain = null_domain;
    pblk.location.space = d_space;
    pblk.location.address = buffinfo[filenum].buff;
    pblk.b_size = BLKSIZE;

    if ( buffinfo[filenum].om != om_write )
    {

	result = K_read_block(od,buffinfo[filenum].currBlk,&pblk,0,&IO_errs,&numread);

	if (IO_errs.devIndep == END_OF_FILE)
	{
		buffinfo[filenum].outofrange = ON;
		IO_errs.devIndep = XOK;
	}

	if (result || IO_errs.devIndep)
	{
		buffinfo[filenum].opend = null_od;
		return ( IO_FAIL );
	}

    }
    else    /* opened strictly for write - null fill the buffer */
	    for (ix=0; ix <= HIGHBUFF;
		buffinfo[filenum].buff[ix++] = NULL);


    return(buffinfo[filenum].opend);
}

N_create(pathname,modecode,da)
char	*pathname;
int	modecode;
discr_access	da;
{
    extern buffstruct buffinfo[];
    extern char first_time;
    K_err_num result = 0;       /* kernel error num */
    int    filenum = 0;		/* buff index */
    int	   ix;			/* index through buff.od */
    open_mode    openmode;      /* openmode for K_open call */
    p_block      pblk;		/* for io Kernel calls */
    seid         proSeid;       /* seid of parent directory */
    seid         entry_seid;    /* seid of file entry */
    char         entry_name[SIZE_NAME]; /* name of entry */
    int          numread;
    seid         fSeid;         /* seid of the file */
    openDescriptor od;
    int          x;             /* index through null buffer */

    if ( first_time )   /* nulls open descriptors */
    {
	first_time = FALSE;
	for ( ix = 0; ix < MAX_FILES; ix++)
	    buffinfo[ix].opend = null_od;

	null_Pblk.location.domain  = null_domain;
	null_Pblk.location.space   = null_space;
	null_Pblk.location.address = 0;
	null_Pblk.b_size           = 0;
    }

    /* check if there is a buffer available */
    for ( filenum = 0; filenum < MAX_FILES && buffinfo[filenum].opend != null_od; filenum++ );
    if ( filenum == MAX_FILES )
	return ( IO_FAIL );

    /* convert modecode to the kernel openmode */
    if ((openmode = modecov(modecode)) == FALSE)
	return ( IO_FAIL );

    if (UDM_find (ROOT, pathname, po_MKENTRY, &proSeid, &entry_seid, &entry_name)
	!= UDM_no_error)
	return ( IO_FAIL );

    if ((result = K_create(proSeid,openmode,da,null_od,&fSeid,&od)) != XOK)
    {
	printf ("k_create returned %d\n", result);
	return ( IO_FAIL );
    }

    if ((result = UDM_mkentry (proSeid, fSeid, &entry_name, TRUE)) != UDM_no_error)
    {
	K_close (od);
	printf ("udm mk entry returned %d\n", result);
	return ( IO_FAIL );
    }

    buffinfo[filenum].bseid = fSeid;
    buffinfo[filenum].opend = od;
    buffinfo[filenum].om = openmode;
    buffinfo[filenum].currBlk = LZERO;
    buffinfo[filenum].buffindex = LOWBUFF;
    buffinfo[filenum].written = OFF;
    buffinfo[filenum].outofrange = ON;

    pblk.location.domain = null_domain;
    pblk.location.space = d_space;
    pblk.location.address = buffinfo[filenum].buff;
    pblk.b_size = BLKSIZE;

    /* null fill the buffer */
    for (x=0; x <= HIGHBUFF;
	buffinfo[filenum].buff[x++] = NULL);

    return(buffinfo[filenum].opend);
}

N_close(od)
openDescriptor od;
{
    extern buffstruct buffinfo[];
    int    filenum;         /* buff index */

    /* check if valid opendescriptor */
    for (filenum = 0;filenum < MAX_FILES && buffinfo[filenum].opend != od; filenum++);
    if (filenum == MAX_FILES )
	    return(IO_FAIL);

    /* call N_flush if file is writable */
    if (buffinfo[filenum].om != om_read )
        if (N_flush(od) == IO_FAIL)
	    return (IO_FAIL);

    if ( K_close(od) == IO_FAIL )
	    return (IO_FAIL);

    buffinfo[filenum].opend = null_od;

    return(IO_OK);
}

N_flush(od)
openDescriptor od;
{
    extern buffstruct buffinfo[];
    K_err_num IO_stat = 0;	/* kernel error num */
    ioStatus IO_errs;		/* error structure */
    int    filenum = 0;		/* buff index */
    int    x;			/* index through null buffer */
    p_block      pblk;		/* for io Kernel calls */
    int          numread;       /* number of bytes read */
    int          done;          /* return flag of K_device_function */
    cardinal32   size;		/* the file size in bytes */
    file_stat_block	statblk;

    IO_errs.devIndep = XOK;

    /* check if valid opendescriptor */
    for (filenum = 0;filenum < MAX_FILES && buffinfo[filenum].opend != od; filenum++);
    if (filenum == MAX_FILES )
	    return (IO_FAIL);

    /* can flush only writable files */
    if (buffinfo[filenum].om == om_read)
	    return (IO_FAIL);

    /* fill rest of buffer with nulls */
    if ( buffinfo[filenum].outofrange )
    {
	    for (x = buffinfo[filenum].buffindex; x < HIGHBUFF;)
		    buffinfo[filenum].buff[++x] = NULL;
	    buffinfo[filenum].written = ON;
    }

    /* write block out */
    if ( buffinfo[filenum].written )
    {
	    pblk.location.domain = null_domain;
	    pblk.location.space = d_space;
	    pblk.location.address = buffinfo[filenum].buff;
	    pblk.b_size = BLKSIZE;

	    IO_stat = K_write_block(od,buffinfo[filenum].currBlk,&pblk,0,&IO_errs);
	    if ( IO_stat || (IO_errs.devIndep && (IO_errs.devIndep != END_OF_FILE)))
		return (IO_FAIL);

	    buffinfo[filenum].written = OFF;

	    /* get current file size */
	    IO_stat = K_get_file_status(buffinfo[filenum].bseid,null_od,&statblk);

	    /* cumpute desired size */
	    size = buffinfo[filenum].currBlk * LBLKSIZE + buffinfo[filenum].buffindex + LONE;

	    /* redefine file length if not correct */
	    if( buffinfo[filenum].outofrange && size != statblk.f_size )
	    { 	

		IO_stat = K_device_function(od,SETFILESIZE,size,&null_Pblk,&null_Pblk,0,&IO_errs,&done);
		if ( IO_stat || IO_errs.devIndep )
		    return (IO_FAIL);

	    }
    }
    return(IO_OK);
}


N_read(od,string,nchar)
openDescriptor od;
char	       *string;
int	       nchar;
{
    extern buffstruct buffinfo[];
    K_err_num IO_stat = 0;	/* kernel error num */
    ioStatus IO_errs;		/* error structure */
    int    filenum = 0;		/* buff index */
    int    result = 1;		/* return value */
    int    char_read = 0;	/* number of chars actually read */
    cardinal32   highblk;	/* highest block of file */
    int	   highindx;		/* highest index of file */
    file_stat_block statblk;	/* block struct containing file info */
    p_block      pblk;		/* for io Kernel calls */
    int          numread;       /* number of bytes read */

    IO_errs.devIndep = XOK;

    /* check if valid opendescriptor */
    for (filenum = 0;filenum < MAX_FILES && buffinfo[filenum].opend != od; filenum++);
    if (filenum == MAX_FILES )
	    return (IO_FAIL);

    /* check if valid access mode */
    if (buffinfo[filenum].om == om_write)
	    return (IO_FAIL);

    /* return eof if attempting to read out of range */
    if (buffinfo[filenum].outofrange)
	    return (IO_EOF);

    pblk.location.domain = null_domain;
    pblk.location.space = d_space;
    pblk.location.address = buffinfo[filenum].buff;
    pblk.b_size = BLKSIZE;

    /* write changes before reading */
    if (buffinfo[filenum].written)
    {
	IO_stat = K_write_block(od,buffinfo[filenum].currBlk,&pblk,0,&IO_errs);
	if ( IO_stat || (IO_errs.devIndep && (IO_errs.devIndep != END_OF_FILE)))
	    return( IO_FAIL );

	buffinfo[filenum].written = OFF;
    }

    /* read chars from buff */

	IO_stat = K_get_file_status(buffinfo[filenum].bseid,null_od,&statblk);
	if ( IO_stat == IO_FAIL )
	    return ( IO_FAIL );
	    
	statblk.f_size--;
	highblk = (statblk.f_size) / LBLKSIZE;
	highindx = (statblk.f_size) % LBLKSIZE;

	/* buffinfo indexes point to last char referenced */
	while( char_read++ < nchar )
	{
	    /* check for end of file */
	    if ( buffinfo[filenum].currBlk >= highblk &&
	    buffinfo[filenum].buffindex >= highindx )
	    {
		buffinfo[filenum].outofrange = ON;
		return (--char_read);
	    }

	    /* read new block */
	    if (buffinfo[filenum].buffindex >= HIGHBUFF)
	    {
		buffinfo[filenum].buffindex = LOWBUFF;
		buffinfo[filenum].currBlk++;

		IO_stat = K_read_block(od,buffinfo[filenum].currBlk,&pblk,0,&IO_errs,&numread);
		if ( IO_stat || (IO_errs.devIndep && (IO_errs.devIndep != END_OF_FILE)))
		    return (IO_FAIL);

	    }

	    /* move buffindex to point to next char not read */
	    buffinfo[filenum].buffindex++;

	    *string++ = buffinfo[filenum].buff[buffinfo[filenum].buffindex];
    }

    return(--char_read);
}

N_write(od,string,nchar)
openDescriptor od;
char	       *string;
int	       nchar;
{
    extern buffstruct buffinfo[];
    K_err_num IO_stat = 0;	/* kernel error num */
    ioStatus IO_errs;		/* error structure */
    int    filenum = 0;		/* buff index */
    int    result = 1;		/* return value */
    int    char_written = 0;	/* number of chars actually read */
    int	   numread;		/* number of bytes read */
    int    highindx;            /* value of highest index */
    cardinal32  highblk;        /* value of highest block */
    file_stat_block statblk;	/* block struct containing file info */
    p_block     pblk;		/* for io Kernel calls */

    IO_errs.devIndep = XOK;

    if (nchar < 0)
	return (IO_FAIL);

    /* check if valid opendescriptor */
    for (filenum = 0;filenum < MAX_FILES && buffinfo[filenum].opend != od; filenum++);
    if (filenum == MAX_FILES )
	return ( IO_FAIL );
	
    /* check if valid access mode */
    if (buffinfo[filenum].om == om_read)
	return ( IO_FAIL );

    pblk.location.domain = null_domain;
    pblk.location.space = d_space;
    pblk.location.address = buffinfo[filenum].buff;
    pblk.b_size = BLKSIZE;

    IO_stat = K_get_file_status(buffinfo[filenum].bseid,null_od,&statblk);
    if ( IO_stat == IO_FAIL )
	return ( IO_FAIL );

    statblk.f_size--;
    highblk = (statblk.f_size) / LBLKSIZE;
    highindx = (statblk.f_size) % LBLKSIZE;

    if ( buffinfo[filenum].currBlk >= highblk &&
    buffinfo[filenum].buffindex >= highindx)
	buffinfo[filenum].outofrange = ON;

    while( char_written++ < nchar )
    {
	/* write out finished block */
	if (buffinfo[filenum].buffindex >= HIGHBUFF )
	{
	    IO_stat = K_write_block(od,buffinfo[filenum].currBlk,&pblk,0,&IO_errs);
	    if ( IO_stat || (IO_errs.devIndep && (IO_errs.devIndep != END_OF_FILE)))
		    return ( IO_FAIL );

	    buffinfo[filenum].buffindex = LOWBUFF;
	    buffinfo[filenum].currBlk++;

	    /* read new block if the file is readable */
	    if (buffinfo[filenum].om != om_write) 
	    {
		IO_stat = K_read_block(od,buffinfo[filenum].currBlk,
		    &pblk,0,&IO_errs,&numread);
		if (IO_stat || (IO_errs.devIndep && (IO_errs.devIndep != END_OF_FILE)))
		    return ( IO_FAIL );
	    }
	}

	/* move buffindex to point to next char not written */
	buffinfo[filenum].buffindex++;

	/* copy char from string to buffer */
    	buffinfo[filenum].buff[buffinfo[filenum].buffindex] = *string++;
    }
    buffinfo[filenum].written = ON;

    return(--char_written);
}

N_seek(od,offset,origin) 
openDescriptor od;
long    offset;
int     origin;  /*(0,1,2) */
{
    extern buffstruct buffinfo[];
    K_err_num IO_stat = 0;	/* kernel error num */
    ioStatus IO_errs;		/* error structure */
    int    filenum = 0;		/* buff index */
    int    indx;		/* remainder of chars after blocks calc */
    int    highindx;            /* value of highest index */
    int    x;			/* index through buff end */
    cardinal32  size;		/* the file size in bytes */
    cardinal32  block;		/* number of blocks in offset */
    cardinal32  highblk;        /* value of highest block */
    p_block      pblk;          /* for io Kernel calls */
    cardinal     numread;       /* number of bytes read */
    file_stat_block statblk;	/* block struct containing file info */

    IO_errs.devIndep = XOK;

    /* check if valid opendescriptor */
    for (filenum = 0;filenum < MAX_FILES && buffinfo[filenum].opend != od; filenum++);
    if (filenum == MAX_FILES )
	return (IO_FAIL);
	
    pblk.location.domain = null_domain;
    pblk.location.space = d_space;
    pblk.location.address = buffinfo[filenum].buff;
    pblk.b_size = BLKSIZE;

    /* write out before moving indexes */
    if (buffinfo[filenum].written)
    {
	if ( N_flush(od) == IO_FAIL)
	    return (IO_FAIL);
    }

    offset--;
    block = (offset) / LBLKSIZE;
    indx  = (offset) % LBLKSIZE;       /* index set one before char
					     to be considered */

    IO_stat = K_get_file_status(buffinfo[filenum].bseid,null_od,&statblk);
    if ( IO_stat == IO_FAIL )
	return ( IO_FAIL );

    statblk.f_size--;
    highblk  = (statblk.f_size) / LBLKSIZE;
    highindx = (statblk.f_size) % LBLKSIZE;

    switch (origin)
    {
	/* end of file, backward seek */
	case -2: if ((highblk < block) || ((highblk == block) && (highindx < indx)))
		     return (IO_FAIL);

		 if (highindx < indx)
		 {
			highindx += BLKSIZE;
			highblk--;
		 }
		 buffinfo[filenum].currBlk = highblk - block;
		 buffinfo[filenum].buffindex = highindx - indx;
		 break;

	/* current position, backward seek */
	case -1: if ((buffinfo[filenum].currBlk < block) || ((buffinfo[filenum].currBlk == block) && (buffinfo[filenum].buffindex < indx)))
		     return (IO_FAIL);

		 if (buffinfo[filenum].buffindex < indx)
		 {
			buffinfo[filenum].buffindex += BLKSIZE;
			buffinfo[filenum].currBlk--;
		 }
		 buffinfo[filenum].currBlk -= block;
		 buffinfo[filenum].buffindex -= indx;
		 break;

	/* beginning of file */
	case 0:  buffinfo[filenum].currBlk = block;
		 buffinfo[filenum].buffindex = indx;
		 break;

	/* current position in file */
	case 1:
		buffinfo[filenum].currBlk += block;
		if ( (buffinfo[filenum].buffindex += indx) >= BLKSIZE )
		{
			buffinfo[filenum].buffindex -= BLKSIZE;
			buffinfo[filenum].currBlk += LONE;
		}
		break;

	/* end of file */
	case 2:
		 buffinfo[filenum].currBlk = block + highblk;
		 if ( (buffinfo[filenum].buffindex = indx + highindx + 1) >= BLKSIZE )
		 {
		     buffinfo[filenum].buffindex -= BLKSIZE;
		     buffinfo[filenum].currBlk += LONE;
		 }
		 break;

	default:
	    return (IO_FAIL);
    }

    if ( buffinfo[filenum].om != om_write )
    {
	IO_stat = K_read_block(od,buffinfo[filenum].currBlk,&pblk,0,&IO_errs,&numread);
	if ((IO_stat != IO_OK) || (IO_errs.devIndep && (IO_errs.devIndep != END_OF_FILE)))
	    return (IO_FAIL);

    }
    else
    {
	/* fill buffer with nulls */
	for (x = buffinfo[filenum].buffindex; x < HIGHBUFF;)
	    buffinfo[filenum].buff[++x] = NULL;
    }

    buffinfo[filenum].outofrange = OFF;

    if ( buffinfo[filenum].currBlk >= highblk &&
    buffinfo[filenum].buffindex >= highindx)
	buffinfo[filenum].outofrange = ON;

    return(IO_OK);
}

N_tell_seid (od, bbseid)        /* given od, finds bbseid */
seid *bbseid;
openDescriptor od;
{
    extern buffstruct buffinfo[];
    int filenum;                /* buff index */

    /* check if valid opendescriptor */
    for (filenum = 0;filenum < MAX_FILES && buffinfo[filenum].opend != od; filenum++);
    if (filenum == MAX_FILES )
	    return(IO_FAIL);

    /* get seid */
    *bbseid = buffinfo[filenum].bseid;

    return(IO_OK);
}
