/* begincopyright
  Copyright (c) 1988, 1989 Xerox Corporation. All rights reserved.
  Use and copying of this software and preparation of derivative works based
  upon this software are permitted. Any distribution of this software or
  derivative works must comply with all applicable United States export
  control laws. This software is made available AS IS, and Xerox Corporation
  makes no warranty about the software, its performance or its conformity to
  any specification. Any person obtaining a copy of this software is requested
  to send their name and post office or electronic mail address to:
    PCR Coordinator
    Xerox PARC
    3333 Coyote Hill Rd.
    Palo Alto, CA
  endcopyright */
/*
*	ThreadsTLI.c
*
*	Tim Diebert: November 21, 1989 5:59:51 pm PST
*	Michael Plass: February 18, 1992 9:51:11 am PST
*
*	This module provides an interface to the Mentat XNS implementation
*	via the AT&T TLI interfaces.  This code started life looking 
*	suprisingly like the AT&T stuff except that it maintains no global
*	state.
*
*/

#include <ThreadsTLI.h>
#include <xr/UIO.h>
#include <xr/Errno.h>

#include <sys/fcntl.h>
#include <sys/param.h> 		/* would you believe NULL is in here */


#include <sys/stream.h>		/* queue←t */
#include <sys/stropts.h>	/* stream ioctl things */

#include <sys/tihdr.h>
#include <nettli/timod.h>
/*	#include <nettli/tiuser.h>	included in ThreadsTLI.h */

#define DEFSIZE 2048

extern XR←Pointer GC←malloc(); 

/* Open and Close */
int
XR←T←Open(cd, path, flags, info)
	struct conn←data * cd;
	char * path;
	int flags;
	register struct t←info * info;
{
	int retval;
	struct T←info←ack inforeq;
	int retlen;

	/* Open up the device.  In the case of the Mentat XNS path should be
	* /dev/xr
	*/
	if ((cd->cd←fd = XR←OpenStream(path, flags)) < 0) {
		cd->cd←errno = TSYSERR;
		return(-1);
	}

	/*
	 * is module already pushed ?
	 */
	if ((retval = XR←IOCtl(cd->cd←fd, I←FIND, "timod")) < 0) {
		cd->cd←errno = TSYSERR;
		XR←Close(cd->cd←fd);
		return(-1);
	}

	if (!retval)
		if (XR←IOCtl(cd->cd←fd, I←PUSH, "timod") < 0) {
			cd->cd←errno = TSYSERR;
			XR←Close(cd->cd←fd);
			return(-1);
		}

	inforeq.PRIM←type = T←INFO←REQ;

	if (!DoTIModIOCtl(cd, (caddr←t)&inforeq, sizeof(struct T←info←req),
	    TI←GETINFO, &retlen)) {
		XR←Close(cd->cd←fd);
		return(-1);
	}

	if (retlen != sizeof(struct T←info←ack)) {
		cd->cd←errno = TSYSERR;
		XR←SetErrno(EIO);
		XR←Close(cd->cd←fd);
		return(-1);
	}
	
	cd->cd←info.addr = inforeq.ADDR←size;
	cd->cd←info.options = inforeq.OPT←size;
	cd->cd←info.tsdu = inforeq.TSDU←size;
	cd->cd←info.etsdu = inforeq.ETSDU←size;
	cd->cd←info.connect = inforeq.CDATA←size;
	cd->cd←info.discon = inforeq.DDATA←size;
	cd->cd←info.servtype = inforeq.SERV←type;

	if (info != NULL) {
		info->addr = inforeq.ADDR←size;
		info->options = inforeq.OPT←size;
		info->tsdu = inforeq.TSDU←size;
		info->etsdu = inforeq.ETSDU←size;
		info->connect = inforeq.CDATA←size;
		info->discon = inforeq.DDATA←size;
		info->servtype = inforeq.SERV←type;
	}

	/*
	 * initialize data structure and allocate buffers if needed
	 */
	if (!(cd->cd←flags & USED)) {

		if (AllocateBuffers(cd, inforeq) < 0){ 
			XR←T←Close(cd);
			cd->cd←errno = TSYSERR;
			return(-1);
		}
		if(XR←IOCtl(cd->cd←fd, I←FLUSH, 3L) < 0) {
			XR←T←Close(cd);
			cd->cd←errno = TSYSERR;
			return(-1);
		}
		cd->cd←flags = cd->cd←flags | USED;
	}
	return(cd->cd←fd);
}


int
XR←T←Close(cd)
	struct conn←data * cd;
{

	if (CheckCD(cd)) return(-1);
	
/*  don't release the memory, since someone else allocated it.
*	(void)free(cd->cd←rcvbuf);
*	(void)free(cd->cd←ctlbuf);
*	(void)free(cd->cd←lookcbuf);
*	(void)free(cd->cd←lookdbuf);
*/

	XR←Close(cd->cd←fd);
	cd->cd←fd = -1; /* mark it as closed */
	return(0);
}

/* Binding */
int
XR←T←Bind(cd, req, ret)
	struct conn←data * cd;
	register struct t←bind *req;
	register struct t←bind *ret;
{
	int fd;
	register char *buf;
	register struct T←bind←req *ti←bind;
	int size;
	
	if (CheckCD(cd)) return(-1);
	fd = cd->cd←fd;
	buf = cd->cd←ctlbuf;
	ti←bind = (struct T←bind←req *)buf;
	size = sizeof(struct T←bind←req);

	ti←bind->PRIM←type = T←BIND←REQ;
	ti←bind->ADDR←length = (req == NULL? 0: req->addr.len);
	ti←bind->ADDR←offset = 0;
	ti←bind->CONIND←number = (req == NULL? 0: req->qlen);


	if (ti←bind->ADDR←length) {
		AlignedCopy(buf, (int)ti←bind->ADDR←length, size,
			     req->addr.buf, &ti←bind->ADDR←offset);
		size = ti←bind->ADDR←offset + ti←bind->ADDR←length;
	}
			       

	if (!DoTIModIOCtl(cd, buf, size, TI←BIND, 0L)) {
		return(-1);
	}

	if ((ret != NULL) && (ti←bind->ADDR←length > ret->addr.maxlen)) {
		cd->cd←errno = TBUFOVFLW;
		return(-1);
	}

	if (ret != NULL) {
		bcopy((char *)(buf + ti←bind->ADDR←offset), ret->addr.buf,
		       (int)ti←bind->ADDR←length);
		ret->addr.len = ti←bind->ADDR←length;
		ret->qlen = ti←bind->CONIND←number;
	}

	return(0);
}

int
XR←T←UnBind(cd)
	struct conn←data * cd;
{
	register struct ←ti←user *tiptr;
	struct T←unbind←req *unbind←req;

	if (CheckCD(cd)) return(-1);
	if (IsEvent(cd->cd←fd, tiptr)) {
		return(-1);
	}

	unbind←req = (struct T←unbind←req *)cd->cd←ctlbuf;
	unbind←req->PRIM←type = T←UNBIND←REQ;

	if (!DoTIModIOCtl(cd, (caddr←t)unbind←req, sizeof(struct T←unbind←req),
	    TI←UNBIND, 0)) {
		return(-1);
	}

	if (XR←IOCtl(cd->cd←fd, I←FLUSH, FLUSHRW) < 0) {
		cd->cd←errno = TSYSERR;
		return(-1);
	}

	/*
	 * clear more data in TSDU bit
	 */
	cd->cd←flags &= ~MORE;

	return(0);
}

/* Listening  */
int
XR←T←Listen(cd, call)
	struct conn←data * cd;
	struct t←call *call;
{
	struct strbuf ctlbuf;
	struct strbuf rcvbuf;
	int flg = 0;
	int retval;
	register union T←primitives *pptr;
	
	if (CheckCD(cd)) return(-1);
	
	/*
         * check if something in look buffer
	 */
	if (cd->cd←lookflg) {
		cd->cd←errno = TLOOK;
		return(-1);
	}

