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 s

⟦33480439a⟧ TextFile

    Length: 38934 (0x9816)
    Types: TextFile
    Names: »ssaprovider.c«

Derivation

└─⟦2d1937cfd⟧ Bits:30007241 EUUGD22: P.P 5.0
    └─⟦35176feda⟧ »EurOpenD22/isode/isode-6.tar.Z« 
        └─⟦de7628f85⟧ 
            └─⟦this⟧ »isode-6.0/ssap/ssaprovider.c« 

TextFile

/* ssaprovider.c - implement the session protocol */

#ifndef	lint
static char *rcsid = "$Header: /f/osi/ssap/RCS/ssaprovider.c,v 7.1 89/11/30 23:51:19 mrose Exp $";
#endif

/* 
 * $Header: /f/osi/ssap/RCS/ssaprovider.c,v 7.1 89/11/30 23:51:19 mrose Exp $
 *
 *
 * $Log:	ssaprovider.c,v $
 * Revision 7.1  89/11/30  23:51:19  mrose
 * touch-up
 * 
 * Revision 7.0  89/11/23  22:25:45  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 <signal.h>
#include "spkt.h"
#include "tailor.h"

/* \f

   DATA */

static int  once_only = 0;
static struct ssapblk ssapque;
static struct ssapblk *SHead = &ssapque;


int	TDATAser (), TDISCser ();


/* \f

   S-DATA.REQUEST */

int	SDataRequest (sd, data, cc, si)
int	sd;
char   *data;
int	cc;
struct SSAPindication *si;
{
    SBV	    smask;
    int     result;
    struct udvec uvs[2];
    register struct udvec *uv = uvs;
    register struct ssapblk *sb;

    missingP (data);
    if (cc <= 0)
	return ssaplose (si, SC_PARAMETER, NULLCP,
		    "illegal value for SSDU length (%d)", cc);
    missingP (si);

    smask = sigioblock ();

    ssapPsig (sb, sd);

    uv -> uv_base = data, uv -> uv_len = cc, uv++;
    uv -> uv_base = NULL;

    result = SDataRequestAux (sb, SPDU_DT, uvs, si);

    (void) sigiomask (smask);

    return result;
}

/* \f

   S-WRITE.REQUEST (pseudo; write user data vectors) */

int	SWriteRequest (sd, typed, uv, si)
int	sd;
int	typed;
struct udvec *uv;
struct SSAPindication *si;
{
    SBV	    smask;
    int     result;
    register struct ssapblk *sb;

    missingP (uv);
    missingP (si);

    smask = sigioblock ();

    ssapPsig (sb, sd);

    result = SDataRequestAux (sb, typed ? SPDU_TD : SPDU_DT, uv, si);

    (void) sigiomask (smask);

    return result;
}

/* \f

 */

#define	NSPUV	12	/* really should be MSG_MAXIOVLEN - 4 */


int	SDataRequestAux (sb, code, uv, si)
register struct ssapblk *sb;
int	code;
register struct udvec *uv;
struct SSAPindication *si;
{
    int     begin,
	    cc,   	    
            j,
	    len,
    	    n,
            result;
    register char *bp,
    		  *ep;
    register struct ssapkt *s;
    struct TSAPdisconnect   tds;
    register struct TSAPdisconnect *td = &tds;
    struct udvec vvs[NSPUV];
    register struct udvec  *vv,
    			   *wv;
    struct udvec *xv;

    switch (code) {
	case SPDU_DT: 
	    if ((sb -> sb_requirements & SR_DAT_EXISTS)
		    && !(sb -> sb_owned & ST_DAT_TOKEN))
		return ssaplose (si, SC_OPERATION, NULLCP,
			"data token not owned by you");
	    break;

	case SPDU_TD: 
	    if (!(sb -> sb_requirements & SR_TYPEDATA))
		return ssaplose (si, SC_OPERATION, NULLCP,
			"typed data service unavailable");
	    break;
    }

    n = 0;
    for (vv = uv; vv -> uv_base; vv++)
	n += vv -> uv_len;
    if (n == 0)
	return ssaplose (si, SC_PARAMETER, NULLCP, "zero-length SSDU");
    
    ep = (bp = uv -> uv_base) + (cc = uv -> uv_len);
    begin = 1;
    while (uv -> uv_base) {
	len = sb -> sb_tsdu_us ? min (n, sb -> sb_tsdu_us - SSDU_MAGIC) : n;
	vv = vvs;
	vvs[0].uv_base = vvs[1].uv_base = NULL;
	vvs[1].uv_inline = 0;

	if (code == SPDU_DT) {
	    if ((s = newspkt (SPDU_GT)) == NULL)
		return ssaplose (si, SC_CONGEST, NULLCP, "out of memory");
	    s -> s_mask |= SMASK_SPDU_GT;

	    if (spkt2tsdu (s, &vv -> uv_base, &vv -> uv_len) == NOTOK) {
		(void) ssaplose (si, s -> s_errno, NULLCP, NULLCP);
		goto out1;
	    }
	    freespkt (s);
	    s = NULL;
	    vv++;
	}

	xv = vv++;

	wv = vvs + NSPUV - 1;
	for (; len > 0 && vv < wv; len -= j) {
	    j = min (cc, len);
	    vv -> uv_base = bp, vv -> uv_len = j, vv -> uv_inline = 1, vv++;
	    bp += j, cc -= j, n -= j;

	    if (bp >= ep) {
		if ((bp = (++uv) -> uv_base) == NULL)
		    break;
		ep = bp + (cc = uv -> uv_len);
	    }
	}
	if (!sb -> sb_tsdu_us && uv -> uv_base) {
	    (void) ssaplose (si, SC_PARAMETER, NULLCP,
			     "too many vector entries in SDU");
	    goto out2;
	}
	vv -> uv_base = NULL;

	vv = xv;
	if ((s = newspkt (code)) == NULL) {
	    (void) ssaplose (si, SC_CONGEST, NULLCP, "out of memory");
	    goto out2;
	}
	if (sb -> sb_tsdu_us) {
	    s -> s_mask |= SMASK_ENCLOSE;
	    if (begin) {
		s -> s_enclose |= ENCL_BEGIN;
		begin = 0;
	    }	    
	    if (uv -> uv_base == NULL)
		s -> s_enclose |= ENCL_END;
	}
	if (spkt2tsdu (s, &vv -> uv_base, &vv -> uv_len) == NOTOK) {
	    (void) ssaplose (si, s -> s_errno, NULLCP, NULLCP);
	    goto out3;
	}
	freespkt (s);
	s = NULL;

	if ((result = TWriteRequest (sb -> sb_fd, vvs, td)) == NOTOK)
	    (void) ts2sslose (si, "TWriteRequest", td);

	free (vvs[0].uv_base);
	if (code == SPDU_DT)
	    free (vvs[1].uv_base);

	if (result == NOTOK)
	    return NOTOK;
    }
    return OK;

out3: ;
    if (vvs[1].uv_base && !vvs[1].uv_inline)
	free (vvs[1].uv_base);
out2: ;
    if (vvs[0].uv_base)
	free (vvs[0].uv_base);
out1: ;
    freespkt (s);

