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 x

⟦9e61ed281⟧ TextFile

    Length: 21026 (0x5222)
    Types: TextFile
    Names: »x400out84.c«

Derivation

└─⟦2d1937cfd⟧ Bits:30007241 EUUGD22: P.P 5.0
    └─⟦dc59850a2⟧ »EurOpenD22/pp5.0/pp-5.tar.Z« 
        └─⟦e5a54fb17⟧ 
            └─⟦this⟧ »pp-5.0/Chans/x40084/x400out84.c« 

TextFile

/* x400out84.c: transmit x400 messages out across RTS */

# ifndef lint
static char Rcsid[] = "@(#)$Header: /cs/research/pp/hubris/pp-beta/Chans/x40084/RCS/x400out84.c,v 5.0 90/09/20 15:56:34 pp Exp Locker: pp $";
# endif

/*
 * $Header: /cs/research/pp/hubris/pp-beta/Chans/x40084/RCS/x400out84.c,v 5.0 90/09/20 15:56:34 pp Exp Locker: pp $
 *
 * $Log:	x400out84.c,v $
 * Revision 5.0  90/09/20  15:56:34  pp
 * rcsforce : 5.0 public release
 * 
 */



#include <sys/param.h>
#include <sys/stat.h>
#include <sys/file.h>
#include "head.h"
#include "prm.h"
#include "q.h"
#include "qmgr.h"
#include "dr.h"
#include "RTS84-types.h"
#include "rtsparams.h"
#include <isode/rtsap.h>
#include <isode/psap.h>
#include <isode/cmd_srch.h>


#define MAXTRIES			10


/* -- externals -- */
extern char				*loc_dom_site;
extern char				*mquedir;
extern char				*quedfldir;
extern char				*rcmd_srch();
extern char				reason[];
extern CMD_TABLE			qtbl_mt_type[];


/* -- queue variables -- */
static Q_struct				Qstruct;
static DRmpdu				DRstruct;
static struct prm_vars			PRMstruct;
Q_struct				*PPQuePtr = &Qstruct;
DRmpdu					*DRptr = &DRstruct;


/* -- globals -- */
char					*body_string = NULLCP;
char					*myname;
int					body_len = 0;
int					trace_type = RTSP_TRACE_ALL; 
ADDR					*ad_list;


/* -- statics -- */
static CHAN				*mychan;
static char 				*this_msgid = NULLCP,
					*dumpp1 = NULLCP,
					*current_mta = NULLCP,
					*connected_to_site = NULLCP,
					*p1_string = NULLCP,
					*p1_ptr;
static struct timeval			data_timer;
static int				data_bytes=0,
					p1_length = 0,
					rts_sd = NOTOK;
static enum { st_init, st_hdr, st_bodyinit, st_body, st_end } trans_state;


/* -- local routines -- */
static int				get_more_message();
static char				*pe_flatten();
static int				rts_downtrans_all();
static int				rts_downtrans_inc();
static int				attempt_connect();
static int				construct_dr();
static int				construct_msg();
static int				construct_probe();
static int				deliver();
static int				endproc();
static int				initproc();
static int				read_body();
static int				rts_connect();
static int				rts_end();
static int				rts_start();
static int				rts_transfer_request();
static void				close_body();
static void				dirinit();
static void				dumptofile();
static void				open_body();
static void				wr_sender2log();
static struct type_Qmgr_DeliveryStatus	*process();





/* ---------------------  Begin	 Routines  -------------------------------- */





main (argc, argv)
int	argc;
char	**argv;
{
	if (myname = rindex (argv[0], '/'))
		myname++;
	if (myname == NULL || *myname == NULL)
		myname = argv[0];

	/* -- read pp tailor file -- */
	chan_init (myname);

	/* do other initialisations -- */
	pp_setuserid();
	or_myinit();

	/* -- check that the Q dirs are ok -- */
	dirinit();


#ifdef PP_DEBUG
	if (argc > 1 && strcmp (argv[1], "debug") == 0) {
		if (argc > 2)
			dumpp1 = argv[2];
		debug_channel_control (argc, argv, initproc, process, endproc);
	} else
#endif
		(void) channel_control (argc, argv, initproc, process, endproc);

	exit (0);
}




