
/*
 *  XFIO.C
 *
 *  Simple File IO with asyncronous READ and WRITE capability
 *  Perfect for protocol transfer applications
 *
 *  xfi = xfopen(name, modes, bufsize)	("r", "w", "w+")
 *   n	= xfread(xfi, buf, bytes)   ASYNCRONOUS READ
 *  err = xfwrite(xfi, buf, bytes)  ASYNCRONOUS WRITE
 *  err = xfclose(xfi)
 *
 *  RESTRICTIONS:   NO seeking.  You can do one of xfread() or xfwrite()
 *  for a given open XFIle handle (not both).
 *
 *  xfwrite() returns a cumulative error (once an error occurs, it will not
 *  do any more writes).  xfclose() returns the cumulative write error
 *  (since the last write may have been asyncronous and thus the error
 *  unknown at the time).
 *
 *  Two buffers are created each bufsize/2 bytes in size.  for writing,
 *  one buffers is sent asyncronously while the other fills.  For reading,
 *  one buffer is filling while the other is being read.
 */

#define XFI	    struct _XFI
#define XFBUF	    struct _XFBUF
#define MSGPORT     struct MsgPort
#define FH	    struct FileHandle
#define STDPKT	    struct StandardPacket


XFBUF {
    long   bufsize;
    long   idx;
    long   max;
    char    buf[4];	/*  actually bufsize bytes long */
};

XFI {
    char    ro; 	/*  read only, else write only	*/
    char    pend;	/*  packet pending		*/
    char    err;	/*  cumulative error		*/
    char    reserved;
    XFBUF   *asbuf;
    XFBUF   *usbuf;
    FH	    *fh;
    STDPKT  sp; 	/*  asyncronous message 	*/
    MSGPORT rp; 	/*  reply port for pending pkts */
};

extern FH *Open();
extern void *malloc(), *FindTask();

void *
xfopen(file, mode, bytes)
char *file;
char *mode;
{
    register XFI *xfi = malloc(sizeof(XFI));
    register long nbytes = bytes >> 1;
    int ap = 0;

    bzero(xfi, sizeof(XFI));
    if (mode[0] == 'w') {
	if (mode[1] == '+') {
	    ap = 1;
	    if ((xfi->fh = Open(file, 1005)) == NULL)
		xfi->fh = Open(file, 1006);
	    goto ok;
	}
	xfi->fh = Open(file, 1006);
	goto ok;
    }
    xfi->fh = Open(file, 1005);
ok:
    if (xfi->fh) {
	if (ap)
	    Seek(xfi->fh, 0, 1);
	xfi->fh = (FH *)((long)xfi->fh << 2);
	xfi->asbuf = malloc(sizeof(XFBUF) + nbytes);	/* a little more    */
	xfi->usbuf = malloc(sizeof(XFBUF) + nbytes);	/* then we need     */
	bzero(xfi->asbuf, sizeof(XFBUF));
	bzero(xfi->usbuf, sizeof(XFBUF));
	xfi->ro = (mode[0] == 'r');
	xfi->asbuf->bufsize = xfi->usbuf->bufsize = nbytes;
	xfi->rp.mp_Node.ln_Type = NT_MSGPORT;
	xfi->rp.mp_Node.ln_Name = "XFIO-Async";
	xfi->rp.mp_Flags = PA_SIGNAL;
	xfi->rp.mp_SigBit = AllocSignal(-1);
	xfi->rp.mp_SigTask = FindTask(NULL);
	NewList(&xfi->rp.mp_MsgList);
	if (xfi->ro)
	    xfstartasync(xfi, ACTION_READ);
    } else {
	free(xfi);
	xfi = NULL;
    }
    return(xfi);
}

xfclose(xfi)
register XFI *xfi;
{
    int err = 1;
    if (xfi) {
	if (xfi->pend) {
	    xfi->pend = 0;
	    WaitPort (&xfi->rp);
	    GetMsg   (&xfi->rp);
	}
	if (!xfi->ro && xfi->usbuf->idx)
	    Write((long)xfi->fh >> 2, xfi->usbuf->buf, xfi->usbuf->idx);
	err = xfi->err;
	Close((long)xfi->fh >> 2);
	free(xfi->asbuf);
	free(xfi->usbuf);
	FreeSignal(xfi->rp.mp_SigBit);
	free(xfi);
    }
    return(err);
}

