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 i

⟦ddb6033ce⟧ TextFile

    Length: 76825 (0x12c19)
    Types: TextFile
    Names: »isoc.c«

Derivation

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

TextFile

/* isoc.c - "minimal" ISODE client for testing */

#ifndef	lint
static char *rcsid = "$Header: /f/osi/support/RCS/isoc.c,v 7.1 89/12/07 21:19:51 mrose Exp $";
#endif

/* 
 * $Header: /f/osi/support/RCS/isoc.c,v 7.1 89/12/07 21:19:51 mrose Exp $
 *
 *
 * $Log:	isoc.c,v $
 * Revision 7.1  89/12/07  21:19:51  mrose
 * stuff
 * 
 * Revision 7.0  89/11/23  22:27:15  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.
 *
 */


#include <stdio.h>
#include <varargs.h>
#include "rosap.h"
#include "rtsap.h"
#include "acsap.h"
#include "psap2.h"
#include "ssap.h"
#include "tsap.h"
#ifdef	TCP
#include "internet.h"
#ifdef	BSD42
#include <sys/ioctl.h>
#endif
#ifdef	SVR3
#include <fcntl.h>
#endif
#endif
#include "isoservent.h"
#include "tailor.h"
#include <sys/stat.h>

#undef	TIMER
#undef	TMS
#ifdef	BSD42
#define	TIMER
#endif
#ifdef	SYS5
#define	TIMER
#ifndef	HPUX
#include <sys/times.h>
#define	TMS
#endif
#endif

#if	!defined (TCP) || defined (FIONBIO) || defined (SVR3)
#define	ASYNC
#endif

/* \f

   DATA */

#define	ISN(req) \
    (req & (SR_MINORSYNC | SR_MAJORSYNC | SR_RESYNC | SR_ACTIVITY)) \
	? (long) ((getpid () % (SERIAL_MAX - SERIAL_MIN + 1)) + SERIAL_MIN) \
	: SERIAL_NONE

static enum { echo, sink, XXX } mode = XXX;

static int   testing_queued_writes = 0;

static char *isacs = NULL;
static int   isrts = 0;

static int   status = 0;

static char *myname = "isoc";


void	adios (), advise ();
void	ts_adios (), ts_advise ();
void	ss_adios (), ss_advise ();
void	ps_adios (), ps_advise ();
void	acs_adios (), acs_advise ();
void	rts_adios (), rts_advise ();
void	ros_adios (), ros_advise ();


long	lseek ();

/* \f

   MAIN */

#define	chkacs()	if (isacs) \
			    adios (NULLCP, "no association control for %s", \
					argv[2])

/* ARGSUSED */

main (argc, argv, envp)
int     argc;
char  **argv,
      **envp;
{
    register struct isoservent *is;

    if (myname = rindex (argv[0], '/'))
	myname++;
    if (myname == NULL || *myname == NULL)
	myname = argv[0];

    isodetailor (myname, 1);

    if (argc != 4)
	adios (NULLCP, "usage: %s host provider entity", myname);

#ifdef	TCP
    if (strcmp (argv[2], "raw") == 0) {
	raw_main (argv[3], argv[1]);
	exit (0);
    }
#endif

    if (index (argv[3], '/')) {
	mode = strcmp (isacs = argv[3], "isode/sink") ? echo : sink;
    }
    else {
	if ((is = getisoserventbyname (argv[3], argv[2])) == NULL)
	    adios (NULLCP, "%s/%s: unknown provider/entity pair",
		    argv[2], argv[3]);

	mode = strcmp (is -> is_entity, "sink") ? echo : sink;
    }

    if (strcmp (argv[2], "tsap") == 0) {
	chkacs ();
	ts_main (is, argv[1]);
    }
    else
	if (strcmp (argv[2], "ssap") == 0) {
	    chkacs ();
	    ss_main (is, argv[1]);
	}
	else
	    if (strcmp (argv[2], "psap") == 0)
		ps_main (is, argv[1]);
	    else
		if (strcmp (argv[2], "rtsap") == 0) {
		    isrts = 1;
		    rts_main (is, argv[1]);
		}
		else
		    if (strcmp (argv[2], "rosap") == 0)
			ros_main (is, argv[1]);
		    else
			adios (NULLCP, "unknown provider: \"%s\"", argv[2]);

    exit (status);		/* NOTREACHED */
}

/* \f

   RAW */

#ifdef	TCP
static int  raw_main (service, addr)
char   *service,
       *addr;
{
    int     sd,
            cc,
            i,
            j;
    char   *cp,
           *dp;
    register struct hostent *hp;
    register struct servent *sp;
    struct sockaddr_in  in_socket;
    register struct sockaddr_in *isock = &in_socket;
    struct stat st;

    if (strcmp (service, "sink"))
	adios (NULLCP, "only sink on raw tcp is supported");
    if ((sp = getservbyname (service, "tcp")) == NULL)
	adios (NULLCP, "%s/%s: unknown service", "tcp", service);
    if ((hp = gethostbystring (addr)) == NULL)
	adios (NULLCP, "%s: unknown host", addr);

    bzero ((char *) isock, sizeof *isock);
    isock -> sin_family = hp -> h_addrtype;
    isock -> sin_port = sp -> s_port;
    inaddr_copy (hp, isock);

    if ((sd = start_tcp_client ((struct sockaddr_in *) 0, 0)) == NOTOK)
	adios ("socket", "unable to start");
    fprintf (stderr, "%s... ", hp -> h_name);
    (void) fflush (stderr);
    if (join_tcp_server (sd, isock) == NOTOK) {
	fprintf (stderr, "failed\n");
	adios ("socket", "unable to connect");
    }
    fprintf (stderr, "connected\n");

    if (fstat (fileno (stdin), &st) == NOTOK
	    || (st.st_mode & S_IFMT) != S_IFREG
	    || (cc = st.st_size) == 0)
	adios (NULLCP, "standard input not a regular file");
    (void) lseek (fileno (stdin), 0L, 0);

    if ((cp = malloc ((unsigned) cc)) == NULL)
	adios (NULLCP, "no memory");
    for (dp = cp, j = cc; j > 0; dp += i, j -= i)
	switch (i = read (fileno (stdin), dp, j)) {
	    case NOTOK: 
		adios ("on stdin", "read failed");

	    case OK: 
		adios (NULLCP, "premature end-of-file");

	    default: 
		break;
	}

#ifdef	TIMER
    timer (0);
#endif
    if (write_tcp_socket (sd, cp, cc) != cc)
	adios ("writing", "error");
    (void) close_tcp_socket (sd);
#ifdef	TIMER
    timer (cc);
#endif
}
#endif

/* \f

   TSAP */

static int  ts_main (is, addr)
struct isoservent *is;
char   *addr;
{
    int     sd,
	    cc,
	    i,
	    j,
	    expedited,
	    expd;
    char   *cp,
	   *dp,
	    buffer[BUFSIZ];
    struct  TSAPaddr *ta;
    struct TSAPconnect  tcs;
    register struct TSAPconnect *tc = &tcs;
    struct TSAPdisconnect   tds;
    register struct TSAPdisconnect *td = &tds;
    struct stat st;

    if ((ta = is2taddr (addr, NULLCP, is)) == NULL)
	adios (NULLCP, "address translation failed");

    fprintf (stderr, "%s... ", addr);
    (void) fflush (stderr);
#ifndef	ASYNC
    if (TConnRequest (NULLTA, ta, 1, NULLCP, 0, NULLQOS, tc, td) == NOTOK) {
	fprintf (stderr, "failed\n");
	ts_adios (td, "T-CONNECT.REQUEST");
    }
    sd = tc -> tc_sd;
#else
    if ((i = TAsynConnRequest (NULLTA, ta, 1, NULLCP, 0, NULLQOS, tc, td, 1))
	    == NOTOK) {
	fprintf (stderr, "failed\n");
	ts_adios (td, "T-(ASYN-)CONNECT.REQUEST");
    }
    sd = tc -> tc_sd, cc = 0;
    while (i == CONNECTING_1 || i == CONNECTING_2) {
	int	nfds;
	fd_set	mask,
	       *rmask,
	       *wmask;

	nfds = 0;
	FD_ZERO (&mask);
	if (TSelectMask (sd, &mask, &nfds, td) == NOTOK) {
	    fprintf (stderr, "failed\n");
	    ts_adios (td, "T-(ASYN-)CONNECT.REQUEST(TSelectMask)");
	}
	rmask = (i == CONNECTING_2) ? &mask : NULLFD;
	wmask = (i == CONNECTING_2) ? NULLFD : &mask;

	fprintf (stderr, ".");
	(void) fflush (stderr);
	if (xselect (nfds, rmask, wmask, NULLFD, 1) == NOTOK) {
	    fprintf (stderr, "failed\n");
	    adios ("failed", "select");
	}

	if ((rmask && FD_ISSET (sd, rmask) == 0)
	        || (wmask && FD_ISSET (sd, wmask) == 0))
	    continue;
	    
	if ((i = TAsynRetryRequest (sd, tc, td)) == NOTOK) {
	    fprintf (stderr, "failed\n");
	    ts_adios (td, "T-ASYN-RETRY.REQUEST");
	}
    }
#endif
    fprintf (stderr, "connected\n");


    if (getenv ("TEST_QUEUED_WRITES")) {
	if (TSetQueuesOK (tc -> tc_sd, 1, td) == NOTOK)
	    ts_adios (td, "T-SET-QUEUES-OK");

	tsap_log -> ll_events |= LLOG_EXCEPTIONS;
	tsap_log -> ll_file = "-";
	(void) ll_close (tsap_log);

	testing_queued_writes = 1;
    }

    expd = tc -> tc_expedited;
#ifdef	DEBUG
    {
	advise (NULLCP, "responding TSAP address: %s",
		taddr2str (&tc -> tc_responding));

	if (tc -> tc_cc > 0)
	    advise (NULLCP, "greetings: %d octets", tc -> tc_cc);
    }
#endif

    if (fstat (fileno (stdin), &st) != NOTOK
	    && (st.st_mode & S_IFMT) == S_IFREG
	    && (cc = st.st_size) != 0) {
	(void) lseek (fileno (stdin), 0L, 0);

	if ((cp = malloc ((unsigned) cc)) == NULL)
	    adios (NULLCP, "no memory");
	for (dp = cp, j = cc; j > 0; dp += i, j -= i)
	    switch (i = read (fileno (stdin), dp, j)) {
		case NOTOK:
		    adios ("on stdin", "read failed");

		case OK:
		    adios (NULLCP, "premature end-of-file");
		    
		default:
		    break;
	    }
	for (i = 10; i > 0; i--) {
#ifdef	TIMER
	    timer (0);
#endif
	    ts_datarequest (sd, cp, cc, 0);
#ifdef	TIMER
	    timer (cc);
#endif
	}
	free (cp);
    }
    else
	for (expedited = 0;
		fgets (buffer, sizeof buffer, stdin);
		expedited = !expedited) {
	    if ((cc = strlen (buffer) + 1) > TX_SIZE && expedited)
		expedited = 0;

	    ts_datarequest (sd, buffer, cc, expd ? expedited : 0);
	}

    if (TDiscRequest (sd, NULLCP, 0, td) == NOTOK)
	ts_adios (td, "T-DISCONNECT.REQUEST");
}

/* \f

 */

static int  ts_datarequest (sd, data, cc, expedited)
int	sd;
char   *data;
int	cc,
	expedited;
{
    struct TSAPdata txs;
    register struct TSAPdata   *tx = &txs;
    struct TSAPdisconnect   tds;
    register struct TSAPdisconnect *td = &tds;

    if ((expedited ? TExpdRequest (sd, data, cc, td)
		: TDataRequest (sd, data, cc, td)) == NOTOK)
	if (expedited)
	    ts_adios (td, "T-EXPEDITED-DATA.REQUEST");
	else
	    ts_adios (td, "T-DATA.REQUEST");

    if (mode == echo) {
	if (testing_queued_writes) {
	    int	    vecp;
	    char   *vec[4];
	    fd_set  rfds;

	    FD_ZERO (&rfds);
	    FD_SET (sd, &rfds);
	    if (TNetAccept (&vecp, vec, sd + 1, &rfds, NULLFD, NULLFD, NOTOK,
			    td) == NOTOK)
		ts_adios (td, "T-NET-ACCEPT");
	}

	if (TReadRequest (sd, tx, NOTOK, td) == NOTOK)
	    ts_adios (td, "T-READ.REQUEST");
	if (cc != tx -> tx_cc) {
	    advise (NULLCP, "length mismatch, orig=%d echo=%d",
		    cc, tx -> tx_cc);
	    status++;
	}
	else
	    if (qcmp (data, &tx -> tx_qbuf, cc))
		status++;
	TXFREE (tx)
    }
}

/* \f

 */

static void  ts_adios (td, event)
register struct TSAPdisconnect *td;
char   *event;
{
    ts_advise (td, event);

    _exit (1);
}