/* ---------------------  Static  Routines  ------------------------------- */




static void dirinit()	/* -- Change into pp queue space -- */
{
	PP_TRACE (("dirinit()"));
	if (chdir (quedfldir) < 0)
		err_abrt (RP_LIO, "Unable to change to dir '%s'", quedfldir);
}




static int initproc (arg)
struct type_Qmgr_Channel	*arg;
{
	char			*cname = qb2str (arg);

	PP_TRACE (("initproc (%s)", cname));

	if ((mychan = ch_nm2struct (cname)) == NULLCHAN)
		err_abrt (RP_PARM, "Channel '%s' not known", cname);
	rename_log (cname);
	free (cname);

	PP_NOTICE (("Starting %s (%s)", mychan -> ch_name, mychan -> ch_show));

	prm_init (&PRMstruct);
	q_init (PPQuePtr);
	dr_init (&DRstruct);

	if (current_mta)
		free (current_mta);
	current_mta = NULLCP;

	return OK;
}




static int endproc()
{
	switch (rts_end()) {
	case RP_OK:
		PP_NOTICE (("Connection successfully terminated"));
		break;
	case RP_BAD:
		PP_NOTICE (("Connection badly terminated"));
		break;
	default:
		PP_NOTICE (("Connection not made"));
		break;
	}
	return OK;
}




static struct type_Qmgr_DeliveryStatus *process (arg)
struct type_Qmgr_ProcMsg		*arg;
{
	struct type_Qmgr_UserList	*up;
	ADDR				*ad_sender = NULLADDR,
					*ad_recip  = NULLADDR,
					*rp,
					*sp,
					*lp = NULL;
	int 				naddrs = 0,
					ad_count,
					retval;

	if (this_msgid)	 free (this_msgid);
	this_msgid = qb2str (arg -> qid);

	PP_NOTICE (("Reading message '%s'", this_msgid));

	q_free (PPQuePtr);
	dr_free (&DRstruct);
	prm_free (&PRMstruct);

	delivery_init (arg -> users);

	retval = rd_msg (this_msgid, &PRMstruct, PPQuePtr,
			&ad_sender, &ad_recip, &ad_count);

	if (rp_isbad (retval)) {
		PP_LOG (LLOG_EXCEPTIONS, 
			("Read message failed for '%s'", this_msgid));
		reason[0] = 0;
		goto process_error;
	}

	if (ad_sender -> ad_r400adr == NULLCP) {
		do_reason ("x400 address not set in sender field of '%s'",
			 this_msgid);
		goto process_error;
	}


	for (sp = ad_sender, rp = ad_recip; rp; rp = rp -> ad_next) {

		for (up = arg -> users; up; up = up -> next) {

			PP_TRACE (("'%s' '%s' ad_no=%d up_no=%d",
				this_msgid, rp -> ad_value, rp -> ad_no,
				up -> RecipientId -> parm));

			if (up -> RecipientId -> parm != rp -> ad_no)
				continue;

			if (dchan_acheck (rp, sp, mychan, naddrs == 0,
					&current_mta))
				continue;
			naddrs ++;
			break;

		} /* -- end of for -- */


		if (up == NULL)
			continue;

		if (ad_list == NULLADDR)
			ad_list = lp = (ADDR *)calloc(1, sizeof *lp);
		else {
			lp -> ad_next = (ADDR *)calloc(1, sizeof *lp);
			lp = lp -> ad_next;
		}

		*lp = *rp;     /* struct copy */
		lp -> ad_next = NULLADDR;

	} /* -- end of for -- */


	wr_sender2log (ad_sender, naddrs);

	if (naddrs == 0) {
		PP_LOG (LLOG_EXCEPTIONS,
			("No recips to be processed in '%s'", this_msgid));
		rd_end ();
		return deliverystate; /* delivery state is set correct */
	}


	deliver (ad_list);		/* deliverystate set in deliver */

	for (lp = ad_list; lp; lp = rp) {
		rp = lp -> ad_next;
		free ((char *)lp);
	}

	ad_list = NULLADDR;
	rd_end();
	return deliverystate;


process_error: ;
	rd_end();
	return delivery_setallstate (int_Qmgr_status_messageFailure, reason);
}