	if (cd->cd←servtype == T←CLTS) {
		cd->cd←errno = TNOTSUPPORT;
		return(-1);
	}

	ctlbuf.maxlen = cd->cd←ctlsize;
	ctlbuf.len = 0;
	ctlbuf.buf = cd->cd←ctlbuf;
	rcvbuf.maxlen = cd->cd←rcvsize;
	rcvbuf.len = 0;
	rcvbuf.buf = cd->cd←rcvbuf;

	if ((retval = XR←GetMsg(cd->cd←fd, &ctlbuf, &rcvbuf, &flg)) < 0) {
		if (XR←GetErrno() == EAGAIN)
			cd->cd←errno = TNODATA;
		else
			cd->cd←errno = TSYSERR;
		return(-1);
	}
	if (rcvbuf.len == -1) rcvbuf.len = 0;

	/*
	 * did I get entire message?
	 */
	if (retval) {
		cd->cd←errno = TSYSERR;
		XR←SetErrno(EIO);
		return(-1);
	}

	/*
	 * is ctl part large enough to determine type
	 */
	if (ctlbuf.len < sizeof(long)) {
		cd->cd←errno = TSYSERR;
		XR←SetErrno(EPROTO);
		return(-1);
	}

	pptr = (union T←primitives *)ctlbuf.buf;

	switch(pptr->type) {
		case T←CONN←IND:
			if ((ctlbuf.len < sizeof(struct T←conn←ind)) ||
			    (ctlbuf.len < (pptr->conn←ind.OPT←length
			    + pptr->conn←ind.OPT←offset))) {
				cd->cd←errno = TSYSERR;
				XR←SetErrno(EPROTO);
				return(-1);
			}
			if ((rcvbuf.len > (int)call->udata.maxlen) ||
			    (pptr->conn←ind.SRC←length > call->addr.maxlen) ||
			    (pptr->conn←ind.OPT←length > call->opt.maxlen)) {
				cd->cd←errno = TBUFOVFLW;
				return(-1);
			}

			bcopy(ctlbuf.buf + pptr->conn←ind.SRC←offset,
				call->addr.buf,
				(int)pptr->conn←ind.SRC←length);
			call->addr.len = pptr->conn←ind.SRC←length;
			bcopy(ctlbuf.buf + pptr->conn←ind.OPT←offset,
				call->opt.buf,
				(int)pptr->conn←ind.OPT←length);
			call->opt.len = pptr->conn←ind.OPT←length;
			bcopy(rcvbuf.buf, call->udata.buf, (int)rcvbuf.len);
			call->udata.len = rcvbuf.len;
			call->sequence = pptr->conn←ind.SEQ←number;
			return(0);

		case T←DISCON←IND:
			PutBack(cd, rcvbuf.buf, rcvbuf.len, ctlbuf.buf,
				ctlbuf.len);
			cd->cd←errno = TLOOK;
			return(-1);

		default:
			break;
	}

	cd->cd←errno = TSYSERR;
	errno = EPROTO;
	return(-1);
}

int
XR←T←Accept(cd, rescd, call)
	struct conn←data * cd;
	struct conn←data * rescd;
	struct t←call *call;
{
	char *buf;
	register struct T←conn←res *cres;
	struct strfdinsert strfdinsert;
	int size;
	int retval;
	int resfd;
	
	if (CheckCD(cd)) return(-1);
	if (CheckCD(rescd)) return(-1);
	
	resfd = rescd->cd←fd;

	if (cd->cd←servtype == T←CLTS) {
		cd->cd←errno = TNOTSUPPORT;
		return(-1);
	}

	if (cd->cd←fd != resfd)
	{
		if ((retval = XR←IOCtl(resfd, I←NREAD, &size)) < 0)
		{
			cd->cd←errno = TSYSERR;
			return(-1);
		}
		if (retval)
		{
			cd->cd←errno = TBADF;
			return(-1);
		}
	}

	if (IsEvent(cd)) {
		return(-1);
	}

	buf = cd->cd←ctlbuf;
	cres = (struct T←conn←res *)buf;
	cres->PRIM←type = T←CONN←RES;
	cres->OPT←length = call->opt.len;
	cres->OPT←offset = 0;
	cres->SEQ←number = call->sequence;
	size = sizeof(struct T←conn←res);

	if (call->opt.len) {
		AlignedCopy(buf, call->opt.len, size,
			     call->opt.buf, &cres->OPT←offset);
		size = cres->OPT←offset + cres->OPT←length;
	}


	strfdinsert.ctlbuf.maxlen = cd->cd←ctlsize;
	strfdinsert.ctlbuf.len = size;
	strfdinsert.ctlbuf.buf = buf;
	strfdinsert.databuf.maxlen = call->udata.maxlen;
	strfdinsert.databuf.len = (call->udata.len? call->udata.len: -1);
	strfdinsert.databuf.buf = call->udata.buf;
	strfdinsert.fildes = resfd;
	strfdinsert.offset = sizeof(long);
	strfdinsert.flags = 0;      /* could be EXPEDITED also */

	if (XR←IOCtl(cd->cd←fd, I←FDINSERT, &strfdinsert) < 0) {
		if (XR←GetErrno() == EAGAIN)
			cd->cd←errno = TFLOW;
		else
			cd->cd←errno = TSYSERR;
		return(-1);
	}

	if (!IsOK(cd, (long)T←CONN←RES)) {
		return(-1);
	}

	return(0);
}

/*  State and Info gathering */
int
XR←T←GetInfo(cd, info)
	struct conn←data * cd;
	register struct t←info *info;
{
	struct T←info←ack inforeq;
	int retlen;
	
	if (CheckCD(cd)) return(-1);
	inforeq.PRIM←type = T←INFO←REQ;

	if (!DoTIModIOCtl(cd, (caddr←t)&inforeq, sizeof(struct T←info←req),
	    TI←GETINFO, &retlen)) {
		return(-1);
	}
		
	if (retlen != sizeof(struct T←info←ack)) {
		XR←SetErrno(EIO);
		cd->cd←errno = TSYSERR;
		return(-1);
	}

	info->addr = inforeq.ADDR←size;
	info->options = inforeq.OPT←size;
	info->tsdu = inforeq.TSDU←size;
	info->etsdu = inforeq.ETSDU←size;
	info->connect = inforeq.CDATA←size;
	info->discon = inforeq.DDATA←size;
	info->servtype = inforeq.SERV←type;

	return(0);
}


int
XR←T←GetState(cd)
	struct conn←data * cd;
{
	struct T←info←ack info;
	int retlen;
	
	if (CheckCD(cd)) return(-1);
	
	info.PRIM←type = T←INFO←REQ;

	if (!DoTIModIOCtl(cd, (caddr←t)&info,
	    sizeof(struct T←info←req), TI←GETINFO, &retlen)) {
		return(-1);
	}
		
	if (retlen != sizeof(struct T←info←ack)) {
		cd->cd←errno = TSYSERR;
		XR←SetErrno(EIO);
		return(-1);
	}

	switch (info.CURRENT←state) {

	case TS←UNBND:
		return(T←UNBND);
	case TS←IDLE:
		return(T←IDLE);
	case TS←WRES←CIND:
		return(T←INCON);
	case TS←WCON←CREQ:
		return(T←OUTCON);
	case TS←DATA←XFER:
		return(T←DATAXFER);
	case TS←WIND←ORDREL:
		return(T←OUTREL);
	case TS←WREQ←ORDREL:
		return(T←INREL);
	default:
		cd->cd←errno = TSTATECHNG;
		return(-1);
	}
}