static void  ts_advise (td, event)
register struct TSAPdisconnect *td;
char   *event;
{
    char    data[BUFSIZ];

    if (td -> td_cc > 0) {
	(void) sprintf (data, "[%s] %*.*s",
		TErrString (td -> td_reason),
		td -> td_cc, td -> td_cc, td -> td_data);
    }
    else
	(void) sprintf (data, "[%s]", TErrString (td -> td_reason));

    advise (NULLCP, "%s: %s", event, data);
}

/* \f

   SSAP */

static int requirements = SR_HALFDUPLEX | SR_DUPLEX | SR_EXPEDITED
		| SR_MINORSYNC | SR_MAJORSYNC | SR_RESYNC | SR_ACTIVITY
		| SR_NEGOTIATED | SR_CAPABILITY | SR_EXCEPTIONS | SR_TYPEDATA;

static int owned = 0;
static int avail = 0;

static long ssn;

static int nmodes;
static int datamodes[4];

static char userdata[1024];

/* \f

 */

static int  ss_main (is, addr)
struct isoservent *is;
char   *addr;
{
    int     sd,
	    cc,
	    i,
	    j,
	    k,
	    l,
	    tokens;
    char   *cp,
	   *dp,
	    buffer[BUFSIZ];
    struct SSAPactid    ids;
    register struct SSAPactid  *id = &ids;
    register struct SSAPaddr *sz;
    struct SSAPref  sfs;
    register struct SSAPref *sf;
    struct SSAPconnect  scs;
    register struct SSAPconnect *sc = &scs;
    struct SSAPrelease  srs;
    register struct SSAPrelease *sr = &srs;
    struct SSAPindication   sis;
    register struct SSAPindication *si = &sis;
    register struct SSAPabort  *sa = &si -> si_abort;
    struct stat st;

    bzero (userdata, sizeof userdata);

    if ((sz = is2saddr (addr, NULLCP, is)) == NULL)
	adios (NULLCP, "address translation failed");
    if ((sf = addr2ref (SLocalHostName ())) == NULL) {
	sf = &sfs;
	(void) bzero ((char *) sf, sizeof *sf);
    }

    tokens = 0;
#define dotoken(requires,shift,bit,type) \
{ \
    if (requirements & requires) \
	tokens |= ST_CALL_VALUE << shift; \
}
    dotokens ();
#undef	dotoken

    fprintf (stderr, "%s... ", addr);
    (void) fflush (stderr);
#ifndef	ASYNC
    if (SConnRequest (sf, NULLSA, sz, requirements, tokens, ISN (requirements),
	    userdata, sizeof userdata /*SS_SIZE*/, NULLQOS, sc, si) == NOTOK) {
	fprintf (stderr, "failed\n");
	ss_adios (sa, "S-CONNECT.REQUEST");
    }
    sd = sc -> sc_sd;
#else
    if ((i = SAsynConnRequest (sf, NULLSA, sz, requirements, tokens,
	    ISN (requirements), userdata, sizeof userdata /*SS_SIZE*/, NULLQOS,
	    sc, si, 1))
	  == NOTOK) {
	fprintf (stderr, "failed\n");
	ss_adios (sa, "S-(ASYN-)CONNECT.REQUEST");
    }
    sd = sc -> sc_sd, cc = 0;
    while (i == CONNECTING_1 || i == CONNECTING_2) {
	int	nfds;
	fd_set	mask,
	       *rmask,
	       *wmask;

	nfds = 0;
	FD_ZERO (&mask);
	if (SSelectMask (sd, &mask, &nfds, si) == NOTOK) {
	    fprintf (stderr, "failed\n");
	    ss_adios (sa, "S-(ASYN-)CONNECT.REQUEST(SSelectMask)");
	}
	rmask = (i == CONNECTING_2) ? &mask : NULLFD;
	wmask = (i == CONNECTING_2) ? NULLFD : &mask;

	fprintf (stderr, ".");
	if (xselect (nfds, rmask, wmask, NULLFD, 1) == NOTOK) {
	    fprintf (stderr, "failed\n");
	    adios ("failed", "select");
	}

	if ((rmask && FD_ISSET (sd, rmask) == 0)
	        || (wmask && FD_ISSET (sd, wmask) == 0))
	    continue;
	    
	if ((i = SAsynRetryRequest (sd, sc, si)) == NOTOK) {
	    fprintf (stderr, "failed\n");
	    ss_adios (sa, "S-ASYN-RETRY.REQUEST");
	}
    }
#endif

    if (sc -> sc_result != SC_ACCEPT) {
	fprintf (stderr, "failed\n");
	if (sc -> sc_cc > 0)
	    adios (NULLCP, "connection rejected: [%s] %*.*s",
			SErrString (sc -> sc_result),
			sc -> sc_cc, sc -> sc_cc, sc -> sc_data);
	else
	    adios (NULLCP, "connection rejected: [%s]",
			SErrString (sc -> sc_result));
    }
    fprintf (stderr, "connected\n");

#ifdef	DEBUG
    {
	advise (NULLCP, "responding SSAP address: %s",
		saddr2str (&sc -> sc_responding));

	if (sc -> sc_cc > 0)
	    advise (NULLCP, "greetings: %d octets", sc -> sc_cc);
    }
#endif
    requirements = sc -> sc_requirements;
    nmodes = 0;
    datamodes[nmodes++] = SX_NORMAL;
    if (requirements & SR_EXPEDITED)
	datamodes[nmodes++] = SX_EXPEDITED;
    if ((requirements & SR_CAPABILITY) && (requirements & SR_ACTIVITY))
	datamodes[nmodes++] = SX_CAPDIND;
    if (requirements & SR_TYPEDATA)
	datamodes[nmodes++] = SX_TYPED;

#define dotoken(requires,shift,bit,type) \
{ \
    if (requirements & requires) \
	switch (sc -> sc_settings & (ST_MASK << shift)) { \
	    case ST_CALL_VALUE: \
		adios (NULLCP, "%s token: choice", type); \
 \
	    case ST_INIT_VALUE: \
		owned |= bit, avail |= bit; \
		break; \
 \
	    case ST_RESP_VALUE: \
		avail |= bit; \
		break; \
 \
	    default: \
		adios (NULLCP, "%s token: reserved", type); \
	} \
}
	dotokens ();
#undef	dotoken

    if (requirements & SR_ACTIVITY) {
	(void) strcpy (id -> sd_data, mode == echo ? "echo" : "sink");
	id -> sd_len = strlen (id -> sd_data);
	if (SActStartRequest (sd, id, userdata, SV_SIZE, si) == NOTOK)
	    ss_adios (sa, "S-ACTIVITY-START.REQUEST");
    }

    if (fstat (fileno (stdin), &st) != NOTOK
	    && (st.st_mode & S_IFMT) == S_IFREG
	    && (cc = st.st_size) != 0) {
	(void) lseek (fileno (stdin), 0L, 0);

	if ((cp = malloc ((unsigned) cc)) == NULL)
	    adios (NULLCP, "no memory");
	for (dp = cp, j = cc; j > 0; dp += i, j -= i)
	    switch (i = read (fileno (stdin), dp, j)) {
		case NOTOK:
		    adios ("on stdin", "read failed");

		case OK:
		    adios (NULLCP, "premature end-of-file");
		    
		default:
		    break;
	    }
	for (i = 10; i > 0; i--) {
#ifdef	TIMER
	    timer (0);
#endif
	    ss_datarequest (sd, cp, cc, SX_NORMAL, 0);
#ifdef	TIMER
	    timer (cc);
#endif
	}
	free (cp);
    }
    else {
	for (j = l = 0; fgets (buffer, sizeof buffer, stdin); ) {
	    k = j >= nmodes ? SX_EXPEDITED : datamodes[j++ % nmodes];
	    if ((cc = strlen (buffer) + 1) > SX_EXSIZE && k == SX_EXPEDITED) {
		if ((k = datamodes[j++ % nmodes]) == SX_EXPEDITED)
		    k = datamodes[j++ % nmodes];
	    }

	    switch (k) {
		case SX_CAPDIND:
		    if (!(requirements & SR_RESYNC) || l++ & 0x01) {
			ss_waitfor (sd, ST_ACT_TOKEN);
			if (l & 0x03) {
			    if (SActIntrRequest (sd, SP_SEQUENCE, si)
				    == NOTOK)
				ss_adios (sa, "S-ACTIVITY-INTERRUPT.REQUEST");
			}
			else {
			    if (SActDiscRequest (sd, SP_SEQUENCE, si)
				    == NOTOK)
				ss_adios (sa, "S-ACTIVITY-DISCARD.REQUEST");
			}
			ss_waitfor (sd, -1);
			goto push_data;
		    }
		    if (!(requirements & SR_RESYNC))
			break;
		    tokens = 0;
#define dotoken(requires,shift,bit,type) \
{ \
		    if (requirements & requires) \
			tokens |= ST_CALL_VALUE << shift; \
}
		    dotokens ();
#undef	dotoken
		    if (SReSyncRequest (sd, SYNC_SET, ssn - 1, tokens,
				userdata, SN_SIZE, si) == NOTOK)
			ss_adios (sa, "S-RESYNCHRONIZE.REQUEST");
		    ss_waitfor (sd, -1);
		    break;

		case SX_EXPEDITED:
		    if (j >= nmodes)
			j = j % nmodes;/* fall... */
		    if (!(requirements & SR_EXPEDITED))
			k = SX_NORMAL;	/* fall... */
		default:
push_data: ;
		    ss_datarequest (sd, buffer, cc, k, 1);
		    if (k == SX_CAPDIND
			    && SActResumeRequest (sd, id, id, 
				    (long) (getpid () % (SERIAL_MAX - SERIAL_MIN + 1))
				    + SERIAL_MIN, sf, userdata, SV_SIZE, si)
				  == NOTOK)
			ss_adios (sa, "S-ACTIVITY-RESUME.REQUEST");
		    break;
	    }
	}

	if (requirements & SR_EXCEPTIONS) {
	    if (owned & ST_DAT_TOKEN)
		if (SGTokenRequest (sd, ST_DAT_TOKEN, si) == NOTOK)
		    ss_adios (sa, "S-TOKEN-GIVE.REQUEST");
		else
		    owned &= ~ST_DAT_TOKEN;
	    if (SUReportRequest (sd, SP_NOREASON, userdata, SP_SIZE, si)
		    == NOTOK)
		ss_adios (sa, "S-U-EXCEPTION-REPORT.REQUEST");
	    ss_waitfor (sd, -1);
	}    
    }

    if ((requirements & SR_MAJORSYNC) && !(requirements & SR_ACTIVITY)) {
	if (SMajSyncRequest (sd, &ssn, userdata, SN_SIZE, si) == NOTOK)
	    switch (sa -> sa_reason) {
		case SC_OPERATION:
		    ss_waitfor (sd, ST_DAT_TOKEN | ST_MIN_TOKEN
			    | ST_MAJ_TOKEN);
		    if (SMajSyncRequest (sd, &ssn, userdata, SN_SIZE, si)
			    == OK)
			break;	/* else fall */

		default:
		    ss_adios (sa, "S-MAJOR-SYNC.REQUEST");
	    }

	ss_waitfor (sd, -1);
    }

    if (requirements & SR_ACTIVITY) {
	if (SActEndRequest (sd, &ssn, userdata, SV_SIZE, si) == NOTOK)
	    switch (sa -> sa_reason) {
		case SC_OPERATION:
		    ss_waitfor (sd, avail);
		    if (SActEndRequest (sd, &ssn, userdata, SV_SIZE, si) == OK)
			break;	/* else fall */

		default:
		    ss_adios (sa, "S-ACTIVITY-END.REQUEST");
	    }

	ss_waitfor (sd, -1);
		
	if (SGControlRequest (sd, si) == NOTOK)
	    switch (sa -> sa_reason) {
		case SC_OPERATION:
		    ss_waitfor (sd, avail);
		    if (SGControlRequest (sd, si) == OK)
			break;	/* else fall */

		default:
		    ss_adios (sa, "S-CONTROL-GIVE.REQUEST");
	    }

	owned = 0;

	ss_waitfor (sd, -1);
    }

    if (SRelRequest (sd, userdata, SF_SIZE, NOTOK, sr, si) == NOTOK)
	switch (sa -> sa_reason) {
	    case SC_OPERATION: 
	    case SC_WAITING: 
		ss_waitfor (sd, avail);
		if (SRelRequest (sd, userdata, SF_SIZE, NOTOK, sr, si) == OK)
		    break;	/* else fall */

	    default: 
		ss_adios (sa, "S-RELEASE.REQUEST");
	}

	if (!sr -> sr_affirmative) {
	    (void) SUAbortRequest (sd, NULLCP, 0, si);

	    if (sr -> sr_cc > 0)
		adios (NULLCP, "release rejected by peer: %*.*s",
			sr -> sr_cc, sr -> sr_cc, sr -> sr_data);
	    else
		adios (NULLCP, "release rejected by peer");
	}
	SRFREE (sr);
}

/* \f

 */