    return NOTOK;
}

/* \f

   S-READ.REQUEST (pseudo; synchronous read) */

int	SReadRequest (sd, sx, secs, si)
int	sd;
struct SSAPdata *sx;
int	secs;
struct SSAPindication *si;
{
    SBV	    smask;
    int     result;
    register struct ssapblk *sb;

    missingP (sx);
    missingP (si);

    smask = sigioblock ();

    if ((sb = findsblk (sd)) == NULL) {
	(void) sigiomask (smask);
	return ssaplose (si, SC_PARAMETER, NULLCP, "invalid session descriptor");
    }
    if (!(sb -> sb_flags & SB_CONN)) {
	(void) sigiomask (smask); 
	return ssaplose (si, SC_PARAMETER, NULLCP, 
			    "session descriptor not connected"); 
    } 
    if (sb -> sb_flags & SB_FINN) { 
	(void) sigiomask (smask); 
	return ssaplose (si, SC_OPERATION, NULLCP, 
			    "session descriptor finishing"); 
    } 

    result = SReadRequestAux (sb, sx, secs, si, 0, NULLTX);

    (void) sigiomask (smask);

    return result;
}

/* \f

 */

static int  SReadRequestAux (sb, sx, secs, si, async, tx)
register struct ssapblk *sb;
register struct SSAPdata *sx;
int	secs;
struct SSAPindication *si;
int	async;
struct TSAPdata *tx;
{
    int     eot;
    char    tokens;
    register struct ssapkt *s;

    bzero ((char *) sx, sizeof *sx);
    sx -> sx_qbuf.qb_forw = sx -> sx_qbuf.qb_back = &sx -> sx_qbuf;
    bzero ((char *) si, sizeof *si);