static void wr_sender2log (sender, naddrs)
ADDR	*sender;
int	naddrs;
{
	if (naddrs == 0) {
		PP_NOTICE (("Sender '%s'", sender -> ad_r400adr));
		return;
	}

	switch (PPQuePtr -> msgtype) { 
	case MT_PMPDU: 
		PP_NOTICE (("Sender (of probe) '%s'", sender -> ad_r400adr));
		break;

	default:
		if (ad_list -> ad_status == AD_STAT_PEND)
			PP_NOTICE (("Sender '%s'", sender -> ad_r400adr));
		else 
			PP_NOTICE (("Sender (of delivery report '%s'",
					sender -> ad_r400adr));
		break;
	}

	return;
}




static int deliver (recip)
ADDR	*recip;
{
	int	value	= int_Qmgr_status_messageFailure;
	ADDR	*ap;
	int msgtype;
	int	retval;

	for (ap = recip; ap; ap = ap -> ad_next)
		PP_NOTICE (("Recipient Address '%s'",  ap -> ad_r400adr));


	switch (recip -> ad_status) {
	    case AD_STAT_PEND:
	    case AD_STAT_DRWRITTEN:
		break;
	    case AD_STAT_DONE:
		delivery_setallstate (int_Qmgr_status_success, "All delivered");
		return NOTOK;
	    default:
		do_reason ("bad state");
		delivery_setallstate (int_Qmgr_status_messageFailure, reason);
		return NOTOK;
	}


	if (attempt_connect() != OK)
		return NOTOK;

	msgtype = Qstruct.msgtype;

	if (recip -> ad_status == AD_STAT_DRWRITTEN ||
	    Qstruct.msgtype == MT_DMPDU) {
		retval = construct_dr ( &Qstruct, recip);
		msgtype = MT_DMPDU;
	}
	else if (Qstruct.msgtype == MT_PMPDU)
		retval = construct_probe (&Qstruct, recip);
	else
		retval = construct_msg (&Qstruct, recip);


	if (retval == NOTOK)
		return NOTOK;


	switch (rts_transfer_request(msgtype)) {
	    case OK:
		value = int_Qmgr_status_success;
		break;
	    default:
		if (rts_sd == NOTOK)
			value = int_Qmgr_status_mtaAndMessageFailure;
		else
			value = int_Qmgr_status_messageFailure;
		break;
	}


	for (ap = recip; ap; ap = ap -> ad_next)
		if (ap -> ad_resp) {
			switch (value) {
			case int_Qmgr_status_success:
				delivery_set (ap -> ad_no, value);
				wr_ad_status (ap, AD_STAT_DONE);
				break;
			default:
				delivery_setstate (ap -> ad_no, value, reason);
				break;
			}
		}


	if (p1_string)		free (p1_string);
	p1_string		= NULLCP;
	data_bytes		= 0;
	p1_length		= 0;

	return OK;
}



static int construct_dr (qp, recip)
Q_struct *qp;
ADDR	*recip;
{
	PE	pe;

	if (rp_isbad (get_dr (recip -> ad_no, this_msgid, &DRstruct))) {
		do_reason ("Can't read delivery report '%s' recip '%d'",
			 this_msgid, recip -> ad_no);
		delivery_setallstate (int_Qmgr_status_messageFailure, reason);
		return NOTOK;
	}

	qp -> msgtype = MT_DMPDU;

	if (build_P1_MPDU (&pe, 1, NULLINT, NULLCP, NULL) == NOTOK) {
		do_reason ("Can't build DR ASN.1: %s", PY_pepy);
		delivery_setallstate (int_Qmgr_status_messageFailure, reason);
		return NOTOK;
	}

	PP_PDU (print_P1_MPDU, pe, "P1.MPDU", PDU_WRITE);

	p1_length = 0;
	p1_string = p1_ptr = pe_flatten (pe, NULLCP, 1, &p1_length);
	pe_free (pe);
	dr_free (&DRstruct);
	return OK;
}