static int ss_datarequest (sd, data, cc, dm, sync)
int	sd;
char   *data;
int	cc,
	dm,
	sync;
{
    int     result;
    struct SSAPdata sxs;
    register struct SSAPdata   *sx = &sxs;
    struct SSAPindication   sis;
    register struct SSAPindication *si = &sis;
    register struct SSAPabort  *sa = &si -> si_abort;

    switch (dm) {
	default: 
	    if (SDataRequest (sd, data, cc, si) == NOTOK)
		switch (sa -> sa_reason) {
		    case SC_OPERATION: 
			ss_waitfor (sd, ST_DAT_TOKEN);
			if (SDataRequest (sd, data, cc, si) == OK)
			    break;/* else fall */

		    default: 
			ss_adios (sa, "S-DATA.REQUEST");
		}
	    break;

	case SX_EXPEDITED: 
	    if (SExpdRequest (sd, data, cc, si) == NOTOK)
		ss_adios (sa, "S-EXPEDITED-DATA.REQUEST");
	    break;

	case SX_CAPDIND: 
	    if (SCapdRequest (sd, data, cc, si) == NOTOK)
		switch (sa -> sa_reason) {
		    case SC_OPERATION: 
			ss_waitfor (sd, avail & ~ST_RLS_TOKEN);
			if (SCapdRequest (sd, data, cc, si) == OK)
			    break;/* else fall */

		    default: 
			ss_adios (sa, "S-CAPABILITY-DATA.REQUEST");
		}
	    break;

	case SX_TYPED: 
	    if (STypedRequest (sd, data, cc, si) == NOTOK)
		ss_adios (sa, "S-TYPED-DATA.REQUEST");
	    break;
    }

    if (mode == echo || dm == SX_CAPDIND)
	for (;;) {
	    switch (result = SReadRequest (sd, sx, NOTOK, si)) {
		case NOTOK: 
		    ss_adios (sa, "S-READ.REQUEST");

		case OK: 
		    if ((dm != SX_CAPDIND ? dm : SX_CAPDCNF)
			    != sx -> sx_type) {
			advise (NULLCP,
				"data indication type mismatch, orig=%d echo=%d",
				dm, sx -> sx_type);
			status++;
		    }
		    if (cc != sx -> sx_cc) {
			advise (NULLCP, "length mismatch, orig=%d echo=%d",
				cc, sx -> sx_cc);
			status++;
		    }
		    else
			if (qcmp (data, &sx -> sx_qbuf, cc))
			    status++;
		    SXFREE (sx)
		    break;

		case DONE: 
		    ss_event (sd, si);
		    continue;

		default: 
		    adios (NULLCP, "unknown return from SReadRequest=%d",
			    result);
	    }
	    break;
	}

    if (sync &&
	    (requirements & SR_MINORSYNC) && !(requirements & SR_ACTIVITY)) {
	if (SMinSyncRequest (sd, SYNC_CONFIRM, &ssn, userdata, SN_SIZE, si)
		== NOTOK)
	    switch (sa -> sa_reason) {
		case SC_OPERATION: 
		    ss_waitfor (sd, ST_DAT_TOKEN | ST_MIN_TOKEN);
		    if (SMinSyncRequest (sd, SYNC_CONFIRM, &ssn, userdata,
			    SN_SIZE, si) == OK)
			break;	/* else fall */

		default: 
		    ss_adios (sa, "S-MINOR-SYNC.REQUEST");
	    }

	ss_waitfor (sd, -1);
    }
    else
	if (sync
		&& (requirements & SR_ACTIVITY)
		&& (requirements & SR_MAJORSYNC)
		&& dm == SX_NORMAL) {
	    if (SMajSyncRequest (sd, &ssn, userdata, SN_SIZE, si) == NOTOK)
		switch (sa -> sa_reason) {
		    case SC_OPERATION: 
			ss_waitfor (sd, ST_DAT_TOKEN | ST_MIN_TOKEN
				| ST_MAJ_TOKEN);
			if (SMajSyncRequest (sd, &ssn, userdata, SN_SIZE, si)
				== OK)
			    break;/* else fall */

		    default: 
			ss_adios (sa, "S-MAJOR-SYNC.REQUEST");
		}

	    ss_waitfor (sd, -1);
	}
}

/* \f

 */

static int  ss_waitfor (sd, want)
int	sd,
	want;
{
    int     result,
	    tokens;
    char    buffer[BUFSIZ];
    struct SSAPdata sxs;
    register struct SSAPdata   *sx = &sxs;
    struct SSAPindication   sis;
    register struct SSAPindication *si = &sis;
    register struct SSAPabort  *sa = &si -> si_abort;

    for (;;) {
	if (want == -1) {
	    want = avail;
	    goto read_it;
	}

	tokens = 0;
#define dotoken(requires,shift,bit,type) \
{ \
	if ((want & bit) && !(owned & bit)) \
	    tokens |= bit; \
}
	dotokens ();
#undef	dotoken
	if (tokens == 0)
	    return;

	if (SPTokenRequest (sd, tokens, userdata, ST_SIZE, si) == NOTOK)
	    ss_adios (sa, "S-TOKEN-PLEASE.REQUEST");

read_it: ;
	switch (result = SReadRequest (sd, sx, NOTOK, si)) {
	    case NOTOK: 
		ss_adios (sa, "S-READ.REQUEST");

	    case OK: 
		(void) strcpy (buffer, "protocol screw-up");
		if (SUAbortRequest (sd, buffer, strlen (buffer) + 1, si) == NOTOK)
		    ss_adios (sa, "S-U-ABORT.REQUEST");
		adios (NULLCP, "%s, data indication type=0x%x",
			buffer, sx -> sx_type);

	    case DONE: 
		ss_event (sd, si);
		break;

	    default: 
		adios (NULLCP, "unknown return from SReadRequest=%d",
			result);
	}
    }
}

/* \f

 */

static	ss_event (sd, si)
int	sd;
register struct SSAPindication *si;
{
    register struct SSAPabort  *sa = &si -> si_abort;
    register struct SSAPactivity *sv = &si -> si_activity;
    register struct SSAPfinish *sf = &si -> si_finish;
    register struct SSAPreport *sp = &si -> si_report;
    register struct SSAPsync   *sn = &si -> si_sync;
    register struct SSAPtoken  *st = &si -> si_token;

    switch (si -> si_type) {
	case SI_TOKEN: 
	    switch (st -> st_type) {
		case ST_GIVE: 
		case ST_CONTROL: 
		    owned = st -> st_owned;
		    break;

		case ST_PLEASE: 
		    if (SGTokenRequest (sd,
				(int) st -> st_tokens, si)
			    == NOTOK)
			ss_adios (sa, "S-TOKEN-GIVE.REQUEST");
		    else
			owned &= ~st -> st_tokens;
		    break;

		default: 
		    adios (NULLCP,
			    "unknown token indication type=0x%x, %d bytes",
			    st -> st_type, st -> st_cc);
	    }
	    STFREE (st);
	    break;

	case SI_SYNC: 
	    switch (sn -> sn_type) {
		case SN_MAJORIND: 
		    adios (NULLCP, "majorsync indication %d, %d bytes",
			    sn -> sn_ssn, sn -> sn_cc);
		    break;

		case SN_MAJORCNF: 
		    break;

		case SN_MINORIND: 
		    adios (NULLCP, "minorsync indication %d%s, %d bytes",
			    sn -> sn_ssn, sn -> sn_options == SYNC_CONFIRM
			    ? " (wants confirmation)" : NULLCP, sn -> sn_cc);
		    break;

		case SN_MINORCNF: 
		    break;

		case SN_RESETIND: 
#define	dotoken(requires,shift,bit,type) \
{ \
		    if (requirements & requires) \
			switch (sn -> sn_settings & (ST_MASK << shift)) { \
			    case ST_CALL_VALUE << shift: \
				sn -> sn_settings &= ~(ST_MASK << shift); \
				sn -> sn_settings |= ST_RESP_VALUE << shift; \
			    case ST_RESP_VALUE << shift: \
				owned &= ~bit; \
				break; \
 \
			    case ST_INIT_VALUE << shift: \
				owned |= bit; \
				break; \
 \
			    default: \
				adios (NULLCP, "%s token: reserved", type); \
				break; \
			} \
}
		    dotokens ();
#undef	dotoken
		    if (SReSyncResponse (sd, sn -> sn_ssn, sn -> sn_settings,
				userdata, SN_SIZE, si) == NOTOK)
			ss_adios (sa, "S-RESYNCHRONIZE.RESPONSE");
		    break;

		case SN_RESETCNF: 
		    break;

		default: 
		    adios (NULLCP,
			    "unknown sync indication=0x%x, ssn=%d, %d bytes",
			    sn -> sn_type, sn -> sn_ssn, sn -> sn_cc);
	    }
	    SNFREE (sn);
	    break;

	case SI_ACTIVITY: 
	    switch (sv -> sv_type) {
		case SV_START: 
		    adios (NULLCP,
			    "activity start indication: %*.*s, %d bytes",
			    sv -> sv_id.sd_len, sv -> sv_id.sd_len,
			    sv -> sv_id.sd_data, sv -> sv_cc);

		case SV_RESUME: 
		    adios (NULLCP, 
			    "activity resume indication: id=%*.*s oid=%*.*s connect=%s ssn=%d, %d bytes",
			    sv -> sv_id.sd_len, sv -> sv_id.sd_len,
			    sv -> sv_id.sd_data, sv -> sv_oid.sd_len,
			    sv -> sv_oid.sd_len, sv -> sv_oid.sd_data,
			    sprintref (&sv -> sv_connect), sv -> sv_ssn,
			    sv -> sv_cc);

		case SV_INTRIND: 
		    adios (NULLCP,
			    "activity interrupt indication %d, %d bytes",
			    sv -> sv_reason, sv -> sv_cc);

		case SV_INTRCNF: 
		    break;

		case SV_DISCIND: 
		    adios (NULLCP,
			    "activity discard indication %d, %d bytes",
			    sv -> sv_reason, sv -> sv_cc);

		case SV_DISCCNF: 
		    break;

		case SV_ENDIND: 
		    adios (NULLCP, "activity end indication %d, %d bytes",
			    sv -> sv_ssn, sv -> sv_cc);

		case SV_ENDCNF: 
		    break;

		default: 
		    adios (NULLCP,
			    "unknown activity indication=0x%x, %d bytes",
			    sv -> sv_type, sv -> sv_cc);
	    }
	    SVFREE (sv);
	    break;

	case SI_REPORT: 
	    if (requirements & SR_DAT_EXISTS) {
		if (SGTokenRequest (sd, ST_DAT_TOKEN, si) == NOTOK)
		    ss_adios (sa, "S-TOKEN-GIVE.REQUEST (to clear exception)");
		else
		    owned &= ~ST_DAT_TOKEN;
	    }
	    else
		if (SUAbortRequest (sd, NULLCP, 0, si) == NOTOK)
		    ss_adios (sa, "S-U-ABORT.REQUEST");
		else
		    adios (NULLCP, "aborted");
	    SPFREE (sp);
	    break;

	case SI_FINISH: 
	    if (SRelResponse (sd, SC_REJECTED, NULLCP, 0, si) == NOTOK)
		ss_adios (sa, "S-RELEASE.RESPONSE");
	    SFFREE (sf);
	    break;

	default: 
	    adios (NULLCP, "unknown indication type=0x%x",
		    si -> si_type);
    }
}

/* \f

 */

static void  ss_adios (sa, event)
register struct SSAPabort *sa;
char   *event;
{
    ss_advise (sa, event);

    _exit (1);
}


static void  ss_advise (sa, event)
register struct SSAPabort *sa;
char   *event;
{
    char    buffer[BUFSIZ];

    if (sa -> sa_cc > 0)
	(void) sprintf (buffer, "[%s] %*.*s",
		SErrString (sa -> sa_reason),
		sa -> sa_cc, sa -> sa_cc, sa -> sa_data);
    else
	(void) sprintf (buffer, "[%s]", SErrString (sa -> sa_reason));

    advise (NULLCP, "%s: %s%s", event, buffer,
	    sa -> sa_peer ? " (peer initiated)" : "");

    SAFREE (sa);
}

/* \f

   PSAP */

static int prequirements = 0;
#define	srequirements requirements

static int nctxs;
static int datactxs[NPCTX];

/* \f

 */

