DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T t

⟦410406eab⟧ TextFile

    Length: 22470 (0x57c6)
    Types: TextFile
    Names: »tp0ts.c«

Derivation

└─⟦2d1937cfd⟧ Bits:30007241 EUUGD22: P.P 5.0
    └─⟦e83f91978⟧ »EurOpenD22/isode/osimis-2.0.tar.Z« 
        └─⟦d846658bd⟧ 
            └─⟦this⟧ »osimis/misode/tsap/tp0ts.c« 
└─⟦2d1937cfd⟧ Bits:30007241 EUUGD22: P.P 5.0
    └─⟦35176feda⟧ »EurOpenD22/isode/isode-6.tar.Z« 
        └─⟦de7628f85⟧ 
            └─⟦this⟧ »isode-6.0/tsap/tp0ts.c« 

TextFile

/* tp0ts.c - TPM: TP0 engine */

#ifndef	lint
static char *rcsid = "$Header: /f/osi/tsap/RCS/tp0ts.c,v 7.4 89/12/19 10:18:22 mrose Exp $";
#endif

/* 
 * $Header: /f/osi/tsap/RCS/tp0ts.c,v 7.4 89/12/19 10:18:22 mrose Exp $
 *
 *
 * $Log:	tp0ts.c,v $
 * Revision 7.4  89/12/19  10:18:22  mrose
 * DLOG
 * 
 * Revision 7.3  89/12/08  09:41:23  mrose
 * touch-up
 * 
 * Revision 7.2  89/12/07  22:15:39  mrose
 * touch-up
 * 
 * Revision 7.1  89/12/07  01:07:30  mrose
 * queued writes
 * 
 * Revision 7.0  89/11/23  22:30:33  mrose
 * Release 6.0
 * 
 */

/*
 *				  NOTICE
 *
 *    Acquisition, use, and distribution of this module and related
 *    materials are subject to the restrictions of a license agreement.
 *    Consult the Preface in the User's Manual for the full terms of
 *    this agreement.
 *
 */


/* LINTLIBRARY */

#include <stdio.h>
#include "tpkt.h"
#include "mpkt.h"
#include "tailor.h"


#if	defined(TCP) || defined(X25)

/* \f

 */

static int  TConnect (tb, expedited, data, cc, td)
register struct tsapblk *tb;
char    *data;
int	expedited,
	cc;
struct TSAPdisconnect *td;
{
    register struct tsapkt *t;

    if (!(tb -> tb_flags & TB_TCP)) {
	expedited = 0;
	if (cc > 0)
	    return tsaplose (td, DR_PARAMETER, NULLCP,
		    "initial user data not allowed with class 0");
    }

    tb -> tb_srcref = htons ((u_short) (getpid () & 0xffff));
    tb -> tb_dstref = htons ((u_short) 0);

    if ((t = newtpkt (TPDU_CR)) == NULL)
	return tsaplose (td, DR_CONGEST, NULLCP, "out of memory");

    t -> t_cr.cr_dstref = tb -> tb_dstref;
    t -> t_cr.cr_srcref = tb -> tb_srcref;
    t -> t_cr.cr_class = CR_CLASS_TP0;
    if (!(tb -> tb_flags & TB_TCP)) {
	register int    i,
	                j;
	int     k;

	i = k = tb -> tb_tsdusize + tb -> tb_tpduslop;
	for (j = 0; i > 0; j++)
	    i >>= 1;
	if (k == (1 << (j - 1)))
	    j--;
	if (j > SIZE_MAXTP0) {
	    j = SIZE_MAXTP0;
	    tb -> tb_tsdusize = (1 << j) - tb -> tb_tpduslop;
	}
	if (j != SIZE_DFLT)
	    t -> t_tpdusize = j;
    }
    bcopy (tb -> tb_initiating.ta_selector, t -> t_calling,
		t -> t_callinglen = tb -> tb_initiating.ta_selectlen);

    bcopy (tb -> tb_responding.ta_selector, t -> t_called,
		t -> t_calledlen = tb -> tb_responding.ta_selectlen);
    if (expedited) {
	tb -> tb_flags |= TB_EXPD;
	t -> t_options |= OPT_TEXPEDITE;
    }

    copyTPKTdata (t, data, cc);	/* XXX: user musn't touch! */