static int construct_probe (qp, recip)
Q_struct *qp;
ADDR	*recip;
{
	PE pe;

	if (build_P1_MPDU (&pe, 1, NULLINT, NULLCP, NULL) == NOTOK) {
		do_reason ("Can't build Probe ASN.1: %s", PY_pepy);
		return NOTOK;
	}

	PP_PDU (print_P1_MPDU, pe, "P1.MPDU", PDU_WRITE);

	p1_length = 0;
	p1_string = p1_ptr = pe_flatten (pe, NULLCP, 1, &p1_length);
	pe_free (pe);
	return OK;
}



static int construct_msg (qp, recip)
Q_struct *qp;
ADDR *recip;
{
	PE	pe;

	if (build_P1_UMPDUEnvelope (&pe, 1, NULLINT, NULLCP, qp) == NOTOK) {
		do_reason ("Can't build MSG ASN.1: %s", PY_pepy);
		return NOTOK;
	}
	PP_PDU (print_P1_UMPDUEnvelope, pe, "P1.UMPDUEnvelope", PDU_WRITE);

	p1_length = 0;
	p1_string = p1_ptr = pe_flatten (pe, NULLCP, 1, &p1_length);
	pe_free (pe);
	return OK;
}




static int attempt_connect()
{
	if (connected_to_site == NULLCP)
		goto attempt_rts_start;

	if (lexequ (connected_to_site, current_mta) == 0)
		return OK;

	free (connected_to_site);
	connected_to_site = NULLCP;
	(void) rts_end();
	goto attempt_rts_start;

attempt_rts_start: ;

	if (rts_start() != OK) {
		delivery_setallstate (int_Qmgr_status_mtaFailure, reason);
		return NOTOK;
	}

	connected_to_site = strdup (current_mta);
	return OK;
}




static int rts_start()
{
	RtsParams	*rp;
	int		count, retcode = NOTOK;

	PP_TRACE (("rts_start site %s", current_mta));

	for (count = 0; count < MAXTRIES; count++) {

		if ((rp = tb_rtsparams (mychan, current_mta)) == NULL) {
			do_reason ("Unable to locate '%s'", current_mta);
			return NOTOK;
		}

		if (rp -> type != RTSP_1984) {
			do_reason ("Not 1984 connection type %s", current_mta);
			break;
		}

		retcode = rts_connect (rp);

		switch (retcode) {
		case OK:
			break;
		default:
			if (rp -> try_next) {
				free (current_mta);
				current_mta = strdup (rp -> try_next);
				RPfree (rp);
				continue;
			}	
			break;
		}

		break;
	}


	trace_type = rp -> trace_type;
	RPfree (rp);
	return retcode;
}




static int rts_connect (rp)
RtsParams *rp;
{
	struct RtSAPaddr			rtsapaddr;
	register struct RtSAPaddr		*rtsap = &rtsapaddr;
	struct RtSAPaddr			rts_calling;
	struct RtSAPaddr			*rt_c;
	struct SSAPaddr				*sa;
	struct RtSAPconnect			rtcs;
	register struct RtSAPconnect		*rtc = &rtcs;
	struct RtSAPindication			rtis;
	register struct RtSAPindication		*rti = &rtis;
	register struct RtSAPabort		*rta = &rti -> rti_abort;
	struct type_RTS84_Request		*req = NULL;
	int					retval = NOTOK;
	PE					pe = NULLPE;

	PP_TRACE (("rts_connect(%d)", rp -> rts_mode));

	if (rts_encode_request (&pe, &req, rp -> our_name,
				rp -> our_passwd) == NOTOK)
		goto rts_connect_free;

	PP_PDU (print_RTS84_Request, pe, "RTS84.Request", PDU_WRITE);

	/* -- clears the relavant structures -- */
	bzero ((char *)rtsap, sizeof (*rtsap));
	bzero ((char *)rtc, sizeof (*rtc));
	bzero ((char *)rti, sizeof (*rti));
	bzero ((char *)rta, sizeof (*rta));

	rtsap -> rta_port = htons(1); /* P1 id */

	if (rp -> their_address == NULLCP) {
		do_reason ("Null called address for %s", rp -> their_name);
		goto rts_connect_free;
	}

	if ((sa = str2saddr (rp -> their_address)) == NULLSA) {
		do_reason ("Bad called address for %s", rp -> their_address);
		goto rts_connect_free;
	}

	rtsap -> rta_addr = *sa; /* struct copy */

	if(rp -> our_address == NULLCP ||
	   (sa = str2saddr (rp -> our_address)) == NULLSA)
		rt_c = NULLRtA;
	else {
		rt_c = &rts_calling;
		bzero ((char *)rtc, sizeof *rt_c);
		rt_c -> rta_addr = *sa; /* struct copy */
	}

	PP_NOTICE (("Connecting to site %s", current_mta));

	if (RtBeginRequest2 (rtsap, rt_c, rp -> rts_mode, RTS_INITIATOR,
			     pe, rtc, rti) == NOTOK) {
		rts_advise (rta, "RT-BEGIN.REQUEST");
		goto rts_connect_free;
	}

	rts_sd = rtc -> rtc_sd;

	if (rtc -> rtc_result != RTS_ACCEPT) {
		PP_LOG (LLOG_EXCEPTIONS,
			("Unable to connect to site %s (reason %s)",
			 current_mta, RtErrString (rtc -> rtc_result)));
		goto rts_connect_free;
	}


	PP_PDU (print_RTS84_Request, rtc -> rtc_data, "RTS84.Response",
		PDU_READ);

	if (parameter_checks (rtc -> rtc_data, rp -> their_name,
			      rp -> their_passwd, current_mta, TRUE) == OK)
		retval = OK;

rts_connect_free: ;
	RTCFREE(rtc);
	RTAFREE(rta);
	if (pe)
		pe_free (pe);
	if (req)
		free_RTS84_Request (req);
	return retval;
}