static int  ps_main (is, addr)
struct isoservent *is;
char   *addr;
{
    int     sd,
	    cc,
	    i,
	    j,
	    k,
	    l,
	    m,
	    tokens;
    char   *cp,
	   *dp,
	    buffer[BUFSIZ];
    register struct PSAPaddr   *pz;
    struct SSAPactid    ids;
    register struct SSAPactid  *id = &ids;
    struct SSAPref  sfs;
    register struct SSAPref *sf;
    struct PSAPconnect  pcs;
    register struct PSAPconnect *pc = &pcs;
    struct PSAPctxlist pls;
    register struct PSAPctxlist *pl = &pls;
    struct PSAPrelease  prs;
    register struct PSAPrelease *pr = &prs;
    struct PSAPindication   pis;
    register struct PSAPindication *pi = &pis;
    register struct PSAPabort  *pa = &pi -> pi_abort;
    struct AcSAPconnect  accs;
    register struct AcSAPconnect *acc = &accs;
    struct AcSAPrelease acrs;
    register struct AcSAPrelease *acr = &acrs;
    struct AcSAPindication   acis;
    register struct AcSAPindication *aci = &acis;
    register struct AcSAPabort  *aca = &aci -> aci_abort;
    register PE	    pe;
    PE	    udata[NPDATA];
    AEI	    aei;
    OID	    oid,
	    ode;
    struct stat st;

    if (isacs) {
	if ((aei = str2aei (addr, isacs)) == NULLAEI)
	    adios (NULLCP, "%s-%s: unknown application-entity", addr, isacs);
	if ((pz = aei2addr (aei)) == NULLPA)
	    adios (NULLCP, "address translation failed");

	cp = mode == echo ? "isode echo pci" : "isode sink pci";
	if ((ode = ode2oid (cp)) == NULLOID)
	    adios (NULLCP, "%s: unknown object descriptor", cp);
	ode = oid_cpy (ode);
    }
    else
	if ((pz = is2paddr (addr, NULLCP, is)) == NULL)
	    adios (NULLCP, "address translation failed");
    if ((sf = addr2ref (PLocalHostName ())) == NULL) {
	sf = &sfs;
	(void) bzero ((char *) sf, sizeof *sf);
    }

    tokens = 0;
#define dotoken(requires,shift,bit,type) \
{ \
    if (srequirements & requires) \
	tokens |= ST_CALL_VALUE << shift; \
}
    dotokens ();
#undef	dotoken

    for (i = (pl -> pc_nctx = NPCTX - (isacs ? 1 : 0)) - 1; i >= 0; i--) {
	pl -> pc_ctx[i].pc_id = i * 2 + 1;
	if ((oid = ode2oid ("iso asn.1 abstract syntax")) == NULLOID)
	    adios (NULLCP, "iso asn.1 abstract syntax: unknown");
	pl -> pc_ctx[i].pc_asn = oid_cpy (oid);
	pl -> pc_ctx[i].pc_atn = NULLOID;
    }

    for (i = 0; i < NPDATA; i++) {
	if ((pe = int2prim (i)) == NULLPE)
	    adios (NULLCP, "unable to allocate hello");
	pe -> pe_context = (i % pl -> pc_nctx) * 2 + 1;
	udata[i] = pe;
    }

    fprintf (stderr, "%s... ", addr);
    (void) fflush (stderr);
    if (isacs) {
#ifndef	ASYNC
	if (AcAssocRequest (ode, NULLAEI, aei, NULLPA, pz, pl, ode,
		prequirements, srequirements, ISN (srequirements), tokens, sf,
		udata, NACDATA, NULLQOS, acc, aci)
		== NOTOK) {
	    fprintf (stderr, "failed\n");
	    acs_adios (aca, "A-ASSOCIATE.REQUEST");
	}
	sd = acc -> acc_sd;
#else
	if ((i = AcAsynAssocRequest (ode, NULLAEI, aei, NULLPA, pz, pl, ode,
		prequirements, srequirements, ISN (srequirements), tokens, sf,
		udata, NACDATA, NULLQOS, acc, aci, 1)) == NOTOK) {
	    fprintf (stderr, "failed\n");
	    acs_adios (aca, "A-(ASYN-)ASSOCIATE.REQUEST");
	}
	sd = acc -> acc_sd, cc = 0;
	while (i == CONNECTING_1 || i == CONNECTING_2) {
	    int	    nfds;
	    fd_set  mask,
		   *rmask,
		   *wmask;

	    nfds = 0;
	    FD_ZERO (&mask);
	    if (PSelectMask (sd, &mask, &nfds, pi) == NOTOK) {
		fprintf (stderr, "failed\n");
		acs_adios (aca, "A-(ASYN-)ASSOCIATE.REQUEST(PSelectMask)");
	    }
	    rmask = (i == CONNECTING_2) ? &mask : NULLFD;
	    wmask = (i == CONNECTING_2) ? NULLFD : &mask;

	    fprintf (stderr, ".");
	    if (xselect (nfds, rmask, wmask, NULLFD, 1) == NOTOK) {
		fprintf (stderr, "failed\n");
		adios ("failed", "select");
	    }

	    if ((rmask && FD_ISSET (sd, rmask) == 0)
	            || (wmask && FD_ISSET (sd, wmask) == 0))
		continue;
	    
	    if ((i = AcAsynRetryRequest (sd, acc, aci)) == NOTOK) {
		fprintf (stderr, "failed\n");
		acs_adios (aca, "A-ASYN-RETRY.REQUEST");
	    }
	}
#endif

	if (acc -> acc_result != ACS_ACCEPT) {
	    fprintf (stderr, "failed\n");
	    adios (NULLCP, "connection rejected: [%s], %d elements",
		    AcErrString (acc -> acc_result), acc -> acc_ninfo);
	}
    }
    else {
#ifndef	ASYNC
	if (PConnRequest (NULLPA, pz, pl, NULLOID, prequirements,
		srequirements, srequirements
		    & (SR_MINORSYNC | SR_MAJORSYNC | SR_RESYNC | SR_ACTIVITY)
		? (long) (getpid () % (SERIAL_MAX - SERIAL_MIN + 1)) + SERIAL_MIN
		: SERIAL_NONE, tokens, sf, udata, NPDATA, NULLQOS, pc, pi) == NOTOK) {
	    fprintf (stderr, "failed\n");
	    ps_adios (pa, "P-CONNECT.REQUEST");
	}
	sd = pc -> pc_sd;
#else
	if ((i = PAsynConnRequest (NULLPA, pz, pl, NULLOID, prequirements,
		srequirements, ISN (srequirements), tokens, sf, udata, NPDATA,
		NULLQOS, pc, pi, 1)) == NOTOK) {
	    fprintf (stderr, "failed\n");
	    ps_adios (pa, "P-CONNECT.REQUEST");
	}
	sd = pc -> pc_sd, cc = 0;
	while (i == CONNECTING_1 || i == CONNECTING_2) {
	    int	    nfds;
	    fd_set  mask,
		   *rmask,
		   *wmask;

	    nfds = 0;
	    FD_ZERO (&mask);
	    if (PSelectMask (sd, &mask, &nfds, pi) == NOTOK) {
		fprintf (stderr, "failed\n");
		ps_adios (pa, "P-CONNECT.REQUEST(PSelectMask)");
	    }
	    rmask = (i == CONNECTING_2) ? &mask : NULLFD;
	    wmask = (i == CONNECTING_2) ? NULLFD : &mask;

	    fprintf (stderr, ".");
	    if (xselect (nfds, rmask, wmask, NULLFD, 1) == NOTOK) {
		fprintf (stderr, "failed\n");
		adios ("failed", "select");
	    }

	    if ((rmask && FD_ISSET (sd, rmask) == 0)
	            || (wmask && FD_ISSET (sd, wmask) == 0))
		continue;

	    if ((i = PAsynRetryRequest (sd, pc, pi)) == NOTOK) {
		fprintf (stderr, "failed\n");
		ps_adios (pa, "P-ASYN-RETRY.REQUEST");
	    }
	}
#endif

	if (pc -> pc_result != PC_ACCEPT) {
	    fprintf (stderr, "failed\n");
	    adios (NULLCP, "connection rejected: [%s], %d elements",
		    PErrString (pc -> pc_result), pc -> pc_ninfo);
	}
    }
    fprintf (stderr, "connected\n");

    if (isacs) {
	pc = &acc -> acc_connect;

#ifdef	DEBUG
	{
	    advise (NULLCP, "context: %s", oid2ode (acc -> acc_context));

	    advise (NULLCP,
		    "responding AE title: %s, responding PSAP address: %s",
		    sprintaei (&acc -> acc_respondtitle),
		    paddr2str (&pc -> pc_responding, NULLNA));

	    advise (NULLCP, "greetings: %d elements", acc -> acc_ninfo);

	    pl = &pc -> pc_ctxlist;
	    for (i = 0; i < pl -> pc_nctx; i++)
		advise (NULLCP, "ctx %d: 0x%x 0x%x %d",
			pl -> pc_ctx[i].pc_id, pl -> pc_ctx[i].pc_asn,
			pl -> pc_ctx[i].pc_atn, pl -> pc_ctx[i].pc_result);
	    advise (NULLCP, "default %d", pc -> pc_defctxresult);
	    advise (NULLCP, "p/s requirements 0x%x/0x%x",
		    pc -> pc_prequirements, pc -> pc_srequirements);
	}
#endif

	pl = &pc -> pc_ctxlist;

	if (mode == echo) {
	    if (acc -> acc_ninfo != NACDATA)
		adios (NULLCP, "expecting %d hellos, got %d elements",
			NACDATA, acc -> acc_ninfo);
	    for (i = 0; i < NACDATA; i++) {
		if ((pe = acc -> acc_info[i]) == NULLPE)
		    adios (NULLCP, "hello %d: NULL", i);
		if ((j = prim2num (pe)) == NOTOK
			&& pe -> pe_errno != PE_ERR_NONE)
		    adios (NULLCP, "hello %d: %s", i,
			    pe_error (pe -> pe_errno));
		if (j != i)
		    adios (NULLCP, "hello %d: value %d", i, j);
		if (pe -> pe_context != udata[i] -> pe_context)
		    adios (NULLCP, "hello %d: context of %d instead of %d",
			i, pe -> pe_context, udata[i] -> pe_context);
	    }
	}

	goto do_release;
    }
    else {
#ifdef	DEBUG
	{
	    advise (NULLCP, "responding PSAP address: %s",
		    paddr2str (&pc -> pc_responding, NULLNA));

	    advise (NULLCP, "greetings: %d elements", pc -> pc_ninfo);

	    pl = &pc -> pc_ctxlist;
	    for (i = 0; i < pl -> pc_nctx; i++)
		advise (NULLCP, "ctx %d: 0x%x 0x%x %d",
			pl -> pc_ctx[i].pc_id, pl -> pc_ctx[i].pc_asn,
			pl -> pc_ctx[i].pc_atn, pl -> pc_ctx[i].pc_result);
	    advise (NULLCP, "default %d", pc -> pc_defctxresult);
	    advise (NULLCP, "p/s requirements 0x%x/0x%x",
		    pc -> pc_prequirements, pc -> pc_srequirements);
	}
#endif

	if (mode == echo) {
	    if (pc -> pc_ninfo != NPDATA)
		adios (NULLCP, "expecting %d hellos, got %d elements",
			NPDATA, pc -> pc_ninfo);
	    for (i = 0; i < NPDATA; i++) {
		if ((pe = pc -> pc_info[i]) == NULLPE)
		    adios (NULLCP, "hello %d: NULL", i);
		if ((j = prim2num (pe)) == NOTOK
			&& pe -> pe_errno != PE_ERR_NONE)
		    adios (NULLCP, "hello %d: %s", i,
			    pe_error (pe -> pe_errno));
		if (j != i)
		    adios (NULLCP, "hello %d: value %d", i, j);
		if (pe -> pe_context != udata[i] -> pe_context)
		    adios (NULLCP, "hello %d: context of %d instead of %d",
			i, pe -> pe_context, udata[i] -> pe_context);
	    }
	}
    }

    nctxs = 0;
    pl = &pc -> pc_ctxlist;
    for (i = 0; i < pl -> pc_nctx; i++)
	if (pl -> pc_ctx[i].pc_result == PC_ACCEPT)
		datactxs[nctxs++] = pl -> pc_ctx[i].pc_id;

    srequirements = pc -> pc_srequirements;
    nmodes = 0;
    datamodes[nmodes++] = SX_NORMAL;
    if (srequirements & SR_EXPEDITED)
	datamodes[nmodes++] = SX_EXPEDITED;
    if ((srequirements & SR_CAPABILITY) && (srequirements & SR_ACTIVITY))
	datamodes[nmodes++] = SX_CAPDIND;
    if (srequirements & SR_TYPEDATA)
	datamodes[nmodes++] = SX_TYPED;

#define dotoken(requires,shift,bit,type) \
{ \
    if (srequirements & requires) \
	switch (pc -> pc_settings & (ST_MASK << shift)) { \
	    case ST_CALL_VALUE: \
		adios (NULLCP, "%s token: choice", type); \
 \
	    case ST_INIT_VALUE: \
		owned |= bit, avail |= bit; \
		break; \
 \
	    case ST_RESP_VALUE: \
		avail |= bit; \
		break; \
 \
	    default: \
		adios (NULLCP, "%s token: reserved", type); \
	} \
}
	dotokens ();
#undef	dotoken

    if (isacs)
	ACCFREE (acc)
    else
	PCFREE (pc);

    if (srequirements & SR_ACTIVITY) {
	(void) strcpy (id -> sd_data, mode == echo ? "echo" : "sink");
	id -> sd_len = strlen (id -> sd_data);
	if (PActStartRequest (sd, id, udata, NPDATA, pi) == NOTOK)
	    ps_adios (pa, "P-ACTIVITY-START.REQUEST");
    }

    if (fstat (fileno (stdin), &st) != NOTOK
	    && (st.st_mode & S_IFMT) == S_IFREG
	    && (cc = st.st_size) != 0) {
	(void) lseek (fileno (stdin), 0L, 0);

	if ((cp = malloc ((unsigned) cc)) == NULL)
	    adios (NULLCP, "no memory");
	for (dp = cp, j = cc; j > 0; dp += i, j -= i)
	    switch (i = read (fileno (stdin), dp, j)) {
		case NOTOK:
		    adios ("on stdin", "read failed");

		case OK:
		    adios (NULLCP, "premature end-of-file");
		    
		default:
		    break;
	    }
	if ((pe = oct2prim (cp, cc)) == NULLPE)
	    adios (NULLCP, "unable to allocate PSDU");
	free (cp);
	if (nctxs)
	    pe -> pe_context = datactxs[0];
	for (i = 10; i > 0; i--) {
#ifdef	TIMER
	    timer (0);
#endif
	    ps_datarequest (sd, pe, SX_NORMAL, 0);
#ifdef	TIMER
	    timer (cc);
#endif
	}
	pe_free (pe);
    }
    else {
	for (j = l = m = 0; fgets (buffer, sizeof buffer, stdin); ) {
	    k = j >= nmodes ? SX_EXPEDITED : datamodes[j++ % nmodes];
	    if ((cc = strlen (buffer) + 1) > SX_EXSIZE - 7
		    && k == SX_EXPEDITED) {
		if ((k = datamodes[j++ % nmodes]) == SX_EXPEDITED)
		    k = datamodes[j++ % nmodes];
	    }

	    switch (k) {
		case SX_CAPDIND:
		    if (!(requirements & SR_RESYNC) || l++ & 0x01) {
			ps_waitfor (sd, ST_ACT_TOKEN);
			if (l & 0x03) {
			    if (PActIntrRequest (sd, SP_SEQUENCE, pi)
				    == NOTOK)
				ps_adios (pa, "P-ACTIVITY-INTERRUPT.REQUEST");
			}
			else {
			    if (PActDiscRequest (sd, SP_SEQUENCE, pi)
				    == NOTOK)
				ps_adios (pa, "P-ACTIVITY-DISCARD.REQUEST");
			}
			ps_waitfor (sd, -1);
			goto push_data;
		    }
		    tokens = 0;
#define dotoken(requires,shift,bit,type) \
{ \
		    if (requirements & requires) \
			tokens |= ST_CALL_VALUE << shift; \
}
		    dotokens ();
#undef	dotoken
		    if (PReSyncRequest (sd, SYNC_SET, ssn - 1, tokens,
				udata, NPDATA, pi) == NOTOK)
			ps_adios (pa, "P-RESYNCHRONIZE.REQUEST");
		    ps_waitfor (sd, -1);
		    break;

		case SX_EXPEDITED:
		    if (j >= nmodes)
			j = j % nmodes;
		    if (!(srequirements & SR_EXPEDITED))
			k = SX_NORMAL;	/* fall... */
		default:
push_data: ;
		    if ((pe = oct2prim (buffer, cc)) == NULLPE)
			adios (NULLCP, "unable to allocate PSDU");
		    if (nctxs && k != SX_EXPEDITED)
			pe -> pe_context = datactxs[m++ % nctxs];
		    ps_datarequest (sd, pe, k, 1);
		    pe_free (pe);
		    if (k == SX_CAPDIND
			    && PActResumeRequest (sd, id, id, 
				    (long) (getpid () % (SERIAL_MAX - SERIAL_MIN + 1))
				    + SERIAL_MIN, sf, udata, NPDATA, pi)
				    == NOTOK)
			ps_adios (pa, "P-ACTIVITY-RESUME.REQUEST");
		    break;
	    }
	}

	if (requirements & SR_EXCEPTIONS) {
	    if (owned & ST_DAT_TOKEN)
		if (PGTokenRequest (sd, ST_DAT_TOKEN, pi) == NOTOK)
		    ps_adios (pa, "P-TOKEN-GIVE.REQUEST");
		else
		    owned &= ~ST_DAT_TOKEN;
	    if (PUReportRequest (sd, SP_NOREASON, udata, NPDATA, pi) == NOTOK)
		ps_adios (pa, "P-U-EXCEPTION-REPORT.REQUEST");
	    ps_waitfor (sd, -1);
	}    
    }

    if ((requirements & SR_MAJORSYNC) && !(requirements & SR_ACTIVITY)) {
	if (PMajSyncRequest (sd, &ssn, udata, NPDATA, pi) == NOTOK)
	    switch (pa -> pa_reason) {
		case PC_OPERATION:
		    ps_waitfor (sd, ST_DAT_TOKEN | ST_MIN_TOKEN
			    | ST_MAJ_TOKEN);
		    if (PMajSyncRequest (sd, &ssn, udata, NPDATA, pi) == OK)
			break;	/* else fall */

		default:
		    ps_adios (pa, "P-MAJOR-SYNC.REQUEST");
	    }

	ps_waitfor (sd, -1);
    }

    if (requirements & SR_ACTIVITY) {
	if (PActEndRequest (sd, &ssn, udata, NPDATA, pi) == NOTOK)
	    switch (pa -> pa_reason) {
		case PC_OPERATION:
		    ps_waitfor (sd, avail);
		    if (PActEndRequest (sd, &ssn, udata, NPDATA, pi) == OK)
			break;	/* else fall */

		default:
		    ps_adios (pa, "P-ACTIVITY-END.REQUEST");
	    }

	ps_waitfor (sd, -1);
		
	if (PGControlRequest (sd, pi) == NOTOK)
	    switch (pa -> pa_reason) {
		case PC_OPERATION:
		    ps_waitfor (sd, avail);
		    if (PGControlRequest (sd, pi) == OK)
			break;	/* else fall */

		default:
		    ps_adios (pa, "P-CONTROL-GIVE.REQUEST");
	    }

	owned = 0;

	ps_waitfor (sd, -1);
    }

do_release: ;
    if (isacs) {
	if (AcRelRequest (sd, ACF_NORMAL, udata, NACDATA, NOTOK, acr, aci)
		== NOTOK)
	    switch (aca -> aca_reason) {
		case ACS_OPERATION:
		    ps_waitfor (sd, avail);
		    if (AcRelRequest (sd, ACF_NORMAL, udata, NACDATA, NOTOK,
				      acr, aci) == OK)
			break;	/* else fall */

		default:
		    acs_adios (aca, "A-RELEASE.REQUEST");
	}

	if (!acr -> acr_affirmative) {
	    (void) AcUAbortRequest (sd, NULLPEP, 0, aci);
	    adios (NULLCP, "release rejected by peer: %d, %d elements",
			acr -> acr_reason, acr -> acr_ninfo);
	}

#ifdef	DEBUG
	advise (NULLCP, "A-RELEASE.CONFIRMATION: %d, %d elements",
		acr -> acr_reason, acr -> acr_ninfo);
#endif		
	ACRFREE (acr);
    }
    else {
	if (PRelRequest (sd, udata, NPDATA, NOTOK, pr, pi) == NOTOK)
	    switch (pa -> pa_reason) {
		case PC_OPERATION: 
		case PC_WAITING: 
		    ps_waitfor (sd, avail);
		    if (PRelRequest (sd, udata, NPDATA, NOTOK, pr, pi) == OK)
			break;	/* else fall */

		default: 
		    ps_adios (pa, "P-RELEASE.REQUEST");
	    }

	if (!pr -> pr_affirmative) {
	    (void) PUAbortRequest (sd, NULLPEP, 0, pi);
	    adios (NULLCP, "release rejected by peer: %d elements",
		    pr -> pr_ninfo);
	}
	PRFREE (pr);
    }

    for (i = 0; i < NPDATA; i++)
	pe_free (udata[i]);
}