    tb -> tb_retry = t;

    return OK;
}

/* \f

 */

static int  TRetry (tb, async, tc, td)
register struct tsapblk *tb;
int	async;
struct TSAPconnect *tc;
struct TSAPdisconnect *td;
{
    int	    len;
    register struct tsapkt *t;

    if (t = tb -> tb_retry) {
	tb -> tb_retry = NULL;

	if (async)
	    switch ((*tb -> tb_retryfnx) (tb, td)) {
		case NOTOK:
	    	    goto out;

	        case OK:
		    tb -> tb_retry = t;
		    return CONNECTING_1;

		case DONE:
		    break;		    
	    }

	if (tpkt2fd (tb, t, tb -> tb_writefnx) == NOTOK) {
	    (void) tsaplose (td, t -> t_errno, NULLCP, NULLCP);
	    goto out;
	}

	freetpkt (t), t = NULL;
    }

    if (async) {
	fd_set	mask;

	FD_ZERO (&mask);
	FD_SET (tb -> tb_fd, &mask);

	if (xselect (tb -> tb_fd + 1, &mask, NULLFD, NULLFD, 0) == OK)
	    return CONNECTING_2;
    }

    if ((t = fd2tpkt (tb -> tb_fd, tb -> tb_initfnx, tb -> tb_readfnx)) == NULL
	    || t -> t_errno != OK) {
	(void) tsaplose (td, t ? t -> t_errno : DR_CONGEST, NULLCP, NULLCP);
	goto out;
    }

    switch (TPDU_CODE (t)) {
	case TPDU_CC: 
	    tc -> tc_sd = tb -> tb_fd;
	    if (CR_CLASS (t) != CR_CLASS_TP0) {
		(void) tpktlose (tb, td, DR_PROTOCOL, NULLCP,
			"proposed class 0, got back 0x%x", CR_CLASS (t));
		goto out;
	    }
	    if (tb -> tb_srcref != t -> t_cc.cc_dstref) {
		(void) tpktlose (tb, td, DR_MISMATCH, NULLCP,
			"sent srcref of 0x%x, got 0x%x",
			ntohs (tb -> tb_srcref), ntohs (t -> t_cc.cc_dstref));
		goto out;
	    }
	    tb -> tb_dstref = t -> t_cc.cc_srcref;
	    if (!(tb -> tb_flags & TB_TCP)) {
		if (t -> t_tpdusize == 0)
		    t -> t_tpdusize = SIZE_DFLT;
		else
		    if (t -> t_tpdusize > SIZE_MAXTP0)
			t -> t_tpdusize = SIZE_MAXTP0;
		tb -> tb_tsdusize = (1 << t -> t_tpdusize) - tb -> tb_tpduslop;
	    }
	    if ((len = t -> t_calledlen) > 0) {
		if (len > sizeof tb -> tb_responding.ta_selector)
		    len = sizeof tb -> tb_responding.ta_selector;
		bcopy (t -> t_called, tb -> tb_responding.ta_selector,
			tb -> tb_responding.ta_selectlen = len);
	    }
	    tc -> tc_responding = tb -> tb_responding;	/* struct copy */
	    if (!(t -> t_options & OPT_TEXPEDITE)
		    || !(tb -> tb_flags & TB_TCP))
		tb -> tb_flags &= ~TB_EXPD;
	    tc -> tc_expedited = (tb -> tb_flags & TB_EXPD) ? 1 : 0;
	    tc -> tc_tsdusize = tb -> tb_tsdusize;
	    tc -> tc_qos = tb -> tb_qos;	/* struct copy */
	    if (t -> t_qbuf) {
		copyTSAPdata (t -> t_qbuf -> qb_data, t -> t_qbuf -> qb_len,
			tc);
	    }
	    else
		tc -> tc_cc = 0;

	    freetpkt (t);
	    tb -> tb_flags |= TB_CONN;
#ifdef  MGMT
	    if (tb -> tb_manfnx)
		(*tb -> tb_manfnx) (OPREQOUT, tb);
#endif

	    return DONE;

	case TPDU_DR: 
	    td -> td_reason = t -> t_dr.dr_reason;
	    if (t -> t_qbuf) {
		copyTSAPdata (t -> t_qbuf -> qb_data, t -> t_qbuf -> qb_len,
			td);
	    }
	    else
		td -> td_cc = 0;
	    goto out;

	case TPDU_ER:
	    switch (t -> t_er.er_reject) {
		case ER_REJ_NOTSPECIFIED:
		default:
		    td -> td_reason = DR_CONNECT;
		    break;

		case ER_REJ_CODE:
		case ER_REJ_TPDU:
		case ER_REJ_VALUE:
		    td -> td_reason = DR_PROTOCOL;
		    break;
	    }
	    td -> td_cc = 0;
	    goto out;
	    
	default: 
	    (void) tpktlose (tb, td, DR_PROTOCOL, NULLCP,
		    "transport protocol mangled: expecting 0x%x, got 0x%x",
		    TPDU_CC, TPDU_CODE (t));
	    goto out;
    }

out: ;
    freetpkt (t);
/*    freetblk (tb); */