int
XR←T←Look(cd)
	register struct conn←data * cd;
{
	struct strpeek strpeek;
	int retval;
	union T←primitives *pptr;
	long type;
	
	if (CheckCD(cd)) return(-1);

	strpeek.ctlbuf.maxlen = sizeof(long);
	strpeek.ctlbuf.len = 0;
	strpeek.ctlbuf.buf = cd->cd←ctlbuf;
	strpeek.databuf.maxlen = 0;
	strpeek.databuf.len = 0;
	strpeek.databuf.buf = NULL;
	strpeek.flags = 0;

	if ((retval = XR←IOCtl(cd->cd←fd, I←PEEK, &strpeek)) < 0)
		return(T←ERROR);

	/*
	 * if something there and cnt part also there
	 */
	if (cd->cd←lookflg || (retval && (strpeek.ctlbuf.len >= sizeof(long))))
	    {
		pptr = (union T←primitives *)strpeek.ctlbuf.buf;
		if (cd->cd←lookflg) {
			if (((type = *((long *)cd->cd←lookcbuf)) !=
			    T←DISCON←IND) &&
		    	    (retval && (pptr->type == T←DISCON←IND))) {
				type = pptr->type;
				cd->cd←lookflg = 0;
			}
		} else
			type = pptr->type;
		switch(type) {

		case T←CONN←IND:
			return(T←LISTEN);

		case T←CONN←CON:
			return(T←CONNECT);

		case T←DISCON←IND:
			return(T←DISCONNECT);

		case T←DATA←IND:
		case T←UNITDATA←IND:
			return(T←DATA);

		case T←EXDATA←IND:
			return(T←EXDATA);

		case T←UDERROR←IND:
			return(T←UDERR);

		case T←ORDREL←IND:
			return(T←ORDREL);

		default:
			cd->cd←errno = TSYSERR;
			XR←SetErrno(EPROTO);
			return(-1);
		}
	}

	/*
	 * if something there put no control part
	 * it must be data
	 */
	if (retval && (strpeek.ctlbuf.len <= 0))
		return(T←DATA);

	/*
	 * if msg there and control
	 * part not large enough to determine type?
	 * it must be illegal TLI message
	 */
	if (retval && (strpeek.ctlbuf.len > 0)) {
		cd->cd←errno = TSYSERR;
		XR←SetErrno(EPROTO);
		return(-1);
	}
	return(0);
}


/* Reliable streams */
int
XR←T←Connect(cd, sndcall, rcvcall)
	struct conn←data * cd;
	struct t←call *sndcall;
	struct t←call *rcvcall;
{
	int fctlflg;

	if (CheckCD(cd)) return(-1);
	
	if (SendConnectionRequest(cd, sndcall) < 0)
		return(-1);

	if ((fctlflg = XR←FCntl(cd->cd←fd, F←GETFL, 0)) < 0) {
		cd->cd←errno = TSYSERR;
		return(-1);
	}

	if (fctlflg & O←NDELAY) {
		cd->cd←errno = TNODATA;
		return(-1);
	}

	if (ReceiveConnectionConfirmation(cd, rcvcall) < 0) {
		return(-1);
	}

	return(0);
}

/*
 * SendConnectionRequest - send connect request message to 
 * transport provider
 */
 int
SendConnectionRequest(cd, call)
	struct conn←data * cd;
	register struct t←call *call;
{
	register struct T←conn←req *creq;
	struct strbuf ctlbuf;
	char *buf;
	int size;
	
	if (CheckCD(cd)) return(-1);
	
	if (cd->cd←servtype == T←CLTS) {
		cd->cd←errno = TNOTSUPPORT;
		return(-1);
	}

	if (IsEvent(cd)) {
		return(-1);
	}


	buf = cd->cd←ctlbuf;
	creq = (struct T←conn←req *)buf;
	creq->PRIM←type = T←CONN←REQ;
	creq->DEST←length = call->addr.len;
	creq->DEST←offset = 0L;
	creq->OPT←length = call->opt.len;
	creq->OPT←offset = 0L;
	size = sizeof(struct T←conn←req);

	if (call->addr.len) {
		AlignedCopy(buf, call->addr.len, size,
			     call->addr.buf, &creq->DEST←offset);
		size = creq->DEST←offset + creq->DEST←length;
	}
	if (call->opt.len) {
		AlignedCopy(buf, call->opt.len, size,
			     call->opt.buf, &creq->OPT←offset);
		size = creq->OPT←offset + creq->OPT←length;
	}

	ctlbuf.maxlen = cd->cd←ctlsize;
	ctlbuf.len = size;
	ctlbuf.buf = buf;

	if (XR←PutMsg(cd->cd←fd, &ctlbuf, (call->udata.len? &call->udata: 0L), 0) < 0) {
		cd->cd←errno = TSYSERR;
		return(-1);
	}

	if (!IsOK(cd, (long)T←CONN←REQ)) {
		return(-1);
	}

	return(0);
}



/*
 * ReceiveConnectionConfirmation - get connection confirmation off
 * of read queue
 */
 
int
ReceiveConnectionConfirmation(cd, call)
	struct conn←data * cd;
	register struct t←call *call;
{
	struct strbuf ctlbuf;
	struct strbuf rcvbuf;
	int flg = 0;
	register union T←primitives *pptr;
	int retval;
	
	if (CheckCD(cd)) return(-1);
	if (cd->cd←servtype == T←CLTS) {
		cd->cd←errno = TNOTSUPPORT;
		return(-1);
	}

	/*
         * see if there is something in look buffer
	 */
	if (cd->cd←lookflg) {
		cd->cd←errno = TLOOK;
		return(-1);
	}

	ctlbuf.maxlen = cd->cd←ctlsize;
	ctlbuf.len = 0;
	ctlbuf.buf = cd->cd←ctlbuf;

	rcvbuf.maxlen = cd->cd←rcvsize;
	rcvbuf.len = 0;
	rcvbuf.buf = cd->cd←rcvbuf;

	if ((retval = XR←GetMsg(cd->cd←fd, &ctlbuf, &rcvbuf, &flg)) < 0) {
		if (XR←GetErrno() == EAGAIN)
			cd->cd←errno = TNODATA;
		else
			cd->cd←errno = TSYSERR;
		return(-1);
	}
	if (rcvbuf.len == -1) rcvbuf.len = 0;


	/*
	 * did we get entire message 
	 */
	if (retval) {
		cd->cd←errno = TSYSERR;
		XR←SetErrno(EIO);
		return(-1);
	}

	/*
	 * is cntl part large enough to determine message type?
	 */
	if (ctlbuf.len < sizeof(long)) {
		cd->cd←errno = TSYSERR;
		XR←SetErrno(EPROTO);
		return(-1);
	}

	pptr = (union T←primitives *)ctlbuf.buf;

	switch((int)pptr->type) {

		case T←CONN←CON:

			if ((ctlbuf.len < sizeof(struct T←conn←con)) ||
			    (ctlbuf.len < (pptr->conn←con.OPT←length +
			     pptr->conn←con.OPT←offset))) {
				cd->cd←errno = TSYSERR;
				XR←SetErrno(EPROTO);
				return(-1);
			}

			if (call != NULL) {
				if ((rcvbuf.len > (int)call->udata.maxlen) ||
				    (pptr->conn←con.RES←length > call->addr.maxlen) ||
				    (pptr->conn←con.OPT←length > call->opt.maxlen)) {
					cd->cd←errno = TBUFOVFLW;
					return(-1);
				}
				bcopy(ctlbuf.buf + pptr->conn←con.RES←offset,
					call->addr.buf,
					(int)pptr->conn←con.RES←length);
				call->addr.len = pptr->conn←con.RES←length;
				bcopy(ctlbuf.buf + pptr->conn←con.OPT←offset,
					call->opt.buf, 
					(int)pptr->conn←con.OPT←length);
				call->opt.len = pptr->conn←con.OPT←length;
				bcopy(rcvbuf.buf, call->udata.buf,
					(int)rcvbuf.len);
				call->udata.len = rcvbuf.len;
				/*
				 * since a confirmation seq number
				 * is -1 by default
				 */
				call->sequence = -1;
			}

			return(0);

		case T←DISCON←IND:

			/*
			 * if disconnect indication then put it in look buf
			 */
			PutBack(cd, rcvbuf.buf, rcvbuf.len, ctlbuf.buf,
			   ctlbuf.len);
			cd->cd←errno = TLOOK;
			return(-1);

		default:
			break;
	}

	cd->cd←errno = TSYSERR;
	XR←SetErrno(EPROTO);
	return(-1);
}