/* \f

 */

static int ps_datarequest (sd, pe, dm, sync)
int	sd;
PE	pe;
int	dm,
	sync;
{
    int     result;
    struct PSAPdata pxs;
    register struct PSAPdata   *px = &pxs;
    struct PSAPindication   pis;
    register struct PSAPindication *pi = &pis;
    register struct PSAPabort  *pa = &pi -> pi_abort;

    switch (dm) {
	default: 
	    if (PDataRequest (sd, &pe, 1, pi) == NOTOK)
		switch (pa -> pa_reason) {
		    case PC_OPERATION: 
			ps_waitfor (sd, ST_DAT_TOKEN);
			if (PDataRequest (sd, &pe, 1, pi) == OK)
			    break;/* else fall */

		    default: 
			ps_adios (pa, "P-DATA.REQUEST");
		}
	    break;

	case SX_EXPEDITED: 
	    if (PExpdRequest (sd, &pe, 1, pi) == NOTOK)
		ps_adios (pa, "P-EXPEDITED-DATA.REQUEST");
	    break;

	case SX_CAPDIND: 
	    if (PCapdRequest (sd, &pe, 1, pi) == NOTOK)
		switch (pa -> pa_reason) {
		    case PC_OPERATION: 
			ps_waitfor (sd, avail & ~ST_RLS_TOKEN);
			if (PCapdRequest (sd, &pe, 1, pi) == OK)
			    break;/* else fall */

		    default: 
			ps_adios (pa, "P-CAPABILITY-DATA.REQUEST");
		}
	    break;

	case SX_TYPED: 
	    if (PTypedRequest (sd, &pe, 1, pi) == NOTOK)
		ps_adios (pa, "P-TYPED-DATA.REQUEST");
	    break;
    }

    if (mode == echo || dm == SX_CAPDIND)
	for (;;) {
	    switch (result = PReadRequest (sd, px, NOTOK, pi)) {
		case NOTOK: 
		    ps_adios (pa, "P-READ.REQUEST");

		case OK: 
		    if ((dm != SX_CAPDIND ? dm : SX_CAPDCNF)
			    != px -> px_type) {
			advise (NULLCP,
				"data indication type mismatch, orig=%d echo=%d",
				dm, px -> px_type);
			status++;
		    }
		    if (px -> px_ninfo != 1) {
			advise (NULLCP, "length mismatch, orig=%d echo=%d",
				1, px -> px_ninfo);
			status++;
		    }
		    if (pe -> pe_context != (*px -> px_info) -> pe_context) {
			advise (NULLCP, "context mismatch, orig=%d echo=%d",
				pe -> pe_context,
				(*px -> px_info) -> pe_context);
			status++;
		    }
		    if (pe_cmp (pe, *px -> px_info)) {
			advise (NULLCP, "data mismatch");
			status++;
		    }
		    PXFREE (px)
		    break;

		case DONE: 
		    ps_event (sd, pi);
		    continue;

		default: 
		    adios (NULLCP, "unknown return from PReadRequest=%d",
			    result);
	    }
	    break;
	}

    if (sync &&
	    (srequirements & SR_MINORSYNC) && !(srequirements & SR_ACTIVITY)) {
	if (PMinSyncRequest (sd, SYNC_CONFIRM, &ssn, NULLPEP, 0, pi) == NOTOK)
	    switch (pa -> pa_reason) {
		case PC_OPERATION: 
		    ps_waitfor (sd, ST_DAT_TOKEN | ST_MIN_TOKEN);
		    if (PMinSyncRequest (sd, SYNC_CONFIRM, &ssn, NULLPEP, 0,
			    pi) == OK)
			break;	/* else fall */

		default: 
		    ps_adios (pa, "P-MINOR-SYNC.REQUEST");
	    }

	ps_waitfor (sd, -1);
    }
    else
	if (sync
		&& (srequirements & SR_ACTIVITY)
		&& (srequirements & SR_MAJORSYNC)
		&& dm == SX_NORMAL) {
	    if (PMajSyncRequest (sd, &ssn, NULLPEP, 0, pi) == NOTOK)
		switch (pa -> pa_reason) {
		    case PC_OPERATION: 
			ps_waitfor (sd, ST_DAT_TOKEN | ST_MIN_TOKEN
				| ST_MAJ_TOKEN);
			if (PMajSyncRequest (sd, &ssn, NULLPEP, 0, pi) == OK)
			    break;/* else fall */

		    default: 
			ps_adios (pa, "P-MAJOR-SYNC.REQUEST");
		}

	    ps_waitfor (sd, -1);
	}
}

/* \f

 */

static int  ps_waitfor (sd, want)
int	sd,
	want;
{
    int     result,
	    tokens;
    struct PSAPdata pxs;
    register struct PSAPdata   *px = &pxs;
    struct PSAPindication   pis;
    register struct PSAPindication *pi = &pis;
    register struct PSAPabort  *pa = &pi -> pi_abort;

    for (;;) {
	if (want == -1) {
	    want = avail;
	    goto read_it;
	}

	tokens = 0;
#define dotoken(requires,shift,bit,type) \
{ \
	if ((want & bit) && !(owned & bit)) \
	    tokens |= bit; \
}
	dotokens ();
#undef	dotoken
	if (tokens == 0)
	    return;

	if (PPTokenRequest (sd, tokens, NULLPEP, 0, pi) == NOTOK)
	    ps_adios (pa, "P-TOKEN-PLEASE.REQUEST");

read_it: ;
	switch (result = PReadRequest (sd, px, NOTOK, pi)) {
	    case NOTOK: 
		ps_adios (pa, "P-READ.REQUEST");

	    case OK: 
		ps_abort (sd, "protocol screw-up");

	    case DONE: 
		ps_event (sd, pi);
		break;

	    default: 
		adios (NULLCP, "unknown return from PReadRequest=%d",
			result);
	}
    }
}

/* \f

 */