    return NOTOK;
}

/* \f

 */

static int  TStart (tb, cp, ts, td)
register struct tsapblk *tb;
char   *cp;
struct TSAPstart *ts;
struct TSAPdisconnect *td;
{
    int	    len,
	    result;
    register struct tsapkt *t;

    if ((t = str2tpkt (cp)) == NULL || t -> t_errno != OK) {
	result = tsaplose (td, DR_PARAMETER, NULLCP,
		    "bad initialization vector");
	goto out;
    }

    if (CR_CLASS (t) != CR_CLASS_TP0) {
	if (t -> t_cr.cr_alternate & (ALT_TP0 | ALT_TP1))
	    t -> t_cr.cr_class = CR_CLASS_TP0;
	else {
	    result = tpktlose (tb, td, DR_CONNECT, NULLCP,
			"only class 0 supported, not 0x%x", CR_CLASS (t));
	    goto out;
	}
    }

    tb -> tb_srcref = htons ((u_short) (getpid () & 0xffff));
    tb -> tb_dstref = t -> t_cr.cr_srcref;
    if (!(tb -> tb_flags & TB_TCP)) {
	if (t -> t_tpdusize == 0)
	    t -> t_tpdusize = SIZE_DFLT;
	else
	    if (t -> t_tpdusize > SIZE_MAXTP0)
		t -> t_tpdusize = SIZE_MAXTP0;
	tb -> tb_tsdusize = (1 << t -> t_tpdusize) - tb -> tb_tpduslop;
    }
    if ((len = t -> t_callinglen) > 0) {
	if (len > sizeof tb -> tb_initiating.ta_selector)
	    len = sizeof tb -> tb_initiating.ta_selector;
	bcopy (t -> t_calling, tb -> tb_initiating.ta_selector,
			tb -> tb_initiating.ta_selectlen = len);
    }
    if ((len = t -> t_calledlen) > 0) {
	if (len > sizeof tb -> tb_responding.ta_selector)
	    len = sizeof tb -> tb_responding.ta_selector;
	bcopy (t -> t_called, tb -> tb_responding.ta_selector,
			tb -> tb_responding.ta_selectlen = len);
    }
    if ((t -> t_options & OPT_TEXPEDITE) && (tb -> tb_flags & TB_TCP))
	tb -> tb_flags |= TB_EXPD;

    ts -> ts_sd = tb -> tb_fd;
    ts -> ts_calling = tb -> tb_initiating;	/* struct copy */
    ts -> ts_called = tb -> tb_responding;	/* struct copy */
    ts -> ts_expedited = (tb -> tb_flags & TB_EXPD) ? 1 : 0;
    ts -> ts_tsdusize = tb -> tb_tsdusize;
    ts -> ts_qos = tb -> tb_qos;	/* struct copy */

    if (t -> t_qbuf) {
	copyTSAPdata (t -> t_qbuf -> qb_data, t -> t_qbuf -> qb_len, ts);
    }
    else
	ts -> ts_cc = 0;

    result = OK;

out: ;
    freetpkt (t);

    return result;
}

/* \f

 */

/* ARGSUSED */

static int  TAccept (tb, responding, data, cc, qos, td)
register struct tsapblk *tb;
char   *data;
int	responding,
	cc;
struct QOStype *qos;
struct TSAPdisconnect *td;
{
    int	    result;
    register struct tsapkt *t;