    for (; s = sb2spkt (sb, si, secs, tx); tx = NULLTX) {
	if (!(s -> s_mask & SMASK_SPDU_EXPD))
	    switch (sb -> sb_pr) {
		case SPDU_PR:
		    break;

		case SPDU_MAA:
		    if (s -> s_code == SPDU_MAA)
			sb -> sb_pr = SPDU_PR;
		    break;

		case SPDU_RS:
		    switch (s -> s_code) {
			case SPDU_AB:
#ifdef	notdef
			case SPDU_AI:	/* aka SPDU_AB */
#endif
			    if (s -> s_mask & SMASK_SPDU_AB)
				break;	/* else fall */
			case SPDU_AD:
			case SPDU_RS:
			    sb -> sb_pr = SPDU_PR;
			    break;

			default:
			    goto drop_it;
		    }
		    break;

		case SPDU_RA:
		    switch (s -> s_code) {
			case SPDU_AB:
			    break;

			case SPDU_AA:
#ifdef	notdef
			case SPDU_AIA:	/* aka SPDU_AA */
#endif
			    if (s -> s_mask & SMASK_SPDU_AA)
				break;	/* else fall */
			case SPDU_ADA:
			case SPDU_RA:
			    sb -> sb_pr = SPDU_PR;
			    break;

			default:
drop_it: ;
			    SLOG (ssap_log, LLOG_EXCEPTIONS, NULLCP,
				  ("discarding 0x%x SPDU", s -> s_code));
			    freespkt (s);
			    goto spin;
		    }
		    break;

		case SPDU_AB:
		    if (s -> s_code != SPDU_AB)
			goto drop_it;
		    sb -> sb_pr = SPDU_PR;
		    break;

		default:
		    break;
	    }

	if (sb -> sb_flags & (SB_RS | SB_AI))
	    switch (s -> s_code) {
		case SPDU_PR:
		    switch (s -> s_pr_type) {
			case PR_RS:
			case PR_RA:
			    break;
			default:
			    goto drop_it;
		    }
		    break;

		case SPDU_RS:
		    if (SDoCollideAux (sb -> sb_flags & SB_INIT ? 1 : 0,
				sb -> sb_rs, sb -> sb_rsn,
				(int) s -> s_rs_type, (long) s -> s_rs_serial)
			    != NOTOK)
			goto drop_it;
		    break;

		case SPDU_RA:
		    break;
		  
		case SPDU_AD:
		    if (SDoCollideAux (sb -> sb_flags & SB_INIT ? 1 : 0,
				sb -> sb_rs, sb -> sb_rsn, SYNC_DISC, 0L)
			    != NOTOK)
			goto drop_it;
		    break;

		case SPDU_AB:
#ifdef	notdef
		case SPDU_AI:	/* aka SPDU_AB */
#endif
		    if (s -> s_mask & SMASK_SPDU_AB)
			break;
		    if (SDoCollideAux (sb -> sb_flags & SB_INIT ? 1 : 0,
				sb -> sb_rs, sb -> sb_rsn, SYNC_INTR, 0L)
			    != NOTOK)
			goto drop_it;
		    break;
	    }
	
	if (sb -> sb_flags & (SB_ED | SB_ERACK))
	    switch (s -> s_code) {
		case SPDU_AB:
		    break;

		case SPDU_MAP:
		case SPDU_MIP:
		    if (sb -> sb_flags & SB_ED)
			break;
		    goto drop_it;

		case SPDU_PR:
		    if (s -> s_pr_type == PR_RS)
			break;
		    goto drop_it;

		case SPDU_GT:
		    if ((s -> s_mask & SMASK_SPDU_GT)
			    && (s -> s_mask & SMASK_GT_TOKEN)
			    && (s -> s_gt_token & ST_DAT_TOKEN))
			break;	/* else fall */

		default:
		    goto drop_it;
	    }

	if (sb -> sb_len > 0)
	    switch (s -> s_code) {
		case SPDU_PT:
		case SPDU_EX:
		    break;

		case SPDU_PR:
		    if (s -> s_pr_type != PR_RS)
			break;
		case SPDU_RS:
		case SPDU_ER:
		case SPDU_ED:
		case SPDU_AD:
#ifdef	notdef
		case SPDU_AI:	/* aka SPDU_AB */
#endif
		case SPDU_AB:
		    SLOG (ssap_log, LLOG_EXCEPTIONS, NULLCP,
			  ("flush partially assembled (T))SSDU"));
		    QBFREE (&sb -> sb_qbuf);
		    sb -> sb_len = 0;
		    break;

		case SPDU_GT:
		    if (s -> s_mask & SMASK_SPDU_GT)
			break;	/* else SPDU_DT */
		default:
		    if (sb -> sb_code == s -> s_code)
			break;
		    (void) spktlose (sb -> sb_fd, si, SC_PROTOCOL, NULLCP,
			    "session protocol mangled: expecting 0x%x, got 0x%x during segmentation",
			    sb -> sb_code, s -> s_code);
		    goto out;
	    }

/* allows AB SPDUs to have 512, not 9, octets (which is fine by me) */
	if (s -> s_ulen > CN_SIZE && sb -> sb_version < SB_VRSN2) {
	    (void) spktlose (sb -> sb_fd, si, SC_PROTOCOL, NULLCP,
			     "too much user data (%d) in SPDU 0x%x",
			     s -> s_ulen, s -> s_code);
	    goto out;
	}

	if ((s -> s_mask & SMASK_ENCLOSE)
	        && (s -> s_code != SPDU_DT || (s -> s_mask & SMASK_SPDU_GT))
	        && s -> s_code != SPDU_TD) {
	    if (sb -> sb_version < SB_VRSN2) {
		(void) spktlose (sb -> sb_fd, si, SC_PROTOCOL, NULLCP,
				 "unexpected segmentation for SPDU 0x%x",
				 s -> s_code);
		goto out;
	    }

/* XXX: in practice, I don't think this is unreasonable.  It is
	however not too restrictive */

	    if (s -> s_enclose != ENCL_MASK) {
		(void) spktlose (sb -> sb_fd, si, SC_PROTOCOL, NULLCP,
				 "non-trivial segmentation (0x%x) for SPDU 0x%x",
				 s -> s_enclose, s -> s_code);
		goto out;
	    }
	}

	switch (s -> s_code) {
	    case SPDU_PT: 
		if (sb -> sb_flags & SB_GTC) {
		    freespkt (s);
		    goto spin;
		}
		tokens = 0;
		if (s -> s_mask & SMASK_PT_TOKEN) {
#define	dotoken(requires,shift,bit,type) \
{ \
		    if ((sb -> sb_requirements & requires) \
			    && (s -> s_pt_token & bit)) \
			tokens |= bit; \
}
			dotokens ();
#undef	dotoken
		}
		si -> si_type = SI_TOKEN;
		{
		    register struct SSAPtoken *st = &si -> si_token;

		    st -> st_type = ST_PLEASE;
		    st -> st_tokens = tokens;
		    st -> st_owned = sb -> sb_owned;
		    copySPKTdata (s, st);
		}
		freespkt (s);
		return DONE;

	    case SPDU_GT: 
		if (s -> s_mask & SMASK_SPDU_GT) {
		    if (sb -> sb_flags & SB_GTC) {
			freespkt (s);
			goto spin;
		    }
		    tokens = 0;
		    if (s -> s_mask & SMASK_GT_TOKEN) {
#define	dotoken(requires,shift,bit,type) \
{ \
			if ((sb -> sb_requirements & requires) \
			    && (s -> s_gt_token & bit)) \
				sb -> sb_owned |= bit, tokens |= bit; \
}
			dotokens ();
#undef	dotoken
		    }
		    freespkt (s);
		    if (tokens & ST_DAT_TOKEN)
			sb -> sb_flags &= ~(SB_ED | SB_ERACK);
		    si -> si_type = SI_TOKEN;
		    {
			register struct SSAPtoken  *st = &si -> si_token;

			st -> st_type = ST_GIVE;
			st -> st_tokens = tokens;
			st -> st_owned = sb -> sb_owned;
		    }
		    return DONE;
		}		/* else fall for case SPDU_DT: */
#ifdef	notdef
	    case SPDU_DT:
#endif
	    case SPDU_TD:
		sb -> sb_code = s -> s_code;
		if (sb -> sb_tsdu_them) {
		    if (!(s -> s_mask & SMASK_ENCLOSE)) {
			(void) spktlose (sb -> sb_fd, si, SC_PROTOCOL, NULLCP,
				"no segmentation information");
			break;
		    }
		    if ((s -> s_enclose & ENCL_BEGIN)
			    ? sb -> sb_len > 0 : sb -> sb_len == 0) {
			(void) spktlose (sb -> sb_fd, si, SC_PROTOCOL, NULLCP,
				"segmentation mismatch");
			break;
		    }
		    eot = s -> s_enclose & ENCL_END;
		}
		else
		    eot = 1;
		if (s -> s_qbuf.qb_forw != &s -> s_qbuf) {
		    sb -> sb_qbuf.qb_back -> qb_forw = s -> s_qbuf.qb_forw;
		    s -> s_qbuf.qb_forw -> qb_back = sb -> sb_qbuf.qb_back;
		    s -> s_qbuf.qb_back -> qb_forw = &sb -> sb_qbuf;
		    sb -> sb_qbuf.qb_back = s -> s_qbuf.qb_back;
		    sb -> sb_len += s -> s_qlen;
		    s -> s_qbuf.qb_forw =
			    s -> s_qbuf.qb_back = &s -> s_qbuf;
		    s -> s_qlen = 0;
		}
		if (!eot && (s -> s_code == SPDU_DT) && sb -> sb_spdu) {
		    freespkt (sb -> sb_spdu);
		    sb -> sb_spdu = NULL;
		}
		freespkt (s);
		if (!eot)
		    goto spin;
		sx -> sx_type = sb -> sb_code == SPDU_DT ? SX_NORMAL
				    : SX_TYPED;
		if (sb -> sb_qbuf.qb_forw != &sb -> sb_qbuf) {
		    sx -> sx_qbuf = sb -> sb_qbuf;/* struct copy */
		    sx -> sx_qbuf.qb_forw -> qb_back =
			    sx -> sx_qbuf.qb_back -> qb_forw = &sx -> sx_qbuf;
		    sx -> sx_cc = sb -> sb_len;
		    sb -> sb_qbuf.qb_forw =
			    sb -> sb_qbuf.qb_back = &sb -> sb_qbuf;
		    sb -> sb_len = 0;
		}
		return OK;

	    case SPDU_EX: 
		if (sb -> sb_pr != SPDU_PR) {
		    SLOG (ssap_log, LLOG_EXCEPTIONS, NULLCP,
			  ("buffering XSDU during preparation"));
		    if (sb -> sb_xspdu) {
			(void) spktlose (sb -> sb_fd, si, SC_CONGEST, NULLCP,
				"unable to buffer second XSDU");
			break;
		    }
		    sb -> sb_xspdu = s;
		    goto spin;
		}
		sx -> sx_type = SX_EXPEDITED;
		if (s -> s_qbuf.qb_forw != &s -> s_qbuf) {
		    sx -> sx_qbuf = s -> s_qbuf;/* struct copy */
		    sx -> sx_qbuf.qb_forw -> qb_back =
			    sx -> sx_qbuf.qb_back -> qb_forw = &sx -> sx_qbuf;
		    sx -> sx_cc = s -> s_qlen;
		    s -> s_qbuf.qb_forw =
			    s -> s_qbuf.qb_back = &s -> s_qbuf;
		    s -> s_qlen = 0;
		}
		freespkt (s);
		return OK;

	    case SPDU_CD:
	    case SPDU_CDA:
		if (s -> s_code == SPDU_CD) {
		    sb -> sb_flags |= SB_CDA;
		    sx -> sx_type = SX_CAPDIND;
		}
		else {
		    sb -> sb_flags &= ~SB_CD;
		    sx -> sx_type = SX_CAPDCNF;
		}
		if (s -> s_udata) {
		    register struct qbuf *qb;

		    qb = (struct qbuf *)
			    malloc (sizeof *qb + (unsigned) s -> s_ulen);
		    if (qb == NULL) {
			(void) spktlose (sb -> sb_fd, si, SC_CONGEST, NULLCP,
				    "out of memory");
			break;
		    }
		    bcopy (s -> s_udata, qb -> qb_data = qb -> qb_base,
			    qb -> qb_len = s -> s_ulen);
		    insque (qb, &sx -> sx_qbuf);
		    sx -> sx_cc = s -> s_ulen;
		}
		freespkt (s);
		return OK;

	    case SPDU_GTC:
		if (sb -> sb_flags & SB_Vact) {
		    freespkt (s);
		    goto spin;
		}
#define	dotoken(requires,shift,bit,type) \
{ \
		if (sb -> sb_requirements & requires) \
		    sb -> sb_owned |= bit; \
}
		dotokens ();
#undef	dotoken
		freespkt (s);
		if ((s = newspkt (SPDU_GTA)) == NULL) {
		    (void) spktlose (sb -> sb_fd, si, SC_CONGEST, NULLCP,
			    "out of memory");
		    break;
		}
		if (spkt2sd (s, sb -> sb_fd, 0, si) == NOTOK)
		    break;
		freespkt (s);
		si -> si_type = SI_TOKEN;
		{
		    register struct SSAPtoken  *st = &si -> si_token;

		    st -> st_type = ST_CONTROL;
		    st -> st_tokens = st -> st_owned = sb -> sb_owned;
		}
		return DONE;

	    case SPDU_GTA:
		if (!(sb -> sb_flags & SB_GTC)) {
		    freespkt (s);
		    goto spin;
		}
		sb -> sb_flags &= ~SB_GTC;
spin: ;
		if (!async || sb -> sb_spdu)
		    continue;
		si -> si_type = SI_DATA;
		{
		    register struct SSAPdata *sk = &si -> si_data;

		    bzero ((char *) sk, sizeof *sk);
		    sk -> sx_qbuf.qb_forw = sk -> sx_qbuf.qb_back =
			&sk -> sx_qbuf;
		}
		return DONE;

	    case SPDU_MAP:
#ifdef	notdef
	    case SPDU_AE:	/* aka SPDU_MAP */
#endif
		if (sb -> sb_V_M != s -> s_map_serial) {
		    freespkt (s);
		    goto spin;
		}
		if (!(s -> s_mask & SMASK_MAP_SYNC)
			|| !(s -> s_map_sync & MAP_SYNC_NOEND))
		    goto spdu_ae;
		if (!(sb -> sb_flags & SB_Vsc))
		    sb -> sb_V_A = sb -> sb_V_M;
		sb -> sb_V_M++;
		if (sb -> sb_flags & (SB_ED | SB_ERACK)) {
		    freespkt (s);
		    goto spin;
		}
		if (sb -> sb_requirements & SR_ACTIVITY)
		    sb -> sb_flags |= SB_Vnextact;
		sb -> sb_flags |= SB_MAA;
		si -> si_type = SI_SYNC;
		{
		    register struct SSAPsync *sn = &si -> si_sync;

		    sn -> sn_type = SN_MAJORIND;
		    sn -> sn_ssn = s -> s_map_serial;
		    copySPKTdata (s, sn);
		}
		freespkt (s);
		return DONE;

	    case SPDU_MAA:
#ifdef	notdef
	    case SPDU_AEA:	/* aka SPDU_MAA */
#endif
		if (sb -> sb_V_M != s -> s_maa_serial + 1) {
		    freespkt (s);
		    goto spin;
		}
		sb -> sb_V_A = sb -> sb_V_R = sb -> sb_V_M;
		if (sb -> sb_requirements & SR_ACTIVITY)
		    if (sb -> sb_flags & SB_Vnextact)
			sb -> sb_flags |= SB_Vact;
		    else
		        sb -> sb_flags &= ~SB_Vact;
		sb -> sb_flags &= ~SB_MAP;
		if (sb -> sb_flags & SB_AE) {
		    sb -> sb_flags &= ~SB_AE;
		    si -> si_type = SI_ACTIVITY;
		    {
			register struct SSAPactivity *sv = &si -> si_activity;

			sv -> sv_type = SV_ENDCNF;
			sv -> sv_ssn = s -> s_maa_serial;
			copySPKTdata (s, sv);
		    }
		}
		else {
		    si -> si_type = SI_SYNC;
		    {
			register struct SSAPsync *sn = &si -> si_sync;

			sn -> sn_type = SN_MAJORCNF;
			sn -> sn_ssn = s -> s_maa_serial;
			copySPKTdata (s, sn);
		    }
		}
		freespkt (s);
		return DONE;

	    case SPDU_MIP:
		if (!(sb -> sb_flags & SB_Vsc)) {
		    sb -> sb_V_A = sb -> sb_V_M;
		    sb -> sb_flags |= SB_Vsc;
		}
		sb -> sb_V_M++;
		if (sb -> sb_flags & (SB_ED | SB_ERACK)) {
		    freespkt (s);
		    goto spin;
		}
		si -> si_type = SI_SYNC;
		{
		    register struct SSAPsync *sn = &si -> si_sync;

		    sn -> sn_type = SN_MINORIND;
		    sn -> sn_options = (s -> s_mask & SMASK_MIP_SYNC)
		    	    && (s -> s_mip_sync & MIP_SYNC_NOEXPL)
			    ? SYNC_NOCONFIRM : SYNC_CONFIRM;
		    sn -> sn_ssn = s -> s_mip_serial;
		    copySPKTdata (s, sn);
		}
		freespkt (s);
	        return DONE;

	    case SPDU_MIA:
		if ((sb -> sb_flags & SB_Vsc)
			|| sb -> sb_V_A > s -> s_mia_serial
			|| s -> s_mia_serial >= sb -> sb_V_M) {
		    freespkt (s);
		    goto spin;
		}
		sb -> sb_V_A = s -> s_mia_serial;
		si -> si_type = SI_SYNC;
		{
		    register struct SSAPsync *sn = &si -> si_sync;

		    sn -> sn_type = SN_MINORCNF;
		    sn -> sn_ssn = s -> s_mia_serial;
		    copySPKTdata (s, sn);
		}
		freespkt (s);
	        return DONE;

	    case SPDU_RS:
		if (s -> s_rs_type == SYNC_RESTART
			&& sb -> sb_V_R > s -> s_rs_serial) {
		    freespkt (s);
		    goto spin;
		}
		sb -> sb_flags &= ~SB_RS, sb -> sb_flags |= SB_RA;
		sb -> sb_rs = s -> s_rs_type;
		sb -> sb_rsn = s -> s_rs_serial;
		if (s -> s_mask & SMASK_RS_SET)
		    sb -> sb_rsettings = s -> s_rs_settings;
		else {
		    sb -> sb_rsettings = 0;
#define	dotoken(requires,shift,bit,type) \
{ \
		    if (sb -> sb_requirements & requires) \
			if ((sb -> sb_owned & bit) \
				&& (sb -> sb_flags & SB_INIT)) \
			    sb -> sb_rsettings = ST_INIT_VALUE << shift; \
			else \
			    sb -> sb_rsettings = ST_RESP_VALUE << shift; \
}
		    dotokens ();
#undef	dotoken
		}
		si -> si_type = SI_SYNC;
		{
		    register struct SSAPsync *sn = &si -> si_sync;

		    sn -> sn_type = SN_RESETIND;
		    sn -> sn_options = sb -> sb_rs;
		    sn -> sn_ssn = sb -> sb_rsn;
		    sn -> sn_settings = sb -> sb_rsettings;
		    copySPKTdata (s, sn);
		}
		freespkt (s);
		return DONE;

	    case SPDU_RA:
		sb -> sb_flags &= ~SB_RS;
		sb -> sb_V_A = sb -> sb_V_M = s -> s_ra_serial;
		if (sb -> sb_rs != SYNC_RESTART)
		    sb -> sb_V_R = 0;
		if (s -> s_mask & SMASK_RA_SET)
		    sb -> sb_rsettings = s -> s_ra_settings;
#define	dotoken(requires,shift,bit,type) \
{ \
		if (sb -> sb_requirements & requires) \
		    switch (sb -> sb_rsettings & (ST_MASK << shift)) { \
			dotoken1 (requires,shift,bit,type); \
 \
			dotoken2 (requires,shift,bit,type); \
		    } \
}
#define	dotoken1(requires,shift,bit,type) \
			case ST_CALL_VALUE << shift: \
			    switch (s -> s_ra_settings & (ST_MASK << shift)) { \
				case ST_INIT_VALUE: \
				    if (sb -> sb_flags & SB_INIT) \
					sb -> sb_owned |= bit; \
				    else \
					sb -> sb_owned &= ~bit; \
				    break; \
 \
				case ST_RESP_VALUE: \
				    if (!(sb -> sb_flags & SB_INIT)) \
					sb -> sb_owned |= bit; \
				    else \
					sb -> sb_owned &= ~bit; \
				    break; \
			    } \
			    break;
#define	dotoken2(requires,shift,bit,type) \
			case ST_INIT_VALUE << shift: \
			    if (sb -> sb_flags & SB_INIT) \
				sb -> sb_owned |= bit; \
			    else \
				sb -> sb_owned &= ~bit; \
			    break; \
 \
			case ST_RESP_VALUE << shift: \
			    if (!(sb -> sb_flags & SB_INIT)) \
				sb -> sb_owned |= bit; \
			    else \
				sb -> sb_owned &= ~bit; \
			    break;
		dotokens ();
#undef	dotoken
#undef	dotoken1
#undef	dotoken2
		si -> si_type = SI_SYNC;
		{
		    register struct SSAPsync *sn = &si -> si_sync;

		    sn -> sn_type = SN_RESETCNF;
		    sn -> sn_ssn = sb -> sb_V_M;
		    sn -> sn_settings = sb -> sb_rsettings;
		    copySPKTdata (s, sn);
		}
		freespkt (s);
		return DONE;

	    case SPDU_PR: 
		switch (s -> s_pr_type) {
		    case PR_MAA: 
			sb -> sb_pr = SPDU_MAA;
			break;
		    case PR_RS: 
			sb -> sb_flags &= ~(SB_ED | SB_ERACK);
			sb -> sb_pr = SPDU_RS;
			break;
		    case PR_RA: 
			sb -> sb_pr = SPDU_RA;
			break;
		    case PR_AB: 
			sb -> sb_pr = SPDU_AB;
			break;
		}
		freespkt (s);
		goto spin;

	    case SPDU_ER:	/* this implementation never generates these */
		sb -> sb_flags |= SB_ERACK;
		si -> si_type = SI_REPORT;
		{
		    register struct SSAPreport *sp = &si -> si_report;

		    sp -> sp_peer = 0;
		    sp -> sp_reason = SP_PROTOCOL;
		}
		freespkt (s);
		return DONE;

	    case SPDU_ED:
		if (sb -> sb_owned & ST_DAT_TOKEN)
		    sb -> sb_flags |= SB_EDACK;
		si -> si_type = SI_REPORT;
		{
		    register struct SSAPreport *sp = &si -> si_report;

		    sp -> sp_peer = 1;
		    sp -> sp_reason = s -> s_ed_reason;
		    copySPKTdata (s, sp);
		}
		freespkt (s);
		return DONE;

	    case SPDU_AS:
		if (sb -> sb_flags & SB_Vact) {
		    freespkt (s);
		    goto spin;
		}
		sb -> sb_V_A = sb -> sb_V_M = sb -> sb_V_R = 1;
		sb -> sb_flags |= SB_Vact;
		si -> si_type = SI_ACTIVITY;
		{
		    register struct SSAPactivity *sv = &si -> si_activity;

		    sv -> sv_type = SV_START;
		    sv -> sv_id = s -> s_as_id;	/* struct copy */
		    copySPKTdata (s, sv);
		}
		freespkt (s);
		return DONE;

	    case SPDU_AR:
		if (sb -> sb_flags & SB_Vact) {
		    freespkt (s);
		    goto spin;
		}
		sb -> sb_V_A = sb -> sb_V_M = s -> s_ar_serial + 1;
		sb -> sb_V_R = 1;
		sb -> sb_flags |= SB_Vact;
		si -> si_type = SI_ACTIVITY;
		{
		    register struct SSAPactivity *sv = &si -> si_activity;

		    sv -> sv_type = SV_RESUME;
		    sv -> sv_id = s -> s_ar_id;	/* struct copy */
		    sv -> sv_oid = s -> s_ar_oid;	/* struct copy */
		    if (s -> s_mask & SMASK_AR_REF)	/* struct copy */
			sv -> sv_connect = s -> s_ar_reference;
		    sv -> sv_ssn = s -> s_ar_serial;
		    copySPKTdata (s, sv);
		}
		freespkt (s);
		return DONE;

	    case SPDU_AD:
spdu_ai: ;
		if (!(sb -> sb_flags & SB_Vact)) {
		    freespkt (s);
		    goto spin;
		}
		sb -> sb_flags &= ~(SB_RS | SB_RA), sb -> sb_flags |= SB_AIA;
		sb -> sb_rs = SYNC_INTR;
		si -> si_type = SI_ACTIVITY;
		{
		    register struct SSAPactivity *sv = &si -> si_activity;

		    sv -> sv_type = s -> s_code == SPDU_AI ? SV_INTRIND
			    : SV_DISCIND;
		    sv -> sv_reason = s -> s_ai_reason;
		}
		freespkt (s);
		return DONE;

	    case SPDU_AA:
#ifdef	notdef
	    case SPDU_AIA:	/* aka SPDU_AA */
#endif
		if (s -> s_mask & SMASK_SPDU_AA) {
		    freespkt (s);
		    goto spin;
		}		/* else fall */
	    case SPDU_ADA:
		if (!(sb -> sb_flags & SB_Vact)) {
		    freespkt (s);
		    goto spin;
		}
		sb -> sb_flags &= ~(SB_AI | SB_Vact);
#define	dotoken(requires,shift,bit,type) \
{ \
		if (sb -> sb_requirements & requires) \
		    sb -> sb_owned |= bit; \
}
		dotokens ();
#undef	dotoken
		si -> si_type = SI_ACTIVITY;
		{
		    register struct SSAPactivity *sv = &si -> si_activity;

		    sv -> sv_type = s -> s_code == SPDU_AIA ? SV_INTRCNF
					: SV_DISCCNF;
		}
		freespkt (s);
		return DONE;		

spdu_ae: ;
		if (!(sb -> sb_flags & SB_Vsc))
		    sb -> sb_V_A = sb -> sb_V_M;
		sb -> sb_V_M++;
		sb -> sb_flags &= ~SB_Vnextact;
		sb -> sb_flags |= SB_MAA | SB_AE;
		si -> si_type = SI_ACTIVITY;
		{
		    register struct SSAPactivity *sv = &si -> si_activity;

		    sv -> sv_type = SV_ENDIND;
		    sv -> sv_ssn = s -> s_map_serial;
		    copySPKTdata (s, sv);
		}
		freespkt (s);
		return DONE;

	    case SPDU_FN: 
		sb -> sb_flags |= SB_FINN;
		si -> si_type = SI_FINISH;
		{
		    register struct SSAPfinish *sf = &si -> si_finish;

		    copySPKTdata (s, sf);
		}
		freespkt (s);
		return DONE;

	    case SPDU_AB: 
		if (!(s -> s_mask & SMASK_SPDU_AB))
		    goto spdu_ai;
		sb -> sb_flags &= ~(SB_ED | SB_EDACK | SB_ERACK);
		si -> si_type = SI_ABORT;
		{
		    register struct SSAPabort  *sa = &si -> si_abort;

		    if (!(sa -> sa_peer = (s -> s_ab_disconnect & AB_DISC_USER)
				? 1 : 0))
			sa -> sa_reason = SC_ABORT;
		    sa -> sa_info = s -> s_udata, sa -> sa_cc = s -> s_ulen;
		    sa -> sa_realinfo = s -> s_udata, s -> s_udata = NULL;
		}
#ifdef	notdef		/* only if transport connection is to be re-used */
		freespkt (s);
		if (s = newspkt (SPDU_AA)) {
		    s -> s_mask |= SMASK_SPDU_AA;
		    (void) spkt2sd (s, sb -> sb_fd, sb -> sb_flags & SB_EXPD
				? 1 : 0, (struct SSAPindication *) 0);
		}
#endif
		break;

	    default: 
		(void) spktlose (sb -> sb_fd, si, SC_PROTOCOL, NULLCP,
			"session protocol mangled: not expecting 0x%x",
			s -> s_code);
		break;
	}
	break;
    }