static	ps_event (sd, pi)
int	sd;
register struct PSAPindication *pi;
{
    register struct PSAPabort  *pa = &pi -> pi_abort;
    register struct PSAPactivity  *pv = &pi -> pi_activity;
    register struct PSAPfinish  *pf = &pi -> pi_finish;
    register struct PSAPreport  *pp = &pi -> pi_report;
    register struct PSAPsync   *pn = &pi -> pi_sync;
    register struct PSAPtoken  *pt = &pi -> pi_token;
    struct AcSAPindication  acis;
    register struct AcSAPindication *aci = &acis;
    register struct AcSAPabort *aca = &aci -> aci_abort;
    register struct AcSAPfinish *acf = &aci -> aci_finish;

    switch (pi -> pi_type) {
	case PI_TOKEN: 
	    switch (pt -> pt_type) {
		case ST_GIVE: 
		case ST_CONTROL: 
		    owned = pt -> pt_owned;
		    break;

		case ST_PLEASE: 
		    if (PGTokenRequest (sd,
				(int) pt -> pt_tokens, pi)
			    == NOTOK)
			ps_adios (pa, "P-TOKEN-GIVE.REQUEST");
		    else
			owned &= ~pt -> pt_tokens;
		    break;

		default: 
		    adios (NULLCP,
			    "unknown token indication type=0x%x",
			    pt -> pt_type);
	    }
	    PTFREE (pt);
	    break;

	case PI_SYNC: 
	    switch (pn -> pn_type) {
		case SN_MAJORIND: 
		    adios (NULLCP, "majorsync indication %d",
			    pn -> pn_ssn);
		    break;

		case SN_MAJORCNF: 
		    break;

		case SN_MINORIND: 
		    adios (NULLCP, "minorsync indication %d%s",
			    pn -> pn_ssn, pn -> pn_options == SYNC_CONFIRM
			    ? " (wants confirmation)" : NULLCP);
		    break;

		case SN_MINORCNF: 
		    break;

		case SN_RESETIND: 
#define	dotoken(requires,shift,bit,type) \
{ \
		    if (srequirements & requires) \
			switch (pn -> pn_settings & (ST_MASK << shift)) { \
			    case ST_CALL_VALUE << shift: \
				pn -> pn_settings &= ~(ST_MASK << shift); \
				pn -> pn_settings |= ST_RESP_VALUE << shift; \
			    case ST_RESP_VALUE << shift: \
				owned &= ~bit; \
				break; \
 \
			    case ST_INIT_VALUE << shift: \
				owned |= bit; \
				break; \
 \
			    default: \
				adios (NULLCP, "%s token: reserved", type); \
				break; \
			} \
}
		    dotokens ();
#undef	dotoken
		    if (PReSyncResponse (sd, pn -> pn_ssn, pn -> pn_settings,
				NULLPEP, 0, pi) == NOTOK)
			ps_adios (pa, "P-RESYNCHRONIZE.RESPONSE");
		    break;

		case SN_RESETCNF: 
		    break;

		default: 
		    adios (NULLCP, "unknown sync indication=0x%x, ssn=%d",
			    pn -> pn_type, pn -> pn_ssn);
	    }
	    PNFREE (pn);
	    break;

	case PI_ACTIVITY: 
	    switch (pv -> pv_type) {
		case SV_START: 
		    adios (NULLCP, "activity start indication: %*.*s",
			    pv -> pv_id.sd_len, pv -> pv_id.sd_len,
			    pv -> pv_id.sd_data);

		case SV_RESUME: 
		    adios (NULLCP, 
			    "activity resume indication: id=%*.*s oid=%*.*s connect=%s ssn=%d",
			    pv -> pv_id.sd_len, pv -> pv_id.sd_len,
			    pv -> pv_id.sd_data, pv -> pv_oid.sd_len,
			    pv -> pv_oid.sd_len, pv -> pv_oid.sd_data,
			    sprintref (&pv -> pv_connect), pv -> pv_ssn);

		case SV_INTRIND: 
		    adios (NULLCP, "activity interrupt indication %d",
			    pv -> pv_reason);

		case SV_INTRCNF: 
		    break;

		case SV_DISCIND: 
		    adios (NULLCP, "activity discard indication %d",
			    pv -> pv_reason);

		case SV_DISCCNF: 
		    break;

		case SV_ENDIND: 
		    adios (NULLCP, "activity end indication %d",
			    pv -> pv_ssn);

		case SV_ENDCNF: 
		    break;

		default: 
		    adios (NULLCP, "unknown activity indication=0x%x",
			    pv -> pv_type);
	    }
	    PVFREE (pv);
	    break;

	case PI_REPORT: 
	    advise (NULLCP, "%s report %d",
		    pp -> pp_peer ? "user" : "provider", pp -> pp_reason);
	    if (srequirements & SR_DAT_EXISTS) {
		if (PGTokenRequest (sd, ST_DAT_TOKEN, pi) == NOTOK)
		    ps_adios (pa, "P-TOKEN-GIVE.REQUEST (to clear exception)");
		else
		    owned &= ~ST_DAT_TOKEN;
	    }
	    else
		ps_abort (sd, "aborted");
	    PPFREE (pp);
	    break;

	case PI_FINISH: 
	    if (isacs) {
		if (AcFINISHser (sd, pf, aci) == NOTOK)
		    acs_adios (aca, "AcFINISHser");
#ifdef	DEBUG
		advise (NULLCP, "A-RELEASE.INDICATION %d, %d elements",
			acf -> acf_reason, acf -> acf_ninfo);
		if (AcRelResponse (sd, ACS_USER_NOREASON, ACR_NOTFINISHED,
				NULLPEP, 0, aci) == NOTOK)
		    acs_adios (aca, "A-RELEASE.RESPONSE");
#endif

		ACFFREE (acf);
	    }
	    else {
		if (PRelResponse (sd, PC_REJECTED, NULLPEP, 0, pi) == NOTOK)
		    ps_adios (pa, "P-RELEASE.RESPONSE");

		PFFREE (pf);
	    }
	    break;

	default: 
	    adios (NULLCP, "unknown indication type=0x%x", pi -> pi_type);
    }
}

/* \f

 */

static  ps_abort (sd, reason)
int     sd;
char   *reason;
{
    struct PSAPindication   pis;
    register struct PSAPindication *pi = &pis;
    register struct PSAPabort  *pa = &pi -> pi_abort;
    struct AcSAPindication  acis;
    register struct AcSAPindication *aci = &acis;
    register struct AcSAPabort *aca = &aci -> aci_abort;

    if (isacs) {
	if (AcUAbortRequest (sd, NULLPEP, 0, aci) == NOTOK)
	    acs_adios (aca, "A-U-ABORT.REQUEST");
    }
    else {
	if (PUAbortRequest (sd, NULLPEP, 0, pi) == NOTOK)
	    ps_adios (pa, "P-U-ABORT.REQUEST");
    }

    adios (NULLCP, "%s", reason);
}

/* \f

 */

static void  ps_adios (pa, event)
register struct PSAPabort *pa;
char   *event;
{
    ps_advise (pa, event);

    _exit (1);
}


static void  ps_advise (pa, event)
register struct PSAPabort *pa;
char   *event;
{
    char    buffer[BUFSIZ];

    if (pa -> pa_cc > 0)
	(void) sprintf (buffer, "[%s] %*.*s",
		PErrString (pa -> pa_reason),
		pa -> pa_cc, pa -> pa_cc, pa -> pa_data);
    else
	(void) sprintf (buffer, "[%s]", PErrString (pa -> pa_reason));

    advise (NULLCP, "%s: %s%s", event, buffer,
	    pa -> pa_peer ? " (peer initiated)" : "");
}

/* \f

   AcSAP */

static void  acs_adios (aca, event)
register struct AcSAPabort *aca;
char   *event;
{
    acs_advise (aca, event);

    _exit (1);
}


static void  acs_advise (aca, event)
register struct AcSAPabort *aca;
char   *event;
{
    char    buffer[BUFSIZ];

    if (aca -> aca_cc > 0)
	(void) sprintf (buffer, "[%s] %*.*s",
		AcErrString (aca -> aca_reason),
		aca -> aca_cc, aca -> aca_cc, aca -> aca_data);
    else
	(void) sprintf (buffer, "[%s]", AcErrString (aca -> aca_reason));

	advise (NULLCP, "%s: %s (source %d)", event, buffer,
		aca -> aca_source);
}

/* \f

   RtSAP */

static int turn = 0;

/* \f

 */

static int  rts_main (is, addr)
struct isoservent *is;
char *addr;
{
    int     sd,
            cc,
            i,
            j,
	    ros;
    char   *cp,
           *dp,
            buffer[BUFSIZ];
    register struct PSAPaddr   *pa;
    struct PSAPctxlist pls;
    register struct PSAPctxlist *pl = &pls;
    struct AcSAPrelease acrs;
    register struct AcSAPrelease *acr = &acrs;
    struct RtSAPaddr    rtzs;
    register struct RtSAPaddr  *rtz = &rtzs;
    struct RtSAPconnect rtcs;
    register struct RtSAPconnect   *rtc = &rtcs;
    struct RtSAPindication  rtis;
    register struct RtSAPindication *rti = &rtis;
    register struct RtSAPabort   *rta = &rti -> rti_abort;
#ifdef	DEBUG
    struct AcSAPconnect *acc= &rtc -> rtc_connect;
    struct PSAPconnect *pc = &acc -> acc_connect;
#endif
    register PE	    pe;
    AEI	    aei;
    OID	    oid,
	    ode;
    struct stat st;

    if ((pe = int2prim (i = getpid ())) == NULLPE)
	adios (NULLCP, "unable to allocate hello");

    turn = mode == sink;

    if (isacs) {
	if (ros = strncmp (isacs, "isode/ros_", strlen ("isode/ros_")) == 0)
	    mode = strcmp (isacs, "isode/ros_sink") ? echo : sink;
	else
	    mode = strcmp (isacs, "isode/rtse sink") ? echo : sink;
	turn = mode == sink;

	if ((aei = str2aei (addr, isacs)) == NULLAEI)
	    adios (NULLCP, "%s-%s: unknown application-entity", addr, isacs);
	if ((pa = aei2addr (aei)) == NULLPA)
	    adios (NULLCP, "address translation failed");

	cp = mode == echo ? "isode echo pci" : "isode sink pci";
	if ((ode = ode2oid (cp)) == NULLOID)
	    adios (NULLCP, "%s: unknown object descriptor", cp);
	ode = oid_cpy (ode);

	for (j = (pl -> pc_nctx = NPCTX - 2) - 1; j >= 0; j--) {
	    pl -> pc_ctx[j].pc_id = j * 2 + 1;
	    if ((oid = ode2oid ("iso asn.1 abstract syntax")) == NULLOID)
		adios (NULLCP, "iso asn.1 abstract syntax: unknown");
	    pl -> pc_ctx[j].pc_asn = oid_cpy (oid);
	    pl -> pc_ctx[j].pc_atn = NULLOID;
	}

	fprintf (stderr, "%s... ", addr);
	(void) fflush (stderr);
	if (RtOpenRequest (RTS_TWA, turn ? RTS_INITIATOR : RTS_RESPONDER,
		    ode, NULLAEI, aei, NULLPA, pa, pl, ode, pe,
		    NULLQOS, rtc, rti) == NOTOK) {
	    fprintf (stderr, "failed\n");
	    rts_adios (rta, "RT-OPEN.REQUEST");
	}
    }
    else {
	register struct SSAPaddr *sa;

	if (ros = strncmp (is -> is_entity, "ros_", strlen ("ros_")) == 0) {
	    mode = strcmp (is -> is_entity, "ros_sink") ? echo : sink;
	    turn = mode == sink;
	}

	rtz -> rta_port = is -> is_port;	/* yikes! */
	if ((is = getisoserventbyname ("rts", "ssap")) == NULL)
	    adios (NULLCP, "ssap/rts: unknown entity");
	if ((sa = is2saddr (addr, NULLCP, is)) == NULLSA)
	    adios (NULLCP, "address translation failed");
	rtz -> rta_addr = *sa;	/* struct copy */

	fprintf (stderr, "%s... ", addr);
	(void) fflush (stderr);
	if (RtBeginRequest (rtz, RTS_TWA, turn ? RTS_INITIATOR : RTS_RESPONDER,
		    pe, rtc, rti) == NOTOK) {
	    fprintf (stderr, "failed\n");
	    rts_adios (rta, "RT-BEGIN.REQUEST");
	}
    }

    pe_free (pe);

    if (rtc -> rtc_result != RTS_ACCEPT) {
	fprintf (stderr, "failed\n");
	adios (NULLCP, "association rejected: [%s]",
		RtErrString (rtc -> rtc_result));
    }
    fprintf (stderr, "connected\n");

#ifdef	DEBUG
    advise (NULLCP, "sent greetings of %d", i);
#endif

    sd = rtc -> rtc_sd;
    if (rtc -> rtc_data) {
	if ((i = prim2num (rtc -> rtc_data)) == NOTOK
		&& rtc -> rtc_data -> pe_errno != PE_ERR_NONE)
	    adios (NULLCP, "error decoding hello: %s (%d)",
		    pe_error (rtc -> rtc_data -> pe_errno), i);
#ifdef	DEBUG
	advise (NULLCP, "received greetings of %d", i);
#endif
    }

#ifdef	DEBUG
    if (isacs) {
	    advise (NULLCP, "context: %s", oid2ode (acc -> acc_context));

	    advise (NULLCP,
		    "responding AE title: %s, responding PSAP address: %s",
		    sprintaei (&acc -> acc_respondtitle),
		    paddr2str (&pc -> pc_responding, NULLNA));

	    advise (NULLCP, "greetings: %d elements", acc -> acc_ninfo);

	    pl = &pc -> pc_ctxlist;
	    for (i = 0; i < pl -> pc_nctx; i++)
		advise (NULLCP, "ctx %d: 0x%x 0x%x %d",
			pl -> pc_ctx[i].pc_id, pl -> pc_ctx[i].pc_asn,
			pl -> pc_ctx[i].pc_atn, pl -> pc_ctx[i].pc_result);
	    advise (NULLCP, "default %d", pc -> pc_defctxresult);
	    advise (NULLCP, "p/s requirements 0x%x/0x%x",
		    pc -> pc_prequirements, pc -> pc_srequirements);
	}
#endif

    RTCFREE (rtc);

    if (ros) {
	struct RoSAPindication rois;
	register struct RoSAPpreject *rop = &rois.roi_preject;

	if (RoSetService (sd, RoRtService, &rois) == NOTOK)
	    ros_adios (rop, "set RO/PT fails");

	do_ros (sd);
	return;
    }

    if (fstat (fileno (stdin), &st) != NOTOK
	    && (st.st_mode & S_IFMT) == S_IFREG
	    && (cc = st.st_size) != 0) {
	(void) lseek (fileno (stdin), 0L, 0);

	if ((cp = malloc ((unsigned) cc)) == NULL)
	    adios (NULLCP, "no memory");
	for (dp = cp, j = cc; j > 0; dp += i, j -= i)
	    switch (i = read (fileno (stdin), dp, j)) {
		case NOTOK: 
		    adios ("on stdin", "read failed");

		case OK: 
		    adios (NULLCP, "premature end-of-file");

		default: 
		    break;
	    }
	if ((pe = oct2prim (cp, cc)) == NULLPE)
	    adios (NULLCP, "unable to allocate APDU");
	free (cp);
	for (i = 10; i > 0; i--) {
#ifdef	TIMER
	    timer (0);
#endif
	    rts_transferequest (sd, pe);
#ifdef	TIMER
	    timer (cc);
#endif
	}
	pe_free (pe);
    }
    else
	while (fgets (buffer, sizeof buffer, stdin)) {
	    if ((pe = oct2prim (buffer, strlen (buffer) + 1)) == NULLPE)
		adios (NULLCP, "unable to allocate APDU");
	    rts_transferequest (sd, pe);
	    pe_free (pe);
	}

    if (isacs) {
	if ((pe = int2prim (i = getpid ())) == NULLPE)
	    adios (NULLCP, "unable to allocate hello");

	if (RtCloseRequest (sd, ACF_NORMAL, pe, acr, rti) == NOTOK)
	    switch (rta -> rta_reason) {
		case RTS_OPERATION: 
		case RTS_WAITING: 
		    rts_waitfor (sd);
		    if (RtCloseRequest (sd, ACF_NORMAL, pe, acr, rti)
			    == OK)
			break;	/* else fall */

		default: 
		    rts_adios (rta, "RT-CLOSE.REQUEST");
	    }

	if (!acr -> acr_affirmative) {
	    (void) RtUAbortRequest (sd, NULLPE, rti);
	    adios (NULLCP, "release rejected by peer: %d, %d elements",
			acr -> acr_reason, acr -> acr_ninfo);
	}

	if (mode == echo) {
	    if (acr -> acr_ninfo != 1)
		advise (NULLCP, "got %d elements returned on close",
			    acr -> acr_ninfo);
		if (pe_cmp (pe, acr -> acr_info[0])) {
		    advise (NULLCP, "data mismatch");
		    status++;
		}
	}

	ACRFREE (acr);

	pe_free (pe);
    }
    else
	if (RtEndRequest (sd, rti) == NOTOK)
	    switch (rta -> rta_reason) {
		case RTS_OPERATION: 
		case RTS_WAITING: 
		    rts_waitfor (sd);
		    if (RtEndRequest (sd, rti) == OK)
			break;	/* else fall */

		default: 
		    rts_adios (rta, "RT-END.REQUEST");
	    }
}