    if (!(tb -> tb_flags & TB_TCP) && cc > 0)
	return tsaplose (td, DR_PARAMETER, NULLCP,
		    "initial user data not allowed with class 0");

    if ((t = newtpkt (TPDU_CC)) == NULL)
	return tsaplose (td, DR_CONGEST, NULLCP, "out of memory");

    t -> t_cc.cc_dstref = tb -> tb_dstref;
    t -> t_cc.cc_srcref = tb -> tb_srcref;
    t -> t_cc.cc_class = CR_CLASS_TP0;
    if (!(tb -> tb_flags & TB_TCP)) {
	register int    i,
	                j;
	int     k;

	i = k = tb -> tb_tsdusize + tb -> tb_tpduslop;
	for (j = 0; i > 0; j++)
	    i >>= 1;
	if (k == (1 << (j - 1)))
	    j--;
	if (j > SIZE_MAXTP0) {
	    j = SIZE_MAXTP0;
	    tb -> tb_tsdusize = (1 << j) - tb -> tb_tpduslop;
	}
	if (j != SIZE_DFLT)
	    t -> t_tpdusize = j;
    }
    if (responding)
	bcopy (tb -> tb_responding.ta_selector, t -> t_called,
			t -> t_calledlen = tb -> tb_responding.ta_selectlen);
    if (tb -> tb_flags & TB_EXPD)
	t -> t_options |= OPT_TEXPEDITE;
    copyTPKTdata (t, data, cc);

    if ((result = tpkt2fd (tb, t, tb -> tb_writefnx)) == NOTOK)
	(void) tsaplose (td, t -> t_errno, NULLCP, NULLCP);
    else {
	tb -> tb_flags |= TB_CONN;
#ifdef  MGMT
	if (tb -> tb_manfnx)
	    (*tb -> tb_manfnx) (OPREQIN, tb);
#endif
    }

    freetpkt (t);

    return result;
}

/* \f

 */

static int  TWrite (tb, uv, expedited, td)
register struct tsapblk *tb;
register struct udvec *uv;
int	expedited;
struct TSAPdisconnect *td;
{
    int	    cc,
	    j,
	    len,
	    result;
#if	defined(X25) || defined(MGMT)
    int	    dlen;
#endif
    register char *bp,
		  *ep;
    register struct tsapkt *t;
    register struct udvec *vv,
			  *wv;

#if	defined(X25) || defined(MGMT)
    dlen = 0;
#endif

    ep = (bp = uv -> uv_base) + (cc = uv -> uv_len);
    while (uv -> uv_base) {
	if ((t = newtpkt (expedited ? TPDU_ED : TPDU_DT)) == NULL)
	    return tsaplose (td, DR_CONGEST, NULLCP, "out of memory");

	wv = (vv = t -> t_udvec) + NTPUV - 1;
	for (len = tb -> tb_tsdusize; len > 0 && vv < wv; len -= j) {
	    j = min (cc, len);
#if	defined(X25) || defined(MGMT)
	    dlen += j;
#endif
	    vv -> uv_base = bp, vv -> uv_len = j, vv++;
	    bp += j, cc -= j;

	    if (bp >= ep) {
		if ((bp = (++uv) -> uv_base) == NULL)
		    break;
		ep = bp + (cc = uv -> uv_len);
	    }
	}

	if (uv -> uv_base == NULL)
	    t -> t_dt.dt_nr |= DT_EOT;

	if ((result = tpkt2fd (tb, t, tb -> tb_writefnx)) == NOTOK) {
	    (void) tsaplose (td, t -> t_errno, NULLCP, NULLCP);
#ifdef	X25
	    if (tb -> tb_flags & TB_X25)
		LLOG (x25_log, LLOG_NOTICE,
		      ("connection %d broken, %d/%d octets sent/recv",
		       tb -> tb_fd, tb -> tb_sent, tb -> tb_recv));
#endif
	    freetblk (tb);
	}

	freetpkt (t);
	if (result == NOTOK)
	    return NOTOK;
    }

#ifdef	X25
    tb -> tb_sent += dlen;
#endif
#ifdef  MGMT
    if (tb -> tb_manfnx)
	(*tb -> tb_manfnx) (USERDT, tb, dlen);
#endif

    return OK;
}

/* \f

 */

/* ARGSUSED */