    if (si -> si_abort.sa_reason == SC_TIMER)
	return NOTOK;

out: ;
    freespkt (s);
    freesblk (sb);

    return NOTOK;
}

/* \f

 */

/* a decision tree (ugh!) */

int	SDoCollideAux (init, localop, localssn, remoteop, remotessn)
int     init,
	localop,
        remoteop;
long    localssn,
        remotessn;
{
    SLOG (ssap_log, LLOG_EXCEPTIONS, NULLCP,
	  ("collide: local<%d,%ld,%s> remote<%d,%ld,%s>",
	   localop, localssn, init ? "initiator" : "responder",
	   remoteop, remotessn, init ? "responder" : "initiator"));

    if (localop == SYNC_DISC)
	return OK;

    if (remoteop == SYNC_DISC)
	return NOTOK;

    if (localop == SYNC_INTR)
	return OK;

    if (remoteop == SYNC_DISC)
	return NOTOK;

    if (localop == SYNC_ABANDON) {
	if (remoteop != SYNC_ABANDON)
	    return OK;

	return (init ? OK : NOTOK);
    }
    else
	if (remoteop == SYNC_ABANDON)
	    return NOTOK;

    if (localop == SYNC_SET) {
	if (remoteop != SYNC_SET)
	    return OK;

	return (init ? OK : NOTOK);
    }
    else
	if (remoteop == SYNC_SET)
	    return NOTOK;

    if (localssn == remotessn)
	return (init ? OK : NOTOK);

    return (localssn < remotessn ? OK : NOTOK);
}