xfgets(xfi, buf, n)
XFI *xfi;
char *buf;
{
    register XFBUF *usbuf = xfi->usbuf;
    register int i, idx;
    if (!xfi->ro)
	return(-1);
    --n;
    for (i = 0;;) {
	for (idx = usbuf->idx; idx < usbuf->max && i < n; ++idx, ++i) {
	    if ((buf[i] = usbuf->buf[idx]) == '\n') {
		buf[i] = 0;
		usbuf->idx = idx+1;
		return(i);
	    }
	}
	usbuf->idx = idx;
	buf[i] = 0;
	if (i == n)
	    return(i);
	if (xfi->pend == 0)				/* EOF	    */
	    return(-1);
	WaitPort (&xfi->rp);
	GetMsg	 (&xfi->rp);
	xfi->pend = 0;
	if (xfi->sp.sp_Pkt.dp_Res1 <= 0) {		/* EOF	    */
	    if (i == 0)
		return(-1);
	    return(i);
	}
	xfi->asbuf->max = xfi->sp.sp_Pkt.dp_Res1;
	xfi->asbuf->idx = 0;
	usbuf = xfi->asbuf;				/* swap bufs*/
	xfi->asbuf = xfi->usbuf;
	xfi->usbuf = usbuf;
	xfstartasync(xfi, ACTION_READ); 		/* new async*/
    }
}


xfread(xfi, buf, n)
XFI *xfi;
char *buf;
{
    register XFBUF *usbuf = xfi->usbuf;
    register int orig = n;
    register int diff;

    if (!xfi->ro)
	return(0);
    while ((diff = usbuf->max - usbuf->idx) < n) {
	movmem(usbuf->buf + usbuf->idx, buf, diff);	/* copy entire buf */
	buf += diff;
	n -= diff;
	if (xfi->pend == 0) {
	    xfi->usbuf->idx = xfi->usbuf->max;
	    return(orig - n);
	}
	WaitPort (&xfi->rp);
	GetMsg	 (&xfi->rp);
	xfi->pend = 0;
	if (xfi->sp.sp_Pkt.dp_Res1 <= 0) {		/* EOF	    */
	    xfi->usbuf->idx = xfi->usbuf->max;
	    return(orig - n);
	}
	xfi->asbuf->max = xfi->sp.sp_Pkt.dp_Res1;
	xfi->asbuf->idx = 0;
	usbuf = xfi->asbuf;				/* swap bufs*/
	xfi->asbuf = xfi->usbuf;
	xfi->usbuf = usbuf;
	xfstartasync(xfi, ACTION_READ); 		/* new async*/
    }
    movmem(usbuf->buf + usbuf->idx, buf, n);
    usbuf->idx += n;
    return(orig);
}

xfwrite(xfi, buf, n)
XFI *xfi;
char *buf;
{
    register XFBUF *usbuf = xfi->usbuf;
    register int diff;

    if (xfi->ro || xfi->err)
	return(1);
    while ((diff = usbuf->bufsize - usbuf->idx) < n) {
	movmem(buf, usbuf->buf + usbuf->idx, diff);	/*  copy buf	*/
	buf += diff;
	n -= diff;
	if (xfi->pend) {
	    WaitPort(&xfi->rp);
	    GetMsg  (&xfi->rp);
	    xfi->pend = 0;
	    if (xfi->sp.sp_Pkt.dp_Res1 != xfi->sp.sp_Pkt.dp_Arg3) {
		xfi->err = 1;
		return(1);
	    }
	}
	usbuf = xfi->asbuf;
	xfi->asbuf = xfi->usbuf;
	xfi->usbuf = usbuf;
	usbuf->idx = 0;
	xfstartasync(xfi, ACTION_WRITE);
    }
    movmem(buf, usbuf->buf + usbuf->idx, n);
    usbuf->idx += n;
    return(xfi->err);
}

static
xfstartasync(xfi, action)
register 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;
    xfi->sp.sp_Pkt.dp_Arg1 = xfi->fh->fh_Arg1;
    xfi->sp.sp_Pkt.dp_Arg2 = (long)xfi->asbuf->buf;
    xfi->sp.sp_Pkt.dp_Arg3 = xfi->asbuf->bufsize;
    PutMsg (xfi->fh->fh_Type, &xfi->sp);
    xfi->pend = 1;
}


