/*
   PL/I-callable routines for variable-length send & receive
   of character strings under RSX-11M 

   calls:

   CALL SNDS(TASK, STRING, DSW);

	sends the string to the specified task, in a series of packets
	using SDAT$ calls. if a send of a packet fails due to pool
	exhaustion, it is retried indefinitely.


   CALL RCVS(TASK, STRING, DSW);

	receives a string sent by SNDS from the specified task, returning it 
	in the given string variable. if there is no data to be received yet,
	the calling task is stopped until data is received.


   CALL RCVAS(TASK, STRING, DSW);

	same as RCVS, except receives from any task, returning the name 
	of the task in the given variable and returning the string in 
	the given string variable.


   where:

	TASK is the RAD50 name of a task (FIXED BIN(31)) for SNDS & RCVS,
		must be a FIXED BIN(31) variable for RCVAS.

	STRING is any PL/I string variable or constant for SNDS, must be a
		variable for RCVS & RCVAS. there's no limit on the size. 
		variables may be either VARYING or UNVARYING. strings can 
		contain arbitrary binary information if needed.

	DSW is a FIXED BIN(15) variable to receive the returned directive 
		status word.


   You can use these routines instead of RECEIV & SEND - they are easier to
   use, and don't constrain your design to an arbitrary 13-word limit.
   If your data is 25 bytes or less, only one packet is sent.

   However, since the data sent via these routines is stored in system pool,
   they should not be used in an application where there is the possibility
   of a significant amount of data piling up before the receiving task can 
   get it - this could bring the whole system down. This applies both to a
   few large messages or to a lot of little ones. For this kind of use,
   use one of the DECUS variable send & receive drivers or use disk queueing.
   These routines are ideal for the case where one or a few one-line messages
   are going back and forth between interacting tasks, which wait for a
   response before sending more, or where data is sent only occasionally to
   a task that does not exit.

   RCVS & RCVAS follow the standard PL/I string assignment rules in storing
   strings into variables - i.e., if to a VARYING variable, the current length
   is set, if to an UNVARYING variable and the string is shorter, it is blank
   padded, and if the string won't fit in the variable, it is truncated and
   the STRINGSIZE condition is raised. In addition, if truncation occurs, 
   error IE.RBS (-15) is returned.
*/

#include <clang.c>;
#include <plistring.c>;
#include <cx.h>;
#include <signal.c>;

/* 13-word (26-byte) packet has one-byte count and 25-byte data piece */
/* if the byte count is negative, there are more packets to come */
#define maxchunk 25 
typedef struct{char nbytes; char data[maxchunk];} packet;

typedef long taskname; /*rad50 variable, in a form that allows assignment*/



procedure SNDS(narg, task, str, dsw)
int narg; taskname *task; plistring str; int *dsw;
{
register charpointer pnext, p, ppkt, plast;
packet sendpkt; int n;

plient("SNDS");
iff narg<3 then signal(NUMARGS);
pnext = plistr(str);  plast = pnext + plilen(str);
do
 {
  n = plast - pnext;
  iff n>maxchunk then n = maxchunk;
  p = pnext; pnext = pnext + n;
  sendpkt.nbytes = ifx pnext==plast thenx n elsex -maxchunk;
  ppkt = &sendpkt.data;
  while (p<pnext) doo *ppkt++ = *p++; /*move chunk of string into packet*/
retry: *dsw = sdat(task, &sendpkt, 0);
  iff *dsw!=IS_SUC
    then { iff *dsw==IE_UPN /*if out of pool*/
	     then { wsig(); goto retry; } /*wait for significant event*/
	     else return;
	 }
  ustp(task); /*unstop receiver*/
 }
  while (pnext<plast);

}

procedure RCVS(narg, task, str, dsw)
int narg; taskname *task; plistring str; int *dsw;
{
taskname ttask; /*temp for unused return value*/
plient("RCVS");
iff narg<3 then signal(NUMARGS);
rcvs_(task, str, &ttask, dsw);
}


procedure RCVAS(narg, task, str, dsw)
int narg; taskname *task; plistring str; int *dsw;
{
taskname ttask;
plient("RCVAS");
iff narg<3 then signal(NUMARGS);
ttask = 0; /*receive from any task*/
rcvs_(&ttask, str, task, dsw);
}


/*
  common receive routine. for the first packet, we receive from rqst_task 
  (which is 0 for receive from any); on all subsequent packets, we receive 
  only from the task the first one came from (name returned in rcvd_task).
*/
procedure rcvs_(rqst_task, str, rcvd_task, dsw)
taskname *rqst_task, *rcvd_task;  plistring str;  int *dsw;
{
register charpointer pnext, p, ppkt, plast, pfirst;
struct { taskname task; packet pkt; } rcvpkt;
boolean overflow; int n;
pnext = pfirst = plistr(str);
plast = pnext + (str plisize);
overflow = false;
rcvpkt.task = *rqst_task;
do
 {
retry: *dsw = rcst(&rcvpkt.task, &rcvpkt); /*receive or stop*/
  iff *dsw==IS_SET then goto retry;
  iff *dsw!=IS_SUC then goto done;
  iff not overflow
    then {
	p = pnext; 
	n = rcvpkt.pkt.nbytes; iff n<0 then n = -n; pnext = pnext + n;
	/*i.e. pnext = pnext + abs(rcvpkt.pkt.nbytes), but without bringing in
	  the abs function, which could conflict with PL/I or user routines*/
	iff pnext>plast then { pnext = plast; overflow = true; }
	ppkt = &rcvpkt.pkt.data;
	while (p<pnext) doo *p++ = *ppkt++; /*move data into string variable*/
	}
 }
   while (rcvpkt.pkt.nbytes<0);

done:
  *rcvd_task = rcvpkt.task;
  iff str isvarying
    then (str->plisdptr.plisdvsp)->plivslen = pnext - pfirst;
    else while (pnext<plast) doo *pnext++ = ' ';
  iff overflow then { signal(STRINGSIZE); *dsw = IE_RBS; };
}