/* \f

   define vectors for INDICATION events */

int	SSetIndications (sd, data, tokens, sync, activity, report, finish,
		abort, si)
int	sd;
IFP	data,	
	tokens,
	sync,
	activity,
	report,
	finish,
	abort;
struct SSAPindication *si;
{
    SBV     smask;
    register struct ssapblk *sb;
    struct TSAPdisconnect   tds;
    register struct TSAPdisconnect *td = &tds;

    if (data || tokens || sync || activity || report || finish || abort) {
	missingP (data);
	missingP (tokens);
	missingP (sync);
	missingP (activity);
	missingP (report);
	missingP (finish);
	missingP (abort);
    }

    smask = sigioblock ();

    ssapPsig (sb, sd);

    if (TSetIndications (sb -> sb_fd, TDATAser, TDISCser, td) == NOTOK)
	if (td -> td_reason == DR_WAITING)
	    return ssaplose (si, SC_WAITING, NULLCP, NULLCP);
	else
	    return ts2sslose (si, "TSetIndications", td);

    if (sb -> sb_DataIndication = data)
	sb -> sb_flags |= SB_ASYN;
    else
	sb -> sb_flags &= ~SB_ASYN;
    sb -> sb_TokenIndication = tokens;
    sb -> sb_SyncIndication = sync;
    sb -> sb_ActivityIndication = activity;
    sb -> sb_ReportIndication = report;
    sb -> sb_ReleaseIndication = finish;
    sb -> sb_AbortIndication = abort;