static int  TRead (tb, tx, td, async, oob)
register struct tsapblk *tb;
register struct TSAPdata *tx;
struct TSAPdisconnect *td;
int	async,
	oob;
{
    int     eot;
    register struct tsapkt *t;

    bzero ((char *) tx, sizeof *tx);
    tx -> tx_qbuf.qb_forw = tx -> tx_qbuf.qb_back = &tx -> tx_qbuf;

    for (;;) {
	if ((t = fd2tpkt (tb -> tb_fd, tb -> tb_initfnx, tb -> tb_readfnx))
			== NULL
		|| t -> t_errno != OK) {
	    (void) tsaplose (td, t ? t -> t_errno : DR_CONGEST, NULLCP,
		    NULLCP);
#ifdef	X25
	    if (tb -> tb_flags & TB_X25)
		LLOG (x25_log, LLOG_NOTICE,
		      ("connection %d broken, %d/%d octets sent/recv",
		       tb -> tb_fd, tb -> tb_sent, tb -> tb_recv));
#endif
	    break;
	}

	switch (TPDU_CODE (t)) {
	    case TPDU_DT: 
		eot = t -> t_dt.dt_nr & DT_EOT;
		if (t -> t_qbuf) {
		    insque (t -> t_qbuf, tb -> tb_qbuf.qb_back);
		    tb -> tb_len += t -> t_qbuf -> qb_len;
#ifdef	X25
		    tb -> tb_recv += t -> t_qbuf -> qb_len;
#endif
		    t -> t_qbuf = NULL;
		}
		freetpkt (t);
#ifdef  MGMT
		if (tb -> tb_manfnx)
		    (*tb -> tb_manfnx) (USERDR, tb, tb -> tb_len);
#endif
		if (!eot) {
		    if (async)
			return DONE;

		    continue;
		}
		tx -> tx_expedited = 0;
		if (tb -> tb_qbuf.qb_forw != &tb -> tb_qbuf) {
		    tx -> tx_qbuf = tb -> tb_qbuf;/* struct copy */
		    tx -> tx_qbuf.qb_forw -> qb_back =
			    tx -> tx_qbuf.qb_back -> qb_forw = &tx -> tx_qbuf;
		    tx -> tx_cc = tb -> tb_len;
		    tb -> tb_qbuf.qb_forw =
			    tb -> tb_qbuf.qb_back = &tb -> tb_qbuf;
		    tb -> tb_len = 0;
		}
		return OK;

	    case TPDU_ED:
		if (t -> t_qbuf) {
		    insque (t -> t_qbuf, tx -> tx_qbuf.qb_back);
		    tx -> tx_cc = t -> t_qbuf -> qb_len;
		    t -> t_qbuf = NULL;
		}
		freetpkt (t);
		tx -> tx_expedited = 1;
		return OK;

	    case TPDU_DR: 
		td -> td_reason = t -> t_dr.dr_reason;
		if (t -> t_qbuf) {
		    copyTSAPdata (t -> t_qbuf -> qb_data,
			    t -> t_qbuf -> qb_len, td);
		}
		else
		    td -> td_cc = 0;
		break;

	    case TPDU_ER:
		switch (t -> t_er.er_reject) {
		    case ER_REJ_NOTSPECIFIED:
		    default:
			td -> td_reason = DR_UNKNOWN;
			break;

		    case ER_REJ_CODE:
		    case ER_REJ_TPDU:
		    case ER_REJ_VALUE:
			td -> td_reason = DR_PROTOCOL;
			break;
		}
		td -> td_cc = 0;
		break;

	    default: 
		(void) tpktlose (tb, td, DR_PROTOCOL, NULLCP,
			"transport protocol mangled: not expecting 0x%x",
			TPDU_CODE (t));
		break;
	}
	break;
    }

    freetpkt (t);
    freetblk (tb);

    return NOTOK;
}

/* \f

 */

static int  TDisconnect (tb, data, cc, td)
register struct tsapblk *tb;
char   *data;
int	cc;
struct TSAPdisconnect *td;
{
    int     result;
#ifdef	TCP
    register struct tsapkt *t;
#endif