int
XR←T←Rcv(cd, buf, nbytes, flags)
	register struct conn←data * cd;
	register char *buf;
	unsigned nbytes;
	int *flags;
{
	struct strbuf ctlbuf, rcvbuf;
	int retval, flg = 0;
	int msglen;
	register union T←primitives *pptr;
	
	if (CheckCD(cd)) return(-1);
	
	if (cd->cd←servtype == T←CLTS) {
		cd->cd←errno = TNOTSUPPORT;
		return(-1);
	}
	
	/*
         * Check in lookbuf for stuff
	 */
	if (cd->cd←lookflg) {
		/*
		 * Beware - this is right!
		 *	If something in lookbuf then check
		 *	read queue to see if there is something there.
		 *	If there is something there and there is not a
		 * 	discon in lookbuf, then it must be a discon.
		 *      If so, fall through to get it off of queue.
		 *	I fall through to make sure it is a discon,
		 * 	instead of making check here.
		 *
		 *	If nothing in read queue then just return TLOOK.
		 */
		if ((retval = XR←IOCtl(cd->cd←fd, I←NREAD, &msglen)) < 0) {
			cd->cd←errno = TSYSERR;
			return(-1);
		}
		if (retval) {
			if (*((long *)cd->cd←lookcbuf) == T←DISCON←IND) {
				cd->cd←errno = TLOOK;
				return(-1);
			}
		} else {
			cd->cd←errno = TLOOK;
			return(-1);
		}
	}

	ctlbuf.maxlen = cd->cd←ctlsize;
	ctlbuf.len = 0;
	ctlbuf.buf = cd->cd←ctlbuf;
	rcvbuf.maxlen = nbytes;
	rcvbuf.len = 0;
	rcvbuf.buf = buf;
	*flags = 0;

	/*
	 * data goes right in user buffer
	 */
	if ((retval = XR←GetMsg(cd->cd←fd, &ctlbuf, &rcvbuf, &flg)) < 0) {
		if (XR←GetErrno() == EAGAIN)
			cd->cd←errno = TNODATA;
		else
			cd->cd←errno = TSYSERR;
		return(-1);
	}
	printf("retval returned from XR←GetMsg: %d\n", retval);
	printf("flg returned from XR←GetMsg: %d\n", flg);
	if (rcvbuf.len == -1) rcvbuf.len = 0;


	if (ctlbuf.len > 0) {
		if (ctlbuf.len < sizeof(long)) {
			cd->cd←errno = TSYSERR;
			XR←SetErrno(EPROTO);
			return(-1);
		}

		pptr = (union T←primitives *)ctlbuf.buf;

		switch(pptr->type) {
			case T←EXDATA←IND:
				*flags |= T←EXPEDITED;
				/* flow thru */
			case T←DATA←IND:
				if ((ctlbuf.len < sizeof(struct T←data←ind)) ||
				    (cd->cd←lookflg)) {
					cd->cd←errno = TSYSERR;
					XR←SetErrno(EPROTO);
					return(-1);
				}
	
				if ((pptr->data←ind.MORE←flag) || retval) {
					printf("pptr->data←ind.MORE←flag = %d\n", pptr->data←ind.MORE←flag);
					*flags |= T←MORE;
				}
				if ((pptr->data←ind.MORE←flag) && retval)
					cd->cd←flags |= MORE;
				return(rcvbuf.len);
	
			case T←ORDREL←IND:
				if (cd->cd←lookflg) {
					cd->cd←errno = TSYSERR;
					XR←SetErrno(EPROTO);
					return(-1);
				}
				/* flow thru */

			case T←DISCON←IND:
				PutBack(cd, rcvbuf.buf, rcvbuf.len, ctlbuf.buf,
				    ctlbuf.len);
				if (retval&MOREDATA) {
					ctlbuf.maxlen = 0;
					ctlbuf.len = 0;
					ctlbuf.buf = cd->cd←ctlbuf;
					rcvbuf.maxlen = cd->cd←rcvsize -
					    rcvbuf.len;
					rcvbuf.len = 0;
					rcvbuf.buf =
					    cd->cd←lookdbuf+cd->cd←lookdsize;
					*flags = 0;

					if ((retval = XR←GetMsg(cd->cd←fd,
					    &ctlbuf, &rcvbuf, &flg)) < 0) {
						cd->cd←errno = TSYSERR;
						return(-1);
					}
					if (rcvbuf.len == -1) rcvbuf.len = 0;
					if (retval) {
						cd->cd←errno = TSYSERR;
						XR←SetErrno(EPROTO);
						cd->cd←lookflg = 0;
						return(-1);
					}
					cd->cd←lookdsize += rcvbuf.len;
				}
					
				cd->cd←errno = TLOOK;
				return(-1);
			default:
				break;
		}
	
		cd->cd←errno = TSYSERR;
		XR←SetErrno(EPROTO);
		return(-1);
	} else {
		if (!retval && (cd->cd←flags&MORE)) {
			*flags |= T←MORE;
			cd->cd←flags &= ~MORE;
		}
		if (retval)
			*flags |= T←MORE;
		return(rcvbuf.len);
	}
}

int
XR←T←RcvConnect(cd, call)
	struct conn←data * cd;
	struct t←call *call;
{

	return(ReceiveConnectionConfirmation(cd, call));
}

int
XR←T←RcvDis(cd, discon)
	struct conn←data * cd;
	struct t←discon *discon;
{
	struct strbuf ctlbuf;
	struct strbuf databuf;
	int retval;
	int flg = 0;
	union T←primitives *pptr;
	
	if (CheckCD(cd)) return(-1);
	
	if (cd->cd←servtype == T←CLTS) {
		cd->cd←errno = TNOTSUPPORT;
		return(-1);
	}

	/*
         * is there a discon in look buffer
	 */
	if (cd->cd←lookflg && (*((long *)cd->cd←lookcbuf) == T←DISCON←IND)) {
		ctlbuf.maxlen = cd->cd←lookcsize;
		ctlbuf.len = cd->cd←lookcsize;
		ctlbuf.buf = cd->cd←lookcbuf;
		databuf.maxlen = cd->cd←lookdsize;
		databuf.len = cd->cd←lookdsize;
		databuf.buf = cd->cd←lookdbuf;
	} else {

		if ((retval = XR←T←Look(cd)) < 0) {
			return(-1);
		}
		

		if (retval != T←DISCONNECT) {
			cd->cd←errno = TNODIS;
			return(-1);
		}

		/*
		 * get disconnect off read queue.
		 * use ctl and rcv buffers
		 */
		ctlbuf.maxlen = cd->cd←ctlsize;
		ctlbuf.len = 0;
		ctlbuf.buf = cd->cd←ctlbuf;
		databuf.maxlen = cd->cd←rcvsize;
		databuf.len = 0;
		databuf.buf = cd->cd←rcvbuf;
	
		if ((retval = XR←GetMsg(cd->cd←fd, &ctlbuf, &databuf, &flg))
		    < 0) {
			cd->cd←errno = TSYSERR;
			return(-1);
		}
		if (databuf.len == -1) databuf.len = 0;

		/*
		 * did I get entire message?
		 */
		if (retval) {
			cd->cd←errno = TSYSERR;
			XR←SetErrno(EIO);
			return(-1);
		}
	}

	cd->cd←lookflg = 0;
	
	pptr = (union T←primitives *)ctlbuf.buf;

	if ((ctlbuf.len < sizeof(struct T←discon←ind)) ||
	    (pptr->type != T←DISCON←IND)) {
		cd->cd←errno = TSYSERR;
		XR←SetErrno(EPROTO);
		return(-1);
	}