    (void) sigiomask (smask);

    return OK;
}

/* \f

   TSAP interface */

int	spkt2sd (s, sd, expedited, si)
register struct ssapkt *s;
int     sd,
        expedited;
register struct SSAPindication *si;
{
    int     i,
            len,
            result;
    char   *base,
           *dp;
    struct TSAPdisconnect   tds;
    register struct TSAPdisconnect *td = &tds;

    if (expedited)
	s -> s_mask |= SMASK_SPDU_EXPD;
    if (spkt2tsdu (s, &base, &len) == NOTOK) {
	(void) ssaplose (si, s -> s_errno, NULLCP, NULLCP);
	return NOTOK;
    }
    if (s -> s_code == SPDU_EX) {/* only SX_EXSIZE octets, so no big deal... */
	if (s -> s_udata) {
	    if ((dp = realloc (base, (unsigned) (i = len + s -> s_ulen)))
		    == NULL) {
		free (base);
		(void) ssaplose (si, SC_CONGEST, NULLCP, NULLCP);
		return NOTOK;
	    }
	    bcopy (s -> s_udata, (base = dp) + len, s -> s_ulen);
	    len = i;
	}
    }

    if (len > TX_SIZE)
	expedited = 0;
    if ((result = expedited ? TExpdRequest (sd, base, len, td)
		: TDataRequest (sd, base, len, td)) == NOTOK)
	(void) ts2sslose (si, expedited ? "TExpdRequest" : "TDataRequest", td);

    if (base)
	free (base);

    return result;
}