static int rts_transfer_request(type)
int	type;
{
	struct RtSAPindication		rtis;
	register struct RtSAPindication *rti = &rtis;
	register struct RtSAPabort	*rta = &rti -> rti_abort;

	PP_TRACE (("rts_transfer_request()"));

	trans_state = st_init;

	switch (type) {
	    case MT_PMPDU:
	    case MT_DMPDU:
		if (RtSetDownTrans (rts_sd, rts_downtrans_all, rti) == NOTOK) {
			rts_advise (rta, "set DownTrans upcall");
			return NOTOK;
		}
		break;

	    case MT_UMPDU:
	    default:
		if (RtSetDownTrans (rts_sd, rts_downtrans_inc, rti) == NOTOK) {
			rts_advise (rta, "set DownTrans upcall");
			return NOTOK;
		}
		break;
	}

	data_bytes = 0;
	timer_start (&data_timer);
	if (RtTransferRequest (rts_sd, NULLPE, NOTOK, rti) == NOTOK) {
		rts_advise (rta, "RT-TRANSFER.REQUEST");
		return NOTOK;
	}


	if (data_bytes > 0)
		timer_end (&data_timer, data_bytes, "Transfer Completed");

	PP_NOTICE((">>> %s %s transfered to %s",
		rcmd_srch(Qstruct.msgtype, qtbl_mt_type),
	   	this_msgid, current_mta));

	return OK;
}




/* ARGSUSED */

static int  rts_downtrans_all (sd, base, len, size, ssn, ack, rti)
int			sd;
char			**base;
int			*len,
			size;
long			ssn,
			ack;
struct RtSAPindication	*rti;
{
	register int	n;
	static char   *bp;
	static int	bsize;


	PP_TRACE (("rts_downtrans()"));

	if (base == NULLVP) {
		PP_TRACE (("RT-PLEASE.INDICATION: %d", size));
		return OK;
	}

	if (bp == NULLCP) {

		if (size == 0)	/* no checkpointing... */
			n =  p1_length;
		else
			n = size;

		if ((bp = malloc ((unsigned) n)) == NULL)
			return (rtsaplose (rti, RTS_CONGEST,
				NULLCP, "out of memory"));

		PP_TRACE (("Selecting block size of %d", n));
		bsize = n;
	}
	if (size > 0 && size < bsize) {
		PP_LOG (LLOG_EXCEPTIONS, 
			("rts_downtrans/downtrans size decreased.."));
		bsize = size;
	}

	n = MIN(bsize, p1_length);

	if (n == 0) {
		*base = NULLCP;
		*len = 0;
		return OK;
	}