	if (discon != NULL) {
		if (databuf.len > (int)discon->udata.maxlen) {
			cd->cd←errno = TBUFOVFLW;
			return(-1);
		}
	
		discon->reason = pptr->discon←ind.DISCON←reason;
		bcopy(databuf.buf, discon->udata.buf, (int)databuf.len);
		discon->udata.len = databuf.len;
		discon->sequence = pptr->discon←ind.SEQ←number;
	}

	/*
	 * clear more flag
	 */
	cd->cd←flags &= ~MORE;

	return(0);
}

int
XR←T←RcvRel(cd)
	struct conn←data * cd;
{
	struct strbuf ctlbuf;
	struct strbuf databuf;
	int retval;
	int flg = 0;
	union T←primitives *pptr;
	
	if (CheckCD(cd)) return(-1);
	
	if (cd->cd←servtype != T←COTS←ORD) {
		cd->cd←errno = TNOTSUPPORT;
		return(-1);
	}

	if ((retval = XR←T←Look(cd)) < 0) {
		return(-1);
	}

	if (retval == T←DISCONNECT) {
		cd->cd←errno = TLOOK;
		return(-1);
	}

	if (cd->cd←lookflg && (*((long *)cd->cd←lookcbuf) == T←ORDREL←IND)) {
		cd->cd←lookflg = 0;
		return(0);
	} else {
		if (retval != T←ORDREL) {
			cd->cd←errno = TNOREL;
			return(-1);
		}
	}

	/*
	 * get ordrel off read queue.
	 * use ctl and rcv buffers
	 */
	ctlbuf.maxlen = cd->cd←ctlsize;
	ctlbuf.len = 0;
	ctlbuf.buf = cd->cd←ctlbuf;
	databuf.maxlen = cd->cd←rcvsize;
	databuf.len = 0;
	databuf.buf = cd->cd←rcvbuf;

	if ((retval = XR←GetMsg(cd->cd←fd, &ctlbuf, &databuf, &flg)) < 0) {
		cd->cd←errno = TSYSERR;
		return(-1);
	}

	/*
	 * did I get entire message?
	 */
	if (retval) {
		cd->cd←errno = TSYSERR;
		XR←SetErrno(EIO);
		return(-1);
	}
	pptr = (union T←primitives *)ctlbuf.buf;

	if ((ctlbuf.len < sizeof(struct T←ordrel←ind)) ||
	    (pptr->type != T←ORDREL←IND)) {
		cd->cd←errno = TSYSERR;
		XR←SetErrno(EPROTO);
		return(-1);
	}

	return(0);
}


int
XR←T←Snd(cd, buf, nbytes, flags)
	struct conn←data * cd;
	register char *buf;
	unsigned nbytes;
	int flags;
{
	struct strbuf ctlbuf, databuf;
	struct T←data←req *datareq;
	int flg = 0;
	int tmpcnt, tmp;
	char *tmpbuf;
	
	if (CheckCD(cd)) return(-1);

	if (cd->cd←servtype == T←CLTS) {
		cd->cd←errno = TNOTSUPPORT;
		return(-1);
	}


	datareq = (struct T←data←req *)cd->cd←ctlbuf;
	if (flags & T←EXPEDITED) {
		datareq->PRIM←type = T←EXDATA←REQ;
	} else
		datareq->PRIM←type = T←DATA←REQ;


	ctlbuf.maxlen = sizeof(struct T←data←req);
	ctlbuf.len = sizeof(struct T←data←req);
	ctlbuf.buf = cd->cd←ctlbuf;
	tmp = nbytes;
	tmpbuf = buf;

	while (tmp) {
		if ((tmpcnt = tmp) > cd->cd←maxpsz) {
			datareq->MORE←flag = 1;
			tmpcnt = cd->cd←maxpsz;
		} else {
			if (flags & T←MORE)
				datareq->MORE←flag = 1;
			else
				datareq->MORE←flag = 0;
		}

		databuf.maxlen = tmpcnt;
		databuf.len = tmpcnt;
		databuf.buf = tmpbuf;
	
		if (XR←PutMsg(cd->cd←fd, &ctlbuf, &databuf, flg) < 0) {
			if (nbytes == tmp) {
				if (XR←GetErrno() == EAGAIN)
					cd->cd←errno = TFLOW;
				else
					cd->cd←errno = TSYSERR;
				return(-1);
			} else
				return(nbytes - tmp);
		}
		tmp = tmp - tmpcnt;
		tmpbuf = tmpbuf + tmpcnt;
	}
	return(nbytes - tmp);
}

int
XR←T←SndDis(cd, call)
	register struct conn←data * cd;
	struct t←call *call;
{
	struct T←discon←req dreq;
	struct strbuf ctlbuf;
	struct strbuf databuf;
	
	if (CheckCD(cd)) return(-1);
	
	if (cd->cd←servtype == T←CLTS) {
		cd->cd←errno = TNOTSUPPORT;
		return(-1);
	}
	
	/*
         * look at look buffer to see if there is a discon there
	 */

	if (XR←T←Look(cd) == T←DISCONNECT) {
		cd->cd←errno = TLOOK;
		return(-1);
	}

	cd->cd←lookflg = 0;

	if (XR←IOCtl(cd->cd←fd, I←FLUSH, FLUSHW) < 0) {
		cd->cd←errno = TSYSERR;
		return(-1);
	}


	dreq.PRIM←type = T←DISCON←REQ;
	dreq.SEQ←number = (call? call->sequence: -1);


	ctlbuf.maxlen = sizeof(struct T←discon←req);
	ctlbuf.len = sizeof(struct T←discon←req);
	ctlbuf.buf = (caddr←t)&dreq;

	databuf.maxlen = (call? call->udata.len: 0);
	databuf.len = (call? call->udata.len: 0);
	databuf.buf = (call? call->udata.buf: NULL);

	if (XR←PutMsg(cd->cd←fd, &ctlbuf, (databuf.len? &databuf: NULL), 0) < 0)
	    {
		cd->cd←errno = TSYSERR;
		return(-1);
	}

	if (!IsOK(cd, (long)T←DISCON←REQ)) {
		return(-1);
	}

	cd->cd←flags &= ~MORE;
	return(0);
}

int
XR←T←SndRel(cd)
	register struct conn←data * cd;
{
	struct T←ordrel←req orreq;
	struct strbuf ctlbuf;

	if (CheckCD(cd)) return(-1);
	
	if (cd->cd←servtype != T←COTS←ORD) {
		cd->cd←errno = TNOTSUPPORT;
		return(-1);
	}

	orreq.PRIM←type = T←ORDREL←REQ;
	ctlbuf.maxlen = sizeof(struct T←ordrel←req);
	ctlbuf.len = sizeof(struct T←ordrel←req);
	ctlbuf.buf = (caddr←t)&orreq;

	if (XR←PutMsg(cd->cd←fd, &ctlbuf, NULL, 0) < 0) {
		if (XR←GetErrno() == EAGAIN)
			cd->cd←errno = TFLOW;
		else
			cd->cd←errno = TSYSERR;
		return(-1);
	}

	return(0);
}

/* Datagram things */