    result = OK;
#ifdef	TCP
    if (tb -> tb_flags & TB_TCP) {
	if (t = newtpkt (TPDU_DR)) {
	    t -> t_dr.dr_srcref = tb -> tb_srcref;
	    t -> t_dr.dr_dstref = tb -> tb_dstref;
	    t -> t_dr.dr_reason = DR_NORMAL;
	    copyTPKTdata (t, data, cc);

	    if ((result = tpkt2fd (tb, t, tb -> tb_writefnx)) == NOTOK)
		(void) tsaplose (td, t -> t_errno, NULLCP, NULLCP);

	    freetpkt (t);
	}
	else
	    result = tsaplose (td, DR_CONGEST, NULLCP, "out of memory");
    }
#endif
#ifdef X25
    if (tb -> tb_flags & TB_X25)
	LLOG (x25_log, LLOG_NOTICE,
	     ("connection %d closed, %d/%d octets sent/recv",
	      tb -> tb_fd, tb -> tb_sent, tb -> tb_recv));
#endif
    freetblk (tb);

    return result;
}

/* \f

 */

static	TLose (tb, reason, td)
register struct tsapblk *tb;
int	reason;
struct TSAPdisconnect *td;
{
    struct tsapkt  *t;

    switch (reason) {
	case DR_UNKNOWN: 
	case DR_CONGEST: 
	case DR_SESSION: 
	case DR_ADDRESS: 
	    if ((t = newtpkt (TPDU_DR)) == NULLPKT)
		break;

	    t -> t_dr.dr_srcref = tb -> tb_srcref;
	    t -> t_dr.dr_dstref = tb -> tb_dstref;
	    t -> t_dr.dr_reason = reason;
	    copyTPKTdata (t, td -> td_data, td -> td_cc);
	    break;

	default: 
	    if ((t = newtpkt (TPDU_ER)) == NULLPKT)
		break;

	    t -> t_er.er_dstref = tb -> tb_dstref;
	    switch (reason) {
		case DR_PROTOCOL: 
		    t -> t_er.er_reject = ER_REJ_TPDU;
		    break;

		default: 
		    t -> t_er.er_reject = ER_REJ_NOTSPECIFIED;
		    break;
	    }
	    break;
    }
    if (t) {
	(void) tpkt2fd (tb, t, tb -> tb_writefnx);
	freetpkt (t);
    }
}

/* \f

 */

/* at present, used by TCP, X.25 and BRG back-ends... */

#ifndef	TCP
#undef	WRITEV
#endif

#include <errno.h>
#include <sys/ioctl.h>
#ifdef	WRITEV
#include <sys/uio.h>
#endif
#ifdef	TCP
#include "internet.h"
#else
#define	write_tcp_socket	NULLIFP
#endif
#ifdef	X25
#include "x25.h"
#else
#define	write_x25_socket	NULLIFP
#endif


extern	int	errno;

/* \f

 */