/* \f

 */

static int  rts_transferequest (sd, pe)
int	sd;
register PE	pe;
{
    int     result;
    struct RtSAPindication  rtis;
    register struct RtSAPindication *rti = &rtis;
    register struct RtSAPabort *rta = &rti -> rti_abort;
    register struct RtSAPtransfer  *rtt = &rti -> rti_transfer;

    if (RtTransferRequest (sd, pe, NOTOK, rti) == NOTOK)
	switch (rta -> rta_reason) {
	    case RTS_OPERATION: 
		rts_waitfor (sd);
		if (RtTransferRequest (sd, pe, NOTOK, rti) == OK)
		    break;	/* else fall */

	    default: 
		if (RTS_FATAL (rta -> rta_reason))
		    rts_adios (rta, "RT-TRANSFER.REQUEST");
		rts_advise (rta, "RT-TRANSFER.REQUEST");
		return;
	}

    if (mode == echo)
	for (;;)
	    switch (result = RtWaitRequest (sd, NOTOK, rti)) {
		case NOTOK: 
		    rts_adios (rta, "RT-WAIT.REQUEST");

		case OK: 
		    if (pe_cmp (pe, rtt -> rtt_data)) {
			advise (NULLCP, "data mismatch");
			status++;
		    }
		    RTTFREE (rtt);
		    return;

		case DONE: 
		    rts_event (sd, rti);
		    break;

		default: 
		    adios (NULLCP, "unknown return from RtWaitRequest=%d",
			    result);
	    }
}

/* \f

 */

static int  rts_waitfor (sd)
int	sd;
{
    int     result;
    struct RtSAPindication  rtis;
    register struct RtSAPindication *rti = &rtis;
    register struct RtSAPabort *rta = &rti -> rti_abort;
    static int  priority = 1;

    if (turn)
	return;

    if (RtPTurnRequest (sd, priority++, rti) == NOTOK)
	rts_adios (rta, "RT-TURN-PLEASE.REQUEST");

    while (!turn)
	switch (result = RtWaitRequest (sd, NOTOK, rti)) {
	    case NOTOK: 
		rts_adios (rta, "RT-WAIT.REQUEST");

	    case OK: 
		adios (NULLCP, "protocol screw-up");

	    case DONE: 
		rts_event (sd, rti);
		break;

	    default: 
		adios (NULLCP, "unknown return from RtWaitRequest=%d",
			result);
	}
}

/* \f

 */

static int  rts_event (sd, rti)
int	sd;
register struct RtSAPindication *rti;
{
    register struct RtSAPabort *rta = &rti -> rti_abort;
    register struct RtSAPturn  *rtu = &rti -> rti_turn;

    switch (rti -> rti_type) {
	case RTI_TURN: 
	    if (rtu -> rtu_please) {
		if (RtGTurnRequest (sd, rti) == NOTOK)
		    rts_adios (rta, "RT-TURN-GIVE.REQUEST");
		turn = 0;
	    }
	    else
		turn = 1;
	    break;

	case RTI_CLOSE: 
	    adios (NULLCP, "got RT-END.INDICATION");

	case RTI_FINISH:
	    adios (NULLCP, "got RT-CLOSE.INDICATION");

	default: 
	    adios (NULLCP, "unknown indication type=0x%x",
		    rti -> rti_type);
    }
}

/* \f

 */

static void  rts_adios (rta, event)
register struct RtSAPabort *rta;
char   *event;
{
    rts_advise (rta, event);

    _exit (1);
}


static void  rts_advise (rta, event)
register struct RtSAPabort *rta;
char   *event;
{
    char    buffer[BUFSIZ];

    if (rta -> rta_cc > 0)
	(void) sprintf (buffer, "[%s] %*.*s", RtErrString (rta -> rta_reason),
		rta -> rta_cc, rta -> rta_cc, rta -> rta_data);
    else
	(void) sprintf (buffer, "[%s]", RtErrString (rta -> rta_reason));

    advise (NULLCP, "%s: %s", event, buffer);
}

/* \f

   RoSAP */

static int  ros_main (is, addr)
struct isoservent *is;
char *addr;
{
    int     sd,
            i;
    char   *cp;
    struct SSAPref  sfs;
    register struct SSAPref *sf;
    register struct PSAPaddr   *pa;
    struct AcSAPconnect accs;
    register struct AcSAPconnect   *acc = &accs;
    struct AcSAPindication  acis;
    register struct AcSAPindication *aci = &acis;
    register struct AcSAPabort *aca = &aci -> aci_abort;
    struct RoSAPaddr    roas;
    register struct RoSAPaddr  *roa = &roas;
    struct RoSAPconnect rocs;
    register struct RoSAPconnect   *roc = &rocs;
    struct RoSAPindication  rois;
    register struct RoSAPindication *roi = &rois;
    register struct RoSAPpreject   *rop = &roi -> roi_preject;
#ifdef	DEBUG
    struct PSAPconnect *pc = &acc -> acc_connect;
#endif
    struct PSAPctxlist pls;
    register struct PSAPctxlist *pl = &pls;
    AEI	    aei;
    OID     oid,
	    ode;
    register PE	    pe;

    if (isacs) {
	if ((aei = str2aei (addr, isacs)) == NULLAEI)
	    adios (NULLCP, "%s-%s: unknown application-entity", addr, isacs);
	if ((pa = aei2addr (aei)) == NULLPA)
	    adios (NULLCP, "address translation failed");

	cp = mode == echo ? "isode echo pci" : "isode sink pci";
	if ((ode = ode2oid (cp)) == NULLOID)
	    adios (NULLCP, "%s: unknown object descriptor", cp);
	ode = oid_cpy (ode);

	if ((sf = addr2ref (PLocalHostName ())) == NULL) {
	    sf = &sfs;
	    (void) bzero ((char *) sf, sizeof *sf);
	}
	pl -> pc_nctx = 1;
	pl -> pc_ctx[0].pc_id = 1;
	if ((oid = ode2oid ("iso asn.1 abstract syntax")) == NULLOID)
	    adios (NULLCP, "iso asn.1 abstract syntax: unknown");
	pl -> pc_ctx[0].pc_asn = oid_cpy (oid);
	pl -> pc_ctx[0].pc_atn = NULLOID;

	fprintf (stderr, "%s... ", addr);
	(void) fflush (stderr);
	if (AcAssocRequest (ode, NULLAEI, aei, NULLPA, pa, pl, ode,
		    0, ROS_MYREQUIRE, SERIAL_NONE, 0, sf, NULLPEP, 0, NULLQOS,
		    acc, aci)
		== NOTOK)
	    acs_adios (aca, "A-ASSOCIATE.REQUEST");

	if (acc -> acc_result != ACS_ACCEPT) {
	    fprintf (stderr, "failed\n");
	    adios (NULLCP, "association rejected: [%s]",
		    AcErrString (acc -> acc_result));
	}
	fprintf (stderr, "connected\n");

	sd = acc -> acc_sd;

#ifdef	DEBUG
	pa = &pc -> pc_responding;
	{
	    advise (NULLCP, "context: %s", oid2ode (acc -> acc_context));

	    advise (NULLCP,
		    "responding AE title: %s, responding PSAP address: %s",
		    sprintaei (&acc -> acc_respondtitle),
		    paddr2str (&pc -> pc_responding, NULLNA));

	    advise (NULLCP, "greetings: %d elements", acc -> acc_ninfo);

	    pl = &pc -> pc_ctxlist;
	    for (i = 0; i < pl -> pc_nctx; i++)
		advise (NULLCP, "ctx %d: 0x%x 0x%x %d",
			pl -> pc_ctx[i].pc_id, pl -> pc_ctx[i].pc_asn,
			pl -> pc_ctx[i].pc_atn, pl -> pc_ctx[i].pc_result);
	    advise (NULLCP, "default %d", pc -> pc_defctxresult);
	    advise (NULLCP, "p/s requirements 0x%x/0x%x",
		    pc -> pc_prequirements, pc -> pc_srequirements);
	}
#endif

	ACCFREE (acc);

	if (RoSetService (sd, RoPService, roi) == NOTOK)
	    ros_adios (rop, "set RO/PT fails");
    }
    else {
	register struct SSAPaddr *sa;

	roa -> roa_port = is -> is_port;	/* yikes! */
	if ((is = getisoserventbyname ("ros", "ssap")) == NULL)
	    adios (NULLCP, "ssap/ros: unknown entity");
	if ((sa = is2saddr (addr, NULLCP, is)) == NULLSA)
	    adios (NULLCP, "address translation failed");
	roa -> roa_addr = *sa;	/* struct copy */

	if ((pe = int2prim (i = getpid ())) == NULLPE)
	    adios (NULLCP, "unable to allocate hello");

	fprintf (stderr, "%s... ", addr);
	(void) fflush (stderr);
	if (RoBeginRequest (roa, pe, roc, roi) == NOTOK) {
	    fprintf (stderr, "failed\n");
	    ros_adios (rop, "RO-BEGIN.REQUEST");
	}

	pe_free (pe);

	if (roc -> roc_result != ROS_ACCEPT) {
	    fprintf (stderr, "failed\n");
	    adios (NULLCP, "association rejected: [%s]",
		    RoErrString (roc -> roc_result));
	}
	fprintf (stderr, "connected\n");

#ifdef	DEBUG
	advise (NULLCP, "sent greetings of %d", i);
#endif

	sd = roc -> roc_sd;
	if (roc -> roc_data) {
	    if ((i = prim2num (roc -> roc_data)) == NOTOK
		    && roc -> roc_data -> pe_errno != PE_ERR_NONE)
		adios (NULLCP, "error decoding hello: %s (%d)",
			pe_error (roc -> roc_data -> pe_errno), i);
#ifdef	DEBUG
	    advise (NULLCP, "received greetings of %d", i);
#endif
	}
	ROCFREE (roc);
    }

    do_ros (sd);
}