int
XR←T←RcvUData(cd, unitdata, flags)
	struct conn←data * cd;
	register struct t←unitdata *unitdata;
	int *flags;
{
	struct strbuf ctlbuf;
	int retval, flg = 0;
	register union T←primitives *pptr;

	if (CheckCD(cd)) return(-1); /* because of cd->cd←errno */
	if (cd->cd←servtype != T←CLTS) {
		cd->cd←errno = TNOTSUPPORT;
		return(-1);
	}

	/*
         * check if there is something in look buffer
	 */
	if (cd->cd←lookflg) {
		cd->cd←errno = TLOOK;
		return(-1);
	}

	ctlbuf.maxlen = cd->cd←ctlsize;
	ctlbuf.len = 0;
	ctlbuf.buf = cd->cd←ctlbuf;
	*flags = 0;

	/*
	 * data goes right in user buffer
	 */
	if ((retval = XR←GetMsg(cd->cd←fd, &ctlbuf,
	    &unitdata->udata, &flg)) < 0) {
		if (XR←GetErrno() == EAGAIN)
			cd->cd←errno = TNODATA;
		else
			cd->cd←errno = TSYSERR;
		return(-1);
	}
	if (unitdata->udata.len == -1) unitdata->udata.len = 0;

	/*
	 * is there control piece with data?
	 */
	if (ctlbuf.len > 0) {
		if (ctlbuf.len < sizeof(long)) {
			cd->cd←errno = TSYSERR;
			XR←SetErrno(EPROTO);
			unitdata->udata.len = 0;
			return(-1);
		}
	
		pptr = (union T←primitives *)ctlbuf.buf;
	
		switch(pptr->type) {
			case T←UNITDATA←IND:
				if ((ctlbuf.len < sizeof(struct T←unitdata←ind)) ||
				    (pptr->unitdata←ind.OPT←length &&
				     (ctlbuf.len < (pptr->unitdata←ind.OPT←length
				     + pptr->unitdata←ind.OPT←offset)))) {
					cd->cd←errno = TSYSERR;
					XR←SetErrno(EPROTO);
					unitdata->udata.len = 0;
					return(-1);
				}
				if ((pptr->unitdata←ind.SRC←length > (int)unitdata->addr.maxlen) ||
				    (pptr->unitdata←ind.OPT←length > (int)unitdata->opt.maxlen)) {
					cd->cd←errno = TBUFOVFLW;
					unitdata->udata.len = 0;
					return(-1);
				}
	
				if (retval)
					*flags |= T←MORE;
	
				bcopy(ctlbuf.buf +
					pptr->unitdata←ind.SRC←offset,
					unitdata->addr.buf,
					(int)pptr->unitdata←ind.SRC←length);
				unitdata->addr.len = pptr->unitdata←ind.SRC←length;
				bcopy(ctlbuf.buf +
					pptr->unitdata←ind.OPT←offset,
					unitdata->opt.buf,
					(int)pptr->unitdata←ind.OPT←length);
				unitdata->opt.len = pptr->unitdata←ind.OPT←length;
	
				return(0);
			case T←UDERROR←IND:
				PutBack(cd, unitdata->udata.buf, 0, ctlbuf.buf, ctlbuf.len);
				unitdata->udata.len = 0;
				cd->cd←errno = TLOOK;
				return(-1);
			default:
				break;
		}
		
		cd->cd←errno = TSYSERR;
		XR←SetErrno(EPROTO);
		return(-1);
	} else { 
		unitdata->addr.len = 0;
		unitdata->opt.len = 0;
		/*
		 * only data in message no control piece
		 */
		if (retval)
			*flags = T←MORE;
		return(0);
	}
}

int
XR←T←RcvUDErr(cd, uderr)
	struct conn←data * cd;
	struct t←uderr *uderr;
{
	struct strbuf ctlbuf, rcvbuf;
	int flg;
	int retval;
	register union T←primitives *pptr;

	if (CheckCD(cd)) return(-1);

	if (cd->cd←servtype != T←CLTS) {
		cd->cd←errno = TNOTSUPPORT;
		return(-1);
	}
	/* 
         * is there an error indication in look buffer
	 */
	if (cd->cd←lookflg) {
		ctlbuf.maxlen = cd->cd←lookcsize;
		ctlbuf.len = cd->cd←lookcsize;
		ctlbuf.buf = cd->cd←lookcbuf;
		rcvbuf.maxlen = 0;
		rcvbuf.len = 0;
		rcvbuf.buf = NULL;
	} else {
		if ((retval = XR←T←Look(cd)) < 0)
			return(-1);
		if (retval != T←UDERR) {
			cd->cd←errno = TNOUDERR;
			return(-1);
		}
	
		ctlbuf.maxlen = cd->cd←ctlsize;
		ctlbuf.len = 0;
		ctlbuf.buf = cd->cd←ctlbuf;
		rcvbuf.maxlen = 0;
		rcvbuf.len = 0;
		rcvbuf.buf = NULL;

		if ((retval = XR←GetMsg(cd->cd←fd, &ctlbuf, &rcvbuf,
		    &flg)) < 0) {
			cd->cd←errno = TSYSERR;
			return(-1);
		}
		/*
		 * did I get entire message?
		 */
		if (retval) {
			cd->cd←errno = TSYSERR;
			XR←SetErrno(EIO);
			return(-1);
		}

	}

	cd->cd←lookflg = 0;

	pptr = (union T←primitives *)ctlbuf.buf;

	if ((ctlbuf.len < sizeof(struct T←uderror←ind)) ||
	    (pptr->type != T←UDERROR←IND)) {
		cd->cd←errno = TSYSERR;
		XR←SetErrno(EPROTO);
		return(-1);
	}

	if (uderr) {
		if ((uderr->addr.maxlen < pptr->uderror←ind.DEST←length) ||
		    (uderr->opt.maxlen < pptr->uderror←ind.OPT←length)) {
			cd->cd←errno = TBUFOVFLW;
			return(-1);
		}
	
		uderr->error = pptr->uderror←ind.ERROR←type;
		bcopy(ctlbuf.buf + pptr->uderror←ind.DEST←offset,
			uderr->addr.buf,
			(int)pptr->uderror←ind.DEST←length);
		bcopy(ctlbuf.buf + pptr->uderror←ind.OPT←offset,
			uderr->opt.buf,
			(int)pptr->uderror←ind.OPT←length);
	}
	return(0);
}

int
XR←T←SndUData(cd, unitdata)
	struct conn←data * cd;
	register struct t←unitdata *unitdata;
{
	register struct T←unitdata←req *udreq;
	char *buf;
	struct strbuf ctlbuf;
	int size;

	if (CheckCD(cd)) return(-1);

	if (cd->cd←servtype != T←CLTS) {
		cd->cd←errno = TNOTSUPPORT;
		return(-1);
	}

	if ((int)unitdata->udata.len == 0)
		return(0);

	if ((int)unitdata->udata.len > cd->cd←maxpsz) {
		cd->cd←errno = TSYSERR;
		XR←SetErrno(ERANGE);
		return(-1);
	}

	buf = cd->cd←ctlbuf;
	udreq = (struct T←unitdata←req *)buf;
	udreq->PRIM←type = T←UNITDATA←REQ;
	udreq->DEST←length = unitdata->addr.len;
	udreq->DEST←offset = 0;
	udreq->OPT←length = unitdata->opt.len;
	udreq->OPT←offset = 0;
	size = sizeof(struct T←unitdata←req);

	if (unitdata->addr.len) {
		AlignedCopy(buf, unitdata->addr.len, size,
			     unitdata->addr.buf, &udreq->DEST←offset);
		size = udreq->DEST←offset + udreq->DEST←length;
	}
	if (unitdata->opt.len) {
		AlignedCopy(buf, unitdata->opt.len, size,
			     unitdata->opt.buf, &udreq->OPT←offset);
		size = udreq->OPT←offset + udreq->OPT←length;
	}

	if (size > cd->cd←ctlsize) {
		cd->cd←errno = TSYSERR;
		XR←SetErrno(EIO);
		return(-1);
	}
	ctlbuf.maxlen = cd->cd←ctlsize;
	ctlbuf.len = size;
	ctlbuf.buf = buf;

	if (XR←PutMsg(cd->cd←fd, &ctlbuf,
	    (unitdata->udata.len? &unitdata->udata: NULL), 0) < 0) {
		if (XR←GetErrno() == EAGAIN)
			cd->cd←errno = TFLOW;
		else
			cd->cd←errno = TSYSERR;
		return(-1);
	}
	return(0);
}



/* Allocation things */