	*base = p1_ptr;
	*len = n;

	data_bytes += n;
	p1_ptr += n;
	p1_length -= n;

	if (dumpp1)
		dumptofile (*base, *len, dumpp1);

	return OK;
}


/* ARGSUSED */
static int  rts_downtrans_inc (sd, base, len, size, ssn, ack, rti)
int			sd;
char			**base;
int			*len,
			size;
long			ssn,
			ack;
struct RtSAPindication	*rti;
{
	int	cc, count;
	int	n;
	char	*ptr, *p;
	static char	*trans_buf;
	static int	bsize;

	PP_TRACE (("rts_downtrans_inc (%d, base, len, %d, %ld, %ld, rti)",
		   sd, size, ssn, ack));

	if (base == NULLVP) {
		PP_TRACE (("RT-PLEASE.INDICATION: %d", size));
		return OK;
	}

	if (trans_buf == NULLCP) {

		if (size == 0)	/* no checkpointing... */
			n  =  BUFSIZ * 10;
		else
			n = size;
			
		if ((trans_buf = malloc ((unsigned) n)) == NULL)
			return (rtsaplose (rti, RTS_CONGEST,
				NULLCP, "out of memory"));

		PP_TRACE (("Selecting block size of %d", n));

		bsize = n;
	}
	if (size > 0 && size < bsize) {
		PP_LOG (LLOG_EXCEPTIONS, ("downtrans size decreased..."));
		bsize = size;
	}

	for (n = bsize, p = trans_buf, count = 0; n > 0;) {
	    if ((cc = get_more_message (&ptr, n)) == 0)
		break;
	    bcopy (ptr,  p, cc);
	    p += cc;
	    n -= cc;
	    count += cc;
	}
	if (dumpp1)
		dumptofile (trans_buf, count, dumpp1);
	if (count == 0) {
		*base = NULLCP;
		*len = 0;
		free (trans_buf);
		trans_buf = NULLCP;
		return OK;
	}
	*base = trans_buf;
	*len = count;
	data_bytes += count;
	PP_TRACE (("rts_downtrans_inc read %d bytes (%d checkpoint)",
		   count, bsize));
	
	return OK;
}

static int get_more_message (dptr, n)
char	**dptr;
int	n;
{
	static char	eoc[3];
	static char	*ptr;
	static int	len = 0, inited = 0;
	static char	buffer[BUFSIZ*8];
	PE		pe;

	PP_TRACE (("get_more_message (%d)", n));
	if (!inited) {
		pe = pe_alloc (PE_CLASS_UNIV, PE_FORM_PRIM, PE_UNIV_EOC);
		inited = 3;
		(void) pe_flatten (pe, eoc, 1, &inited);
		pe_free (pe);
	}
	
	if (len <= 0) {
		switch (trans_state) {
		    case st_init:
			pe = pe_alloc (PE_CLASS_CONT, PE_FORM_CONS, 0);
			pe -> pe_len = PE_LEN_INDF;
			len = sizeof buffer;
			(void) pe_flatten (pe, buffer, -1, &len);
			pe_free (pe);
			trans_state = st_hdr;
			ptr = buffer;
			break;

		    case st_hdr:
			len = p1_length;
			ptr = p1_string;
			trans_state = st_bodyinit;
			break;

		    case st_bodyinit:
			pe = pe_alloc (PE_CLASS_UNIV, PE_FORM_CONS, PE_PRIM_OCTS);
			pe -> pe_len = PE_LEN_INDF;
			len = sizeof buffer;
			(void) pe_flatten (pe, buffer, -1, &len);
			pe_free (pe);
			ptr = buffer;
			open_body ();
			trans_state = st_body;
			break;
		
		    case st_body:
			len = n;
			if ((len = read_body (buffer, sizeof buffer)) != 0) {
				ptr = buffer;
				break;
			}
			close_body ();
			bcopy (eoc, buffer, 2); /* end of octet string (contents) */
			bcopy (eoc, buffer + 2, 2); /* end of hdr sequence */
			len = 4;
			ptr = buffer;
			trans_state = st_end;
			break;
		
		    case st_end:
			return 0;
		}
	}
	*dptr = ptr;
	n = MIN (n, len);
	len -= n;
	ptr += n;
	PP_TRACE (("get_more_message returning %d bytes", n));
	return n;

}