/* \f

 */

struct ssapkt   *sb2spkt (sb, si, secs, ty)
register struct ssapblk *sb;
register struct SSAPindication *si;
int     secs;
register struct TSAPdata   *ty;
{
    int     cc;
    register struct ssapkt   *s,
			     *p;
    struct TSAPdata txs;
    register struct TSAPdata   *tx = &txs;
    struct TSAPdisconnect   tds;
    register struct TSAPdisconnect *td = &tds;

    if (sb -> sb_pr == SPDU_PR && sb -> sb_xspdu) {
	SLOG (ssap_log, LLOG_EXCEPTIONS, NULLCP,
	      ("returning XSDU buffered during preparation"));
	s = sb -> sb_xspdu;
	sb -> sb_xspdu = NULL;

	return s;
    }

    if (sb -> sb_spdu) {	/* get previous category 0 SPDU */
	SLOG (ssap_log, LLOG_EXCEPTIONS, NULLCP,
	      ("returning category 0 SPDU previously buffered"));
	s = sb -> sb_spdu;
	sb -> sb_spdu = NULL;

	return s;
    }

    if (ty) {
	*tx = *ty;		/* struct copy */
	tx -> tx_qbuf.qb_forw -> qb_back =
		tx -> tx_qbuf.qb_back -> qb_forw = &tx -> tx_qbuf;
	bzero ((char *) ty, sizeof *ty);
	ty -> tx_qbuf.qb_forw = ty -> tx_qbuf.qb_back = &ty -> tx_qbuf;
    }
    else
	if (TReadRequest (sb -> sb_fd, tx, secs, td) == NOTOK) {
	    if (td -> td_reason != DR_TIMER)
		(void) ts2sslose (si, "TReadRequest", td);
	    else
		(void) ssaplose (si, SC_TIMER, NULLCP, NULLCP);

	    return NULL;
	}

    DLOG (ssap_log, LLOG_DEBUG, ("read TSDU, size %d", tx -> tx_cc));

    if ((s = tsdu2spkt (&tx -> tx_qbuf, tx -> tx_cc, (cc = 1, &cc))) == NULL
	    || s -> s_errno != SC_ACCEPT) {
	(void) ssaplose (si, s ? s -> s_errno : SC_CONGEST, NULLCP, NULLCP);
bad1: ;
	freespkt (s);
	TXFREE (tx);
	return NULL;
    }

    if (tx -> tx_expedited)
	s -> s_mask |= SMASK_SPDU_EXPD;
    tx -> tx_cc -= cc;

    switch (s -> s_code) {
	case SPDU_GT: 		/* category 0 SPDUs */
	case SPDU_PT: 
	    if (tx -> tx_cc <= 0)
		goto simple;
	    break;

	case SPDU_EX: 		/* category 1 SPDUs with user data */
	case SPDU_TD: 
	    if (tx -> tx_qbuf.qb_forw != &tx -> tx_qbuf) {
		s -> s_qbuf = tx -> tx_qbuf;/* struct copy */
		s -> s_qbuf.qb_forw -> qb_back =
			s -> s_qbuf.qb_back -> qb_forw = &s -> s_qbuf;
		s -> s_qlen = tx -> tx_cc;
	    }
	    return s;

	case SPDU_CN: 		/* category 1 SPDUs */
	case SPDU_AC: 
	case SPDU_RF: 
	case SPDU_FN: 
	case SPDU_DN: 
	case SPDU_NF: 
	case SPDU_AB: 
	case SPDU_AA: 
	case SPDU_GTC: 
	case SPDU_GTA: 
	case SPDU_PR: 
	    if (tx -> tx_cc <= 0) {
	simple: ;
		TXFREE (tx);
		return s;
	    }
	    (void) ssaplose (si, SC_PROTOCOL, NULLCP,
		    "session protocol mangled: not expecting user information after 0x%x (%d bytes)",
		    s -> s_code, tx -> tx_cc);
	    goto bad1;

	default:
	    (void) ssaplose (si, SC_PROTOCOL, NULLCP,
		    "session protocol mangled: not expecting 0x%x",
		    s -> s_code);
	    goto bad1;
    }

    sb -> sb_spdu = p = s;		/* save category 0 SPDU */

    if ((s = tsdu2spkt (&tx -> tx_qbuf, tx -> tx_cc, (cc = 0, &cc))) == NULL
	    || s -> s_errno != SC_ACCEPT) {
	(void) ssaplose (si, s ? s -> s_errno : SC_CONGEST, NULLCP, NULLCP);
bad2: 	;
	freespkt (s);
	freespkt (p);
	sb -> sb_spdu = NULL;
	TXFREE (tx);
	return NULL;
    }

    if (tx -> tx_expedited)
	s -> s_mask |= SMASK_SPDU_EXPD;
    tx -> tx_cc -= cc;

    switch ((p -> s_code) << 8 | s -> s_code) {
	case (SPDU_GT << 8) | SPDU_DT:	/* category 2 SPDUs with user data */
	    if (tx -> tx_qbuf.qb_forw != &tx -> tx_qbuf) {
		s -> s_qbuf = tx -> tx_qbuf;/* struct copy */
		s -> s_qbuf.qb_forw -> qb_back =
			s -> s_qbuf.qb_back -> qb_forw = &s -> s_qbuf;
		s -> s_qlen = tx -> tx_cc;
	    }
	    break;

	case (SPDU_GT << 8) | SPDU_MIP: /* category 2 SPDUs */
	case (SPDU_PT << 8) | SPDU_MIA: 
	case (SPDU_GT << 8) | SPDU_MAP: 
	case (SPDU_PT << 8) | SPDU_MAA: 
	case (SPDU_GT << 8) | SPDU_RS: 
	case (SPDU_PT << 8) | SPDU_RA: 
	case (SPDU_GT << 8) | SPDU_AS: 
	case (SPDU_GT << 8) | SPDU_AR: 
	case (SPDU_GT << 8) | SPDU_AD: 
	case (SPDU_PT << 8) | SPDU_ADA: 
	case (SPDU_GT << 8) | SPDU_AI:
	case (SPDU_PT << 8) | SPDU_AIA: 
#ifdef	notdef
	case (SPDU_GT << 8) | SPDU_AE: 	/* aka SPDU_MAP */
	case (SPDU_PT << 8) | SPDU_AEA: /* aka SPDU_MAA */
#endif
	case (SPDU_GT << 8) | SPDU_CD: 
	case (SPDU_PT << 8) | SPDU_CDA: 
	case (SPDU_PT << 8) | SPDU_ER: 
	case (SPDU_PT << 8) | SPDU_ED: 
	    if (tx -> tx_cc <= 0) {
		TXFREE (tx);
		break;
	    }
	    (void) ssaplose (si, SC_PROTOCOL, NULLCP,
		    "session protocol mangled: not expecting user information after 0x%x (%d bytes)",
		    s -> s_code, tx -> tx_cc);
		goto bad2;

	default: 
	    (void) ssaplose (si, SC_PROTOCOL, NULLCP,
		    "session protocol mangled: not expecting 0x%x to be concatenated after 0x%x",
		    s -> s_code, p -> s_code);
	    goto bad2;
    }

    switch (s -> s_code) {
	default:
	    if (p -> s_code == SPDU_GT) {
		if ((p -> s_mask & SMASK_GT_TOKEN) && p -> s_gt_token)
		    break;
	    }
	    else {
		if (((p -> s_mask & SMASK_PT_TOKEN) && p -> s_pt_token)
			|| p -> s_ulen)
		    break;
	    }			/* fall... */

	case SPDU_RS: 
	case SPDU_AD: 
	case SPDU_AI: 
	case SPDU_CD: 
	    freespkt (p);
	    sb -> sb_spdu = NULL;
	    break;
    }

    return s;
}