char *
XR←T←Alloc(cd, struct←type, fields)
	struct conn←data * cd;
	int struct←type;
	int fields;
{
	struct strioctl strioc;
	struct T←info←ack info;
	union structptrs {
		char	*caddr;
		struct t←bind *bind;
		struct t←call *call;
		struct t←discon *dis;
		struct t←optmgmt *opt;
		struct t←unitdata *udata;
		struct t←uderr *uderr;
		struct t←info *info;
	} p;
	unsigned dsize;
	
	if (CheckCD(cd)) return(NULL);
	
	/*
	 * Get size info for T←ADDR, T←OPT, and T←UDATA fields
	 */
	info.PRIM←type = T←INFO←REQ;
	strioc.ic←cmd = TI←GETINFO;
	strioc.ic←timout = -1;
	strioc.ic←len = sizeof(struct T←info←req);
	strioc.ic←dp = (char *)&info;
	if (XR←IOCtl(cd->cd←fd, I←STR, &strioc) < 0) {
		cd->cd←errno = TSYSERR;
		return(NULL);
	}
	if (strioc.ic←len != sizeof(struct T←info←ack)) {
		XR←SetErrno(EIO);
		cd->cd←errno = TSYSERR;
		return(NULL);
	}
	
	/*
	 * Malloc appropriate structure and the specified
	 * fields within each structure.  Initialize the
	 * 'buf' and 'maxlen' fields of each.
	 */
	switch (struct←type) {

	case T←BIND:
		if ((p.bind = (struct t←bind *)
			GC←malloc((unsigned)sizeof(struct t←bind))) == NULL)
				goto out;
		if (fields & T←ADDR) {
			if (AllocBuf(&p.bind->addr, info.ADDR←size) < 0)
				goto out;
		}
		return((char *)p.bind);

	case T←CALL:
		if ((p.call = (struct t←call *)
			GC←malloc((unsigned)sizeof(struct t←call))) == NULL)
				goto out;
		if (fields & T←ADDR) {
			if (AllocBuf(&p.call->addr, info.ADDR←size) < 0)
				goto out;
		}
		if (fields & T←OPT) {
			if (AllocBuf(&p.call->opt, info.OPT←size) < 0)
				goto out;
		}
		if (fields & T←UDATA) {
			dsize = Max(info.CDATA←size, info.DDATA←size);
			if (AllocBuf(&p.call->udata, dsize) < 0)
				goto out;
		}
		return((char *)p.call);

	case T←OPTMGMT:
		if ((p.opt = (struct t←optmgmt *)
			GC←malloc((unsigned)sizeof(struct t←optmgmt))) == NULL)
				goto out;
		if (fields & T←OPT){
			if (AllocBuf(&p.opt->opt, info.OPT←size) < 0)
				goto out;
		}
		return((char *)p.opt);

	case T←DIS:
		if ((p.dis = (struct t←discon *)
			GC←malloc((unsigned)sizeof(struct t←discon))) == NULL)
				goto out;
		if (fields & T←UDATA){
			if (AllocBuf(&p.dis->udata, info.DDATA←size) < 0)
				goto out;
		}
		return((char *)p.dis);

	case T←UNITDATA:
		if ((p.udata = (struct t←unitdata *)
			GC←malloc((unsigned)sizeof(struct t←unitdata))) == NULL)
				goto out;
		if (fields & T←ADDR){
			if (AllocBuf(&p.udata->addr, info.ADDR←size) < 0)
				goto out;
		}
		if (fields & T←OPT){
			if (AllocBuf(&p.udata->opt, info.OPT←size) < 0)
				goto out;
		}
		if (fields & T←UDATA){
			if (AllocBuf(&p.udata->udata, info.TSDU←size) < 0)
				goto out;
		}
		return((char *)p.udata);

	case T←UDERROR:
		if ((p.uderr = (struct t←uderr *)
			GC←malloc((unsigned)sizeof(struct t←uderr))) == NULL)
				goto out;
		if (fields & T←ADDR){
			if (AllocBuf(&p.uderr->addr, info.ADDR←size) < 0)
				goto out;
		}
		if (fields & T←OPT){
			if (AllocBuf(&p.uderr->opt, info.OPT←size) < 0)
				goto out;
		}
		return((char *)p.uderr);

	case T←INFO:
		if ((p.info = (struct t←info *)
			GC←malloc((unsigned)sizeof(struct t←info))) == NULL)
				goto out;
		return((char *)p.info);

	default:
		XR←SetErrno(EINVAL);
		cd->cd←errno = TSYSERR;
		return(NULL);
	}

	/*
	 * Clean up. Set errno to ENOMEM if
	 * memory could not be allocated.
	 */
out:
	if (p.caddr)
		XR←T←Free(cd, p.caddr, struct←type);

	cd->cd←errno = TSYSERR;
	XR←SetErrno(ENOMEM);
	return(NULL);
};

int
XR←T←Free(cd, ptr, struct←type)
	struct conn←data * cd;
	char *ptr;
	int struct←type;
{
	union structptrs {
		struct t←bind *bind;
		struct t←call *call;
		struct t←discon *dis;
		struct t←optmgmt *opt;
		struct t←unitdata *udata;
		struct t←uderr *uderr;
	} p;
	
	if (CheckCD(cd)) return(-1); /* because of cd->cd←errno */
	/*
	 * Free all the buffers associated with the appropriate
	 * fields of each structure.
	 */

	switch (struct←type) {

	case T←BIND:
		p.bind = (struct t←bind *)ptr;
		if (p.bind->addr.buf != NULL)
			free(p.bind->addr.buf);
		break;

	case T←CALL:
		p.call = (struct t←call *)ptr;
		if (p.call->addr.buf != NULL)
			free(p.call->addr.buf);
		if (p.call->opt.buf != NULL)
			free(p.call->opt.buf);
		if (p.call->udata.buf != NULL)
			free(p.call->udata.buf);
		break;

	case T←OPTMGMT:
		p.opt = (struct t←optmgmt *)ptr;
		if (p.opt->opt.buf != NULL)
			free(p.opt->opt.buf);
		break;

	case T←DIS:
		p.dis = (struct t←discon *)ptr;
		if (p.dis->udata.buf != NULL)
			free(p.dis->udata.buf);
		break;

	case T←UNITDATA:
		p.udata = (struct t←unitdata *)ptr;
		if (p.udata->addr.buf != NULL)
			free(p.udata->addr.buf);
		if (p.udata->opt.buf != NULL)
			free(p.udata->opt.buf);
		if (p.udata->udata.buf != NULL)
			free(p.udata->udata.buf);
		break;

	case T←UDERROR:
		p.uderr = (struct t←uderr *)ptr;
		if (p.uderr->addr.buf != NULL)
			free(p.uderr->addr.buf);
		if (p.uderr->opt.buf != NULL)
			free(p.uderr->opt.buf);
		break;

	case T←INFO:
		break;

	default:
		XR←SetErrno(EINVAL);
		cd->cd←errno = TSYSERR;
		return(-1);
	}
	
	free(ptr);
	return(0);
}


/* Other useful stuff  */

/* 
 * put data and control info in look buffer
 * 
 * The only thing that can be in look buffer is a T←discon←ind,
 * T←ordrel←ind or a T←uderr←ind.
 */
int
PutBack(cd, dptr, dsize, cptr, csize)
	struct conn←data * cd;
	caddr←t dptr;
	int dsize;
	caddr←t cptr;
	int csize;
{
	if (CheckCD(cd)) return(-1);
	
	bcopy(dptr, cd->cd←lookdbuf, dsize);
	bcopy(cptr, cd->cd←lookcbuf, csize);
	cd->cd←lookdsize = dsize;
	cd->cd←lookcsize = csize;
	cd->cd←lookflg++;

}