int	tp0write (tb, t, cp, n)
register struct tsapblk *tb;
register struct tsapkt *t;
char   *cp;
int	n;
{
    register int    cc;
    register char   *p,
		    *q;
    register struct qbuf *qb;
    register struct udvec  *uv;
#if	defined(WRITEV) || defined(SUN_X25) || defined(CAMTEC_CCL)
    struct iovec iovs[NTPUV + 4];
    register struct iovec *iov;
#endif

#if	defined(WRITEV) || defined(SUN_X25) || defined(CAMTEC_CCL)
#ifdef	FIONBIO
    if (tb -> tb_flags & TB_QWRITES)
	goto single;
#endif
    iov = iovs;
    cc = 0;

    if (tb -> tb_flags & TB_X25) {
	iov -> iov_base = (char *) &t -> t_li;
	cc += (iov -> iov_len = sizeof t -> t_li);
	iov++;

	iov -> iov_base = (char *) &t -> t_code;
	cc += (iov -> iov_len = sizeof t -> t_code);
	iov++;

    }
    else {
	iov -> iov_base = (char *) &t -> t_pkthdr;
	cc += (iov -> iov_len = TPKT_HDRLEN (t));
	iov++;
    }

    iov -> iov_base = cp;
    cc += (iov -> iov_len = n);
    iov++;

    if (t -> t_vdata) {
	iov -> iov_base = t -> t_vdata;
	cc += (iov -> iov_len = t -> t_vlen);
	iov++;
    }

    for (uv = t -> t_udvec; uv -> uv_base; uv++) {
	iov -> iov_base = uv -> uv_base;
	cc += (iov -> iov_len = uv -> uv_len);
	iov++;
    }

    if ((n = writev (tb -> tb_fd, iovs, iov - iovs)) != cc) {
	cc = NOTOK;
#ifdef	SUN_X25
	if (tb -> tb_flags & TB_X25
	        && compat_log -> ll_events & LLOG_EXCEPTIONS)
	    (void) log_cause_and_diag (tb -> tb_fd);
#endif
    }
    else
	if (tb -> tb_flags & TB_X25) {
	    DLOG (compat_log, LLOG_DEBUG, ("X.25 write %d bytes", cc));
	}
    goto out;

single: ;
#endif

    cc = ((tb -> tb_flags & TB_X25) ? sizeof t -> t_li + sizeof t -> t_code
	  			    : TPKT_HDRLEN (t))
		+ n;
    if (t -> t_vdata)
	cc += t -> t_vlen;
    for (uv = t -> t_udvec; uv -> uv_base; uv++)
	cc += uv -> uv_len;

    if (p = malloc (sizeof *qb + (unsigned) cc)) {
	int	nc,
		onoff;
	IFP	wfnx = (tb -> tb_flags & TB_X25) ? write_x25_socket
						 : write_tcp_socket;

#ifdef	FIONBIO
	if (tb -> tb_flags & TB_QWRITES) {
	    qb = (struct qbuf *) p;
	    qb -> qb_forw = qb -> qb_back = qb;
	    qb -> qb_data = qb -> qb_base, qb -> qb_len = cc;
	    p = qb -> qb_data;
	}
#endif

	if (tb -> tb_flags & TB_X25) {
	    bcopy ((char *) &t -> t_li, q = p, sizeof t -> t_li);
	    q += sizeof t -> t_li;

	    bcopy ((char *) &t -> t_code, q, sizeof t -> t_code);
	    q += sizeof t -> t_code;
	}
	else {
	    bcopy ((char *) &t -> t_pkthdr, q = p, TPKT_HDRLEN (t));
	    q += TPKT_HDRLEN (t);
	}

	bcopy (cp, q, n);
	q += n;

	if (t -> t_vdata) {
	    bcopy (t -> t_vdata, q, t -> t_vlen);
	    q += t -> t_vlen;
	}

	for (uv = t -> t_udvec; uv -> uv_base; uv++) {
	    bcopy (uv -> uv_base, q, uv -> uv_len);
	    q += uv -> uv_len;
	}

#ifdef	FIONBIO
	if (tb -> tb_qwrites.qb_forw != &tb -> tb_qwrites) {
	    nc = 0;
	    goto insert;
	}

	if (tb -> tb_flags & TB_QWRITES)
	    (void) ioctl (tb -> tb_fd, FIONBIO, (onoff = 1, (char *) &onoff));
#endif

	nc = (*wfnx) (tb -> tb_fd, p, cc);

#ifdef	FIONBIO
	if (tb -> tb_flags & TB_QWRITES)
	    (void) ioctl (tb -> tb_fd, FIONBIO, (onoff = 0, (char *) &onoff));
#endif

	if (nc != cc) {
#ifdef	FIONBIO
	    if (tb -> tb_flags & TB_QWRITES) {
		if (nc == NOTOK) {
		    if (errno != EWOULDBLOCK)
			goto losing;
		    nc = 0;
		}
		else
		    if (nc > 0 && (tb -> tb_flags & TB_X25)) {
			SLOG (tsap_log, LLOG_EXCEPTIONS, NULLCP,
			      ("partial write (%d of %d octets) to X.25",
			       nc, cc));
			goto losing;
		    }

		if ((*tb -> tb_queuePfnx) (tb, 1, (struct TSAPdisconnect *) 0)
		        == NOTOK)
		    goto losing;

		qb -> qb_data += nc, qb -> qb_len -= nc;
insert: ;
		insque (qb, tb -> tb_qwrites.qb_back);
		DLOG (tsap_log, LLOG_TRACE,
		      ("queueing blocked write of %d of %d octets", nc, cc));
		qb = NULL;
	    }
	    else
#endif
	    {
losing: ;
		cc = NOTOK;
	    }
	}

#ifdef	FIONBIO
	if (tb -> tb_flags & TB_QWRITES) {
	    if (qb)
		free ((char *) qb);
	}
	else
#endif
	    free (p);
	goto out;
    }
    if ((tb -> tb_flags & TB_X25) || tb -> tb_flags & TB_QWRITES) {
	SLOG (tsap_log, LLOG_EXCEPTIONS, NULLCP,
	      ("unable to malloc %d octets for pseudo-writev, failing...",
	       cc));

	cc = NOTOK;
	goto out;
    }

#ifdef	TCP
    SLOG (tsap_log, LLOG_EXCEPTIONS, NULLCP,
	  ("unable to malloc %d octets for pseudo-writev, continuing...",
	   cc));

    cc = TPKT_HDRLEN (t);
    if (write_tcp_socket (tb -> tb_fd, (char *) &t -> t_pkthdr, cc) != cc) {
err: 	;
	cc = NOTOK;
	goto out;
    }

    if (write_tcp_socket (tb -> tb_fd, cp, n) != n)
	goto err;
    cc += n;

    if (t -> t_vdata
	    && write_tcp_socket (tb -> tb_fd, t -> t_vdata, t -> t_vlen)
		    != t -> t_vlen)
	goto err;
    cc += t -> t_vlen;

    for (uv = t -> t_udvec; uv -> uv_base; uv++) {
	if (write_tcp_socket (tb -> tb_fd, uv -> uv_base, uv -> uv_len)
		!= uv -> uv_len)
	    goto err;
	cc += uv -> uv_len;
    }
#endif

out: ;

    return cc;
}