/* \f

 */

static int  do_ros (sd)
int	sd;
{
    int     cc,
            i,
            j;
    char   *cp,
           *dp,
            buffer[BUFSIZ];
    struct RoSAPindication  rois;
    register struct RoSAPindication *roi = &rois;
    register struct RoSAPpreject   *rop = &roi -> roi_preject;
    struct AcSAPrelease acrs;
    register struct AcSAPrelease *acr = &acrs;
    register PE	pe;
    struct stat st;
    
    if (fstat (fileno (stdin), &st) != NOTOK
	    && (st.st_mode & S_IFMT) == S_IFREG
	    && (cc = st.st_size) != 0) {
	(void) lseek (fileno (stdin), 0L, 0);

	if ((cp = malloc ((unsigned) cc)) == NULL)
	    adios (NULLCP, "no memory");
	for (dp = cp, j = cc; j > 0; dp += i, j -= i)
	    switch (i = read (fileno (stdin), dp, j)) {
		case NOTOK: 
		    adios ("on stdin", "read failed");

		case OK: 
		    adios (NULLCP, "premature end-of-file");

		default: 
		    break;
	    }
	if ((pe = oct2prim (cp, cc)) == NULLPE)
	    adios (NULLCP, "unable to allocate invocation argument");
	free (cp);
	for (i = 10; i > 0; i--) {
#ifdef	TIMER
	    timer (0);
#endif
	    ros_invokerequest (sd, pe);
#ifdef	TIMER
	    timer (cc);
#endif
	}
	pe_free (pe);
    }
    else
	while (fgets (buffer, sizeof buffer, stdin)) {
	    if ((pe = oct2prim (buffer, strlen (buffer) + 1)) == NULLPE)
		adios (NULLCP, "unable to allocate invocation argument");
	    ros_invokerequest (sd, pe);
	    pe_free (pe);
	}

    if (isrts) {
	struct RtSAPindication  rtis;
	register struct RtSAPindication *rti = &rtis;
	register struct RtSAPabort *rta = &rti -> rti_abort;

	if (isacs) {
	    if (RtCloseRequest (sd, ACF_NORMAL, NULLPE, acr, rti) == NOTOK)
		switch (rta -> rta_reason) {
		    case RTS_OPERATION:
		    case RTS_WAITING: 
			rts_waitfor (sd);
			if (RtCloseRequest (sd, ACF_NORMAL, NULLPE, acr, rti)
				== OK)
			    break;	/* else fall */

		    default:
			rts_adios (rta, "RT-CLOSE.REQUEST");
		}

	    if (!acr -> acr_affirmative) {
		(void) RtUAbortRequest (sd, NULLPE, rti);
		adios (NULLCP, "release rejected by peer: %d, %d elements",
			    acr -> acr_reason, acr -> acr_ninfo);
	    }
	}
	else
	    if (RtEndRequest (sd, rti) == NOTOK)
		switch (rta -> rta_reason) {
		    case RTS_OPERATION: 
		    case RTS_WAITING: 
			rts_waitfor (sd);
			if (RtEndRequest (sd, rti) == OK)
			    break;	/* else fall */

		    default: 
			rts_adios (rta, "RT-END.REQUEST");
		}
    }
    else
	if (isacs) {
	    struct AcSAPindication  acis;
	    register struct AcSAPindication *aci = &acis;
	    register struct AcSAPabort *aca = &aci -> aci_abort;

	    if (AcRelRequest (sd, ACF_NORMAL, NULLPEP, 0, NOTOK, acr, aci)
		    == NOTOK)
		acs_adios (aca, "A-RELEASE.REQUEST");

	    if (!acr -> acr_affirmative) {
		(void) AcUAbortRequest (sd, NULLPEP, 0, aci);
		adios (NULLCP, "release rejected by peer: %d, %d elements",
			    acr -> acr_reason, acr -> acr_ninfo);
	    }
	    ACRFREE (acr);
	}
	else
	    if (RoEndRequest (sd, ROS_NOPRIO, roi) == NOTOK)
		ros_adios (rop, "RO-END.REQUEST");
}

/* \f

 */

static int  ros_invokerequest (sd, pe)
int	sd;
PE	pe;
{
    int     result;
    struct RoSAPindication  rois;
    register struct RoSAPindication *roi = &rois;
    register struct RoSAPpreject   *rop = &roi -> roi_preject;
    static int  id = 0;
    static int  op = 0;

    switch (result = RoInvokeRequest (sd, op++, ROS_SYNC, pe, ++id, NULLIP,
		ROS_NOPRIO, roi)) {
	case NOTOK: 
	    if (ROS_FATAL (rop -> rop_reason))
		ros_adios (rop, "RO-INVOKE.REQUEST");
	    ros_advise (rop, "RO-INVOKE.REQUEST");
	    break;

	case OK: 
	    switch (roi -> roi_type) {
		case ROI_INVOKE: 
		    adios (NULLCP, "got RO-INVOKE.INDICATION");

		case ROI_RESULT: 
		    {
			register struct RoSAPresult *ror = &roi -> roi_result;

			if (ror -> ror_id != id) {
			    advise (NULLCP, "id mismatch (wanted %d, got %d)",
				    id, ror -> ror_id);
			    status++;
			    if (RoURejectRequest (sd, &ror -> ror_id,
				    ROS_RRP_UNRECOG, ROS_NOPRIO, roi) == NOTOK)
				ros_adios (rop, "RO-REJECT-U.REQUEST");
			}
			else
			    if (mode == echo
				    && pe_cmp (pe, ror -> ror_result)) {
				advise (NULLCP, "data mismatch");
				status++;
			    }

			RORFREE (ror);
		    }
		    break;

		case ROI_ERROR: 
		    {
			register struct RoSAPerror *roe = &roi -> roi_error;

			if (roe -> roe_id != id) {
			    advise (NULLCP, "id mismatch (wanted %d, got %d)",
				    id, roe -> roe_id);
			    status++;
			    if (RoURejectRequest (sd, &roe -> roe_id,
				    ROS_REP_UNRECOG, ROS_NOPRIO, roi) == NOTOK)
				ros_adios (rop, "RO-REJECT-U.REQUEST");
			}
			else
			    if (mode == echo
				    && pe_cmp (pe, roe -> roe_param)) {
				advise (NULLCP, "data mismatch");
				status++;
			    }

			ROEFREE (roe);
		    }
		    break;

		case ROI_UREJECT: 
		    {
			register struct RoSAPureject *rou = &roi -> roi_ureject;

			if (rou -> rou_noid)
			    advise (NULLCP, "RO-REJECT-U.INDICATION: %s",
				    RoErrString (rou -> rou_reason));
			else
			    advise (NULLCP, "RO-REJECT-U.INDICATION: %s (id=%d)",
				    RoErrString (rou -> rou_reason),
				    rou -> rou_id);
			if (!rou -> rou_noid && rou -> rou_id != id) {
			    advise (NULLCP, "id mismatch (wanted %d, got %d)",
				    id, rou -> rou_id);
			    status++;
			}
		    }
		    break;

		default: 
		    adios (NULLCP, "unknown indication type=%d",
			    roi -> roi_type);
	    }
	    if (isrts)
		turn = 0;
	    break;

	case DONE: 
	    adios (NULLCP, "got RO-END.INDICATION");

	default: 
	    adios (NULLCP, "unknown return from RoInvokeRequest=%d", result);
    }
}


/* \f

 */

static void  ros_adios (rop, event)
register struct RoSAPpreject *rop;
char   *event;
{
    ros_advise (rop, event);

    _exit (1);
}


static void  ros_advise (rop, event)
register struct RoSAPpreject *rop;
char   *event;
{
    char    buffer[BUFSIZ];

    if (rop -> rop_cc > 0)
	(void) sprintf (buffer, "[%s] %*.*s", RoErrString (rop -> rop_reason),
		rop -> rop_cc, rop -> rop_cc, rop -> rop_data);
    else
	(void) sprintf (buffer, "[%s]", RoErrString (rop -> rop_reason));

    advise (NULLCP, "%s: %s", event, buffer);
}

/* \f

   TIMER */

#ifdef	TIMER

#ifndef	NBBY
#define	NBBY	8
#endif


#ifndef	TMS
static  timer (cc)
int     cc;
{
    int     bytes;
    long    ms;
    float   bs;
    struct timeval  stop,
                    td;
    static struct timeval   start;

    if (cc == 0) {
	(void) gettimeofday (&start, (struct timezone *) 0);
	return;
    }
    else
	(void) gettimeofday (&stop, (struct timezone  *) 0);

    tvsub (&td, &stop, &start);
    ms = (td.tv_sec * 1000) + (td.tv_usec / 1000);
    bytes = mode == echo ? cc * 2 : cc;
    bs = (((float) bytes * NBBY * 1000) / (float) (ms ? ms : 1)) / NBBY;

    advise (NULLCP, "%d bytes %s in %d.%02d seconds (%.2f Kbytes/s)",
	    cc, mode == echo ? "echoed" : "sunk",
	    td.tv_sec, td.tv_usec / 10000, bs / 1024);
}


static  tvsub (tdiff, t1, t0)
register struct timeval *tdiff,
			*t1,
			*t0;
{

    tdiff -> tv_sec = t1 -> tv_sec - t0 -> tv_sec;
    tdiff -> tv_usec = t1 -> tv_usec - t0 -> tv_usec;
    if (tdiff -> tv_usec < 0)
	tdiff -> tv_sec--, tdiff -> tv_usec += 1000000;
}
#else
long	times ();


static	timer (cc)
int	cc;
{
    int	    bytes;
    long    ms;
    float   bs;
    long    stop,
	    td,
	    secs,
	    msecs;
    struct tms tm;
    static long start;

    if (cc == 0) {
	start = times (&tm);
	return;
    }
    else
	stop = times (&tm);

    td = stop - start;
    secs = td / 60, msecs = (td % 60) * 1000 / 60;
    ms = (secs * 1000) +  msecs;
    bytes = mode == echo ? cc * 2 : cc;
    bs = (((float) bytes * NBBY * 1000) / (float) (ms ? ms : 1)) / NBBY;
    
    advise (NULLCP, "%d bytes %s in %d.%02d seconds (%.2f KBytes/s)",
	    cc, mode == echo ? "echoed" : "sunk",
	    secs, msecs / 10, bs / 1024);
}
#endif
#endif

/* \f

   QBUF */

static int  qcmp (b, qb, l)
register char *b;
register struct qbuf *qb;
register int l;
{
    register struct qbuf   *qp;

    for (qp = qb -> qb_forw; qp != qb; qp = qp -> qb_forw) {
	if ((l -= qp -> qb_len) < 0) {
	    advise (NULLCP, "length mismatch(1)");
	    return NOTOK;
	}

	if (bcmp (b, qp -> qb_data, qp -> qb_len)) {
	    advise (NULLCP, "data mismatch");
	    return NOTOK;
	}

	b += qp -> qb_len;
    }

    if (l != 0) {
	advise (NULLCP, "length mismatch(2)");
	return NOTOK;
    }

    return OK;
}

/* \f

   ERRORS */

#ifndef	lint
void	_advise ();


void	adios (va_alist)
va_dcl
{
    va_list ap;

    va_start (ap);

    _advise (ap);

    va_end (ap);

    _exit (1);
}
#else
/* VARARGS */

void	adios (what, fmt)
char   *what,
       *fmt;
{
    adios (what, fmt);
}
#endif


#ifndef	lint
void	advise (va_alist)
va_dcl
{
    va_list ap;

    va_start (ap);

    _advise (ap);

    va_end (ap);
}


static void  _advise (ap)
va_list	ap;
{
    char    buffer[BUFSIZ];

    asprintf (buffer, ap);

    (void) fflush (stdout);

    fprintf (stderr, "%s: ", myname);
    (void) fputs (buffer, stderr);
    (void) fputc ('\n', stderr);

    (void) fflush (stderr);
}
#else
/* VARARGS */

void	advise (what, fmt)
char   *what,
       *fmt;
{
    advise (what, fmt);
}
#endif