static char *pe_flatten (pe, buffer, flag, ccp)
PE	pe;
char	*buffer;
int	*ccp;
int	flag;
{
	PS	ps;
	char	*cp;

	if ((ps = ps_alloc (str_open)) == NULLPS)
		adios (NULLCP, "ps_alloc failed");
	if (str_setup (ps, buffer, *ccp, buffer == NULLCP ? 0 : 1) == NOTOK)
		adios (NULLCP, "str_setup failed", ps_error (ps -> ps_errno));

	if (pe2ps_aux (ps, pe, flag) == NOTOK)
		adios (NULLCP, "pe2ps failed [%s]", ps_error (ps -> ps_errno));
	*ccp = ps -> ps_ptr - ps -> ps_base;
	cp = ps -> ps_base;
	ps -> ps_base = NULLCP;
	ps_free (ps);
	return cp;
}


static int rts_end()
{
	struct RtSAPindication		rtis;
	register struct RtSAPindication *rti = &rtis;
	register struct RtSAPabort	*rta = &rti -> rti_abort;

	PP_TRACE (("rts_end (%d)", rts_sd));

	if (rts_sd != NOTOK)
		if (RtEndRequest (rts_sd, rti) == NOTOK) {
			rts_advise (rta, "RT-END.REQUEST");
			rts_sd = NOTOK;
			return RP_BAD;
		}
		else {
			rts_sd = NOTOK;
			return RP_OK;
		}

	return RP_NOOP;
}

#ifdef PP_DEBUG
static void dumptofile (str, n, file)
char	*str;
int	n;
char	*file;
{
	FILE	*fp;
	static int once_only = 0;

	if ((fp = fopen (file, once_only ? "a": "w")) == NULL) {
		advise (LLOG_NOTICE, NULLCP, file, "Can't open file");
		return;
	}
	once_only ++;

	fwrite (str, 1, n, fp);
	(void) fclose (fp);
}
#endif

static FILE	*body_fp;

static void open_body()
{
	char	filename[FILNSIZE],
		*msgdir = NULLCP;

	if (body_fp)
		close_body();

	if (qid2dir (this_msgid, ad_list, TRUE, &msgdir) == NOTOK)
		adios (NULLCP, "Can't find message %s", this_msgid);

	if (rp_isbad (msg_rinit (msgdir)))
		adios (NULLCP, "Can't initialise directory %s", msgdir);

	if (rp_isbad (msg_rfile (filename)))
		adios (NULLCP, "Can't read file-name");

	(void) msg_rend();

	if ((body_fp = fopen (filename, "r")) == NULL)
		adios (filename, "Can't open file");

}


static int read_body (buf, size)
char	*buf;
int	size;
{
	int		len, i;
	static PE	pe = NULLPE;
	PE		pe2;
	char		tbuf[BUFSIZ*8];

	PP_TRACE (("read_body (%d)", size));
	if (pe == NULLPE)
	    pe = pe_alloc (PE_CLASS_UNIV, PE_FORM_PRIM, PE_PRIM_OCTS);

	pe -> pe_len = size;
	pe -> pe_len -= ps_get_abs (pe) - size;
	for (i = 0; i < 10; i++) {
	    if (ps_get_abs(pe) <= size)
		break;
	    pe -> pe_len -= 2;
	}

	if ( i >= 10)
	    adios (NULLCP, "Can't sort out asn1 length after 10 attempts");
	

	bzero (tbuf, BUFSIZ);
	if ((len = fread (tbuf, 1, pe -> pe_len, body_fp)) <= 0) {
		if (ferror (body_fp))
			adios ("fread", "error on file");
		else if (feof (body_fp))
			return 0;
	}

	pe2 = oct2prim (tbuf, len);
	(void) pe_flatten (pe2, buf, 1, &size);
	PP_TRACE (("octet string of length %d returned (%d)",
		   pe2 -> pe_len, size));
	pe_free (pe2);
	return size;
}



static void close_body()
{
	if (body_fp)
		fclose (body_fp);
	body_fp = NULL;
}