/*
BEGIN DOCUMENTATION

Name: Daniel T. Soldahl				Created: 08/02/83
					    Last Update: 12/20/83

Title: VSENDRECV.C - C callable routines for variable-length send & receive

Index: Send, Receive, Intertask Communication

Abstract: Variable length character send & receive data under RSX-11M.
	  Compatible with SNDS & RCVS which are PL/I callable.

Usage: rc = vsend(&task,buff,buflen)

	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.

       rc = vrecv(&task,buff,&buflen)

	Receives a string sent by SNDS or VSEND from the specified task,
	returning it in the given buffer area. Buflen is set to actual count
	of characters received. If there is no data to be received yet, the
	calling task is stopped until data is received. 

       rc = vrecva(&task,buff,&buflen)

	Same as VRECV, except receives from any task, returning the name 
	of the task in the given variable and returning the received data
	in the given buffer area.


Parameters: struct rad50 task -	Rad50 taskname, INPUT for vsend	& vrecv,
				OUTPUT for vrecva.
	    char *buff -	Pointer to data buffer INPUT for vsend,
				OUTPUT for vrecv & vrecva.  Buff may contain
				arbitrary binary data, there is no size limit.
	    int  buflen -	INPUT for vsend: number of characters to send.
				INPUT for vrecv & vrecva: Maximum buffer size.
				OUTPUT for vrecv & vrecva: Number of characters
				actually received.

	    int  rc	-	Return code is the returned directive status
				word (dsw$) from SDAT$, RCST$ directives.

	    int	tskabo	-	INPUT for vrecv & vrecva: External boolean used
				to force return if receiving task is aborted.
				(Must be set false on entry, can be set true by
				an external ast routine.)

Environment: RSX11M Ver. 4.0, DECUS C Compiler

See Also: SNDRCVS.C (SNDS, RCVS), TCM.C

Description:

Example(s):

Uses:	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. 


Internal:   Each 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. 


Update History: 2-AUG-83 SNDSRCVS.C written by Walter Epp was modified to be
		callable from C by Dan Soldahl.
		16-SEP-83 Changed vsend to return IS_SUC if ustp gives IE_ITS
		(task not stopped), DTS.
		20-DEC-83 Added check for tskabo in rcvs_ routine, DTS.

END DOCUMENTATION
*/


#include <clang.c>;
#include <cxd.h>;

#define maxchunk 25 
typedef struct{char nbytes; char data[maxchunk];} packet;

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

extern boolean tskabo;	/* If true then vrecv will return if unstopped & no
			   receive data ready. */

int
function vsend(task, str, len)
taskname *task; charpointer str; int len;
{
register charpointer pnext, p, ppkt, plast;
packet sendpkt; int n,dsw;

pnext = str;  plast = pnext + len;
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(dsw);
	 }
  dsw = ustp(task); /*unstop receiver*/
  iff dsw==IE_ITS then dsw = IS_SUC;
 }
  while (pnext<plast);
  return(dsw);

}

int
function vrecv(task, str, len)
taskname *task; charpointer str; int *len;
{
taskname ttask; /*temp for unused return value*/
return(rcvs_(task, str, &ttask, len));
}


int
function vrecva(task, str, len)
taskname *task; charpointer str; int *len;
{
taskname ttask;
ttask = 0; /*receive from any task*/
return(rcvs_(&ttask, str, task, len));
}


/*
  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).
*/

int
function rcvs_(rqst_task, str, rcvd_task, len)
taskname *rqst_task, *rcvd_task;  charpointer str;  int *len;
{
register charpointer pnext, p, ppkt, plast, pfirst;
struct { taskname task; packet pkt; } rcvpkt;
boolean overflow; int dsw,n;

pnext = pfirst = str;
plast = pnext + *len;
overflow = false;
rcvpkt.task = *rqst_task;
do
 {
retry: dsw = rcst(&rcvpkt.task, &rcvpkt); /*receive or stop*/
  iff (dsw==IS_SET) and !tskabo	/* Task stopped & unstopped, no data */
    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 += 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;
    *len = pnext - pfirst;
    /* while (pnext<plast) doo *pnext++ = ' ' */
    return(dsw);
}