int
AllocBuf(buf, n)
	struct netbuf *buf;
{
	switch(n)
	{
		case -1:
			if ((buf->buf = (char *) GC←malloc(1024)) == NULL)
				return(-1);
			else buf->maxlen = 1024;
			break;

		case 0:
		case -2:
			buf->buf = NULL;
			buf->maxlen = 0;
			break;

		default:
			if ((buf->buf = (char *) GC←malloc(n)) == NULL)
				return(-1);
			else buf->maxlen = n;
			break;
	}
	return(0);
}


/* 
 * copy data to output buffer and align it as in input buffer
 * This is to ensure that if the user wants to align a network
 * addr on a non-word boundry then it will happen.
 */
 int
AlignedCopy(buf, len, init←offset, datap, rtn←offset)
	char *buf;
	int len;
	int init←offset;
	char *datap;
	long *rtn←offset;
{
		*rtn←offset = ROUNDUP(init←offset) + ((unsigned)datap&0x03);
		bcopy(datap, (char *)(buf + *rtn←offset), (int)len);
}

/*
*	CheckCD  checks to see that the contents of the cd data
*		structure is good
*/
int
CheckCD(cd)
	struct conn←data * cd;
{
	if (cd == NULL) return(1);
	if ((cd->cd←fd < 0) || !(cd->cd←flags & USED)) {
		cd->cd←errno = TBADF;
		return(1);
	};
	return(0);
};

/*
 * Max - return max between two ints
 */
int
Max(x, y)
	int x;
	int y;
{
	if (x > y)
		return(x);
	else 
		return(y);
};

/*
 * Is there something that needs attention?
 */

int
IsEvent(cd)
	struct conn←data * cd;
 {
	int size, retval;

	if ((retval = XR←IOCtl(cd->cd←fd, I←NREAD, &size)) < 0) {
		cd->cd←errno = TSYSERR;
		return(1);
	}

	if (retval || cd->cd←lookflg) {
		cd->cd←errno = TLOOK;
		return(1);
	}

	return(0);
}

/* 
 * wait for T←OK←ACK
 */
int
IsOK(cd, type)
	register struct conn←data * cd;
	long type;
{

	struct strbuf ctlbuf;
	struct strbuf rcvbuf;
	register union T←primitives *pptr;
	int flags, retval, cntlflag;
	int size;
	int fd = cd->cd←fd;
	
	cntlflag = XR←FCntl(fd, F←GETFL, 0);
	XR←FCntl(fd, F←SETFL, XR←FCntl(fd, F←GETFL, 0) & ~O←NDELAY);

	ctlbuf.len = 0;
	ctlbuf.buf = cd->cd←ctlbuf;
	ctlbuf.maxlen = cd->cd←ctlsize;
	rcvbuf.maxlen = cd->cd←rcvsize;
	rcvbuf.len = 0;
	rcvbuf.buf = cd->cd←rcvbuf;
	flags = RS←HIPRI;

	while ((retval = XR←GetMsg(fd, &ctlbuf, &rcvbuf, &flags)) < 0) { 
		if (XR←GetErrno() == EINTR)
			continue;
		cd->cd←errno = TSYSERR;
		return(0);
	}

	/* did I get entire message */
	if (retval) {
		cd->cd←errno = TSYSERR;
		XR←SetErrno(EIO);
		return(0);
	}

	/* 
	 * is ctl part large enough to determine type?
	 */
	if (ctlbuf.len < sizeof(long)) {
		cd->cd←errno = TSYSERR;
		XR←SetErrno(EPROTO);
		return(0);
	}

	XR←FCntl(cd->cd←fd, F←SETFL, cntlflag);

	pptr = (union T←primitives *)ctlbuf.buf;

	switch((int)pptr->type) {
		case T←OK←ACK:
			if ((ctlbuf.len < sizeof(struct T←ok←ack)) ||
			    (pptr->ok←ack.CORRECT←prim != type)) {
				cd->cd←errno = TSYSERR;
				XR←SetErrno(EPROTO);
				return(0);
			}
			return(1);

		case T←ERROR←ACK:
			if ((ctlbuf.len < sizeof(struct T←error←ack)) ||
			    (pptr->error←ack.ERROR←prim != type)) {
				cd->cd←errno = TSYSERR;
				XR←SetErrno(EPROTO);
				return(0);
			}
			/*
			 * if error is out of state and there is something
			 * on read queue, then indicate to user that
			 * there is something that needs attention
			 */
			if (pptr->error←ack.TLI←error == TOUTSTATE) {
				if ((retval = XR←IOCtl(fd, I←NREAD, &size)) < 0)
				    {
					cd->cd←errno = TSYSERR;
					return(0);
				}
				if (retval)
					cd->cd←errno = TLOOK;
				else
					cd->cd←errno = TOUTSTATE;
			} else {
				cd->cd←errno = pptr->error←ack.TLI←error;
				if (cd->cd←errno == TSYSERR)
				    XR←SetErrno(pptr->error←ack.UNIX←error);
			}
			return(0);

		default:
			cd->cd←errno = TSYSERR;
			XR←SetErrno(EPROTO);
			return(0);
	}
}

/*
 * timod XR←IOCtl
 */
int
DoTIModIOCtl(cd, buf, size, cmd, retlen)
	struct conn←data * cd;
	char *buf;
	int size;
	int cmd;
	int *retlen;
{
	int fd = cd->cd←fd;
	int retval;
	struct strioctl strioc;

	strioc.ic←cmd = cmd;
	strioc.ic←timout = -1;
	strioc.ic←len = size;
	strioc.ic←dp = buf;

	if ((retval = XR←IOCtl(fd, I←STR, &strioc)) < 0) {
		cd->cd←errno = TSYSERR;
		return(0);
	}

	if (retval) {
		cd->cd←errno = retval&0xff;
		if (cd->cd←errno == TSYSERR)
			XR←SetErrno((retval >>  8)&0xff);
		return(0);
	}
	if (retlen){
		*retlen = strioc.ic←len;
	}
	return(1);
}

/*
 * alloc scratch buffers and look buffers
 */

AllocateBuffers(cd, info)
	struct conn←data * cd;
	struct T←info←ack info;
{
	unsigned size1, size2;
	unsigned csize, dsize, asize, osize;
	char *ctlbuf, *rcvbuf;
	char *lookdbuf, *lookcbuf;

	csize = SetSize(info.CDATA←size);
	dsize = SetSize(info.DDATA←size);

	size1 = Max(csize, dsize);

	if ((rcvbuf = (char *) GC←malloc(size1)) == NULL) {
		return(-1);
	}

	if ((lookdbuf = (char *) GC←malloc(size1)) == NULL) {
		(void)free(rcvbuf);
		return(-1);
	}

	asize = SetSize(info.ADDR←size);
	osize = SetSize(info.OPT←size);

	size2 = sizeof(union T←primitives) + asize + sizeof(long) +
		osize + sizeof(long);

	if ((ctlbuf = (char *) GC←malloc(size2)) == NULL) {
		(void)free(rcvbuf);
		(void)free(lookdbuf);
		return(-1);
	}

	if ((lookcbuf = (char *) GC←malloc(size2)) == NULL) {
		(void)free(rcvbuf);
		(void)free(lookdbuf);
		(void)free(ctlbuf);
		return(-1);
	}


	cd->cd←rcvsize = size1;
	cd->cd←rcvbuf = rcvbuf;
	cd->cd←ctlsize = size2;
	cd->cd←ctlbuf = ctlbuf;
	cd->cd←lookcbuf = lookcbuf;
	cd->cd←lookdbuf = lookdbuf;
	cd->cd←lookcsize = 0;
	cd->cd←lookdsize = 0;
	cd->cd←lookflg = 0;
	cd->cd←flags = USED;
	cd->cd←maxpsz = info.TIDU←size;
	cd->cd←servtype = info.SERV←type;
	return(0);
}

/*
 * set sizes of buffers
 */

SetSize(infosize)
long infosize;
{
	switch((int)infosize)
	{
		case -1: return(DEFSIZE);
		case -2: return(0);
		default: return(infosize);
	}
}