/* \f

 */

static int  TDATAser (sd, tx)
int     sd;
register struct TSAPdata   *tx;
{
    IFP	    abort;
    register struct ssapblk *sb;
    struct SSAPdata sxs;
    register struct SSAPdata   *sx = &sxs;
    struct SSAPindication   sis;
    register struct SSAPindication *si = &sis;
    register struct SSAPabort  *sa = &si -> si_abort;

    if ((sb = findsblk (sd)) == NULL)
	return;

    abort = sb -> sb_AbortIndication;

    for (;; tx = NULLTX) {
	switch (SReadRequestAux (sb, sx, OK, si, 1, tx)) {
	    case NOTOK: 
		(*abort) (sd, sa);
		return;

	    case OK: 
		(*sb -> sb_DataIndication) (sd, sx);
		break;

	    case DONE: 
		switch (si -> si_type) {
		    case SI_TOKEN: 
			(*sb -> sb_TokenIndication) (sd, &si -> si_token);
			break;

		    case SI_SYNC: 
			(*sb -> sb_SyncIndication) (sd, &si -> si_sync);
			break;

		    case SI_ACTIVITY: 
			(*sb -> sb_ActivityIndication) (sd, &si -> si_activity);
			break;

		    case SI_REPORT: 
			(*sb -> sb_ReportIndication) (sd, &si -> si_report);
			break;

		    case SI_FINISH: 
			(*sb -> sb_ReleaseIndication) (sd, &si -> si_finish);
			break;

		    case SI_DATA: /* partially assembled (T)SSDU */
			break;
		}
		break;
	}

	if (sb -> sb_spdu == NULL)
	    break;
    }
}

/* \f

 */

static int TDISCser (sd, td)
int	sd;
register struct TSAPdisconnect *td;
{
    IFP	    abort;
    register struct ssapblk *sb;
    struct SSAPindication sis;
    register struct SSAPindication *si = &sis;

    if ((sb = findsblk (sd)) == NULL)
	return;

    (void) ts2sslose (si, NULLCP, td);

    abort = sb -> sb_AbortIndication;

    sb -> sb_fd = NOTOK;
    (void) freesblk (sb);

    (*abort) (sd, &si -> si_abort);
}

/* \f

 */

int	ts2sslose (si, event, td)
register struct SSAPindication *si;
char   *event;
register struct TSAPdisconnect *td;
{
    int     reason;
    char   *cp,
            buffer[BUFSIZ];

    if (event)
	SLOG (ssap_log, LLOG_EXCEPTIONS, NULLCP,
	      (td -> td_cc > 0 ? "%s: %s [%*.*s]": "%s: %s", event,
	       TErrString (td -> td_reason), td -> td_cc, td -> td_cc,
	       td -> td_data));

    cp = "";
    switch (td -> td_reason) {
	case DR_REMOTE: 
	case DR_CONGEST: 
	    reason = SC_CONGEST;
	    break;

	case DR_SESSION: 
	case DR_ADDRESS: 
	    reason = SC_ADDRESS;
	    break;

	case DR_REFUSED:
	    reason = SC_REFUSED;
	    break;

	default: 
	    (void) sprintf (cp = buffer, " (%s at transport)",
		    TErrString (td -> td_reason));
	case DR_NETWORK:
	    reason = SC_TRANSPORT;
	    break;
    }

    if (td -> td_cc > 0)
	return ssaplose (si, reason, NULLCP, "%*.*s%s",
		td -> td_cc, td -> td_cc, td -> td_data, cp);
    else
	return ssaplose (si, reason, NULLCP, "%s", *cp ? cp + 1 : cp);
}

/* \f

   INTERNAL */

struct ssapblk  *newsblk () {
    register struct ssapblk *sb;

    sb = (struct ssapblk   *) calloc (1, sizeof *sb);
    if (sb == NULL)
	return NULL;

    sb -> sb_fd = NOTOK;
    sb -> sb_qbuf.qb_forw = sb -> sb_qbuf.qb_back = &sb -> sb_qbuf;
    sb -> sb_pr = SPDU_PR;

    if (once_only == 0) {
	SHead -> sb_forw = SHead -> sb_back = SHead;
	once_only++;
    }

    insque (sb, SHead -> sb_back);

    return sb;
}


int	freesblk (sb)
register struct ssapblk *sb;
{
    if (sb == NULL)
	return;

    if (sb -> sb_fd != NOTOK) {
	struct TSAPdata		txs;
	struct TSAPdisconnect   tds;

				/* SunLink says this is proper behavior...
				   using NOTOK, I've changed it to 10 secs */
	if (sb -> sb_flags & SB_FINN)
	    while (TReadRequest (sb -> sb_fd, &txs, 10, &tds) != NOTOK) {
		TXFREE (&txs);
	    }

	(void) TDiscRequest (sb -> sb_fd, NULLCP, 0, &tds);
    }

    if (sb -> sb_retry) {
	sb -> sb_retry -> s_mask &= ~SMASK_UDATA_PGI;
	sb -> sb_retry -> s_udata = NULL, sb -> sb_retry -> s_ulen = 0;
	freespkt (sb -> sb_retry);
    }

    if (sb -> sb_xspdu)
	freespkt (sb -> sb_xspdu);
    if (sb -> sb_spdu)
	freespkt (sb -> sb_spdu);

    QBFREE (&sb -> sb_qbuf);

    remque (sb);

    free ((char *) sb);
}

/* \f

 */

struct ssapblk   *findsblk (sd)
register int sd;
{
    register struct ssapblk *sb;

    if (once_only == 0)
	return NULL;

    for (sb = SHead -> sb_forw; sb != SHead; sb = sb -> sb_forw)
	if (sb -> sb_fd == sd)
	    return sb;

    return NULL;
}