/* \f

 */

#ifdef	FIONBIO
static int  TDrain (tb, td)
register struct tsapblk *tb;
struct TSAPdisconnect *td;
{
    int	    nc,
	    onoff,
	    result;
    register struct qbuf *qb;
    IFP	    wfnx = (tb -> tb_flags & TB_X25) ? write_x25_socket
					     : write_tcp_socket;

    (void) ioctl (tb -> tb_fd, FIONBIO, (onoff = 1, (char *) &onoff));

    while ((qb = tb -> tb_qwrites.qb_forw) != &tb -> tb_qwrites) {
	if ((nc = (*wfnx) (tb -> tb_fd, qb -> qb_data, qb -> qb_len))
	        != qb -> qb_len) {
	    if (nc == NOTOK) {
		if (errno != EWOULDBLOCK) {
		    result = tsaplose (td, DR_NETWORK, "failed",
				      "write to network");
		    goto out;
		}

		nc = 0;
	    }
	    else
		if (nc > 0 && (tb -> tb_flags & TB_X25)) {
		    SLOG (tsap_log, LLOG_EXCEPTIONS, NULLCP,
			  ("partial write (%d of %d octets) to X.25",
			   nc, qb -> qb_len));
		    result = tsaplose (td, DR_NETWORK, NULLCP,
				       "partial write (%d of %d octets) to X.25",
				       nc, qb -> qb_len);
		    goto out;
		}

	    DLOG (tsap_log, LLOG_TRACE,
		  ("wrote %d of %d octets from blocked write", nc,
		   qb -> qb_len));
	    qb -> qb_data += nc, qb -> qb_len -= nc;

	    result = OK;
	    goto out;
	}

	DLOG (tsap_log, LLOG_TRACE,
	      ("finished blocked write of %d octets", qb -> qb_len));
	remque (qb);
	free ((char *) qb);
    }
    result = DONE;

out: ;
    (void) ioctl (tb -> tb_fd, FIONBIO, (onoff = 0, (char *) &onoff));

    return result;
}
#endif

/* \f

 */

int	tp0init (tb)
register struct tsapblk *tb;
{
    tb -> tb_connPfnx = TConnect;
    tb -> tb_retryPfnx = TRetry;

    tb -> tb_startPfnx = TStart;
    tb -> tb_acceptPfnx = TAccept;

    tb -> tb_writePfnx = TWrite;
    tb -> tb_readPfnx = TRead;
    tb -> tb_discPfnx = TDisconnect;
    tb -> tb_losePfnx = TLose;

#ifdef	FIONBIO
    tb -> tb_drainPfnx = TDrain;
#endif

#ifdef  MGMT
    tb -> tb_manfnx = TManGen;
#endif
}
#endif