DEFINITION MODULE ITCUTL;   (*$VER 1.0*)

(* ----------------------------------------------------------------------
               Copyright (c) 1987 by Telepath Systems, Inc.
                           All Rights Reserved

              Inter-Task Communication System Access Library

    This library exports high-level facilities for message processing
    through the AMOS Inter-Task Communication System. Although these
    routines are designed only for messaging on the same system they
    may be easily modified for netword use.
  ---------------------------------------------------------------------- *)

FROM SYSTEM IMPORT BYTE;


CONST
 (* Define more meaningful result constants *)

 MsgSuccess      = 0D;                 (* operation was successful       *)
 SocketNotOpen   = 1D;                 (* no socket open for sender      *)
 NoPendingMsg    = 2D;                 (* no messages are pending        *)
 NotSupported    = 3D;                 (* message system not supported   *)
 NetworkNotFound = 4D;                 (* destination netword not found  *)
 NodeNotFound    = 5D;                 (* destination node not found     *)
 SocketNotFound  = 6D;                 (* destination socket not found   *)
 SocketExists    = 7D;                 (* socket already exists          *)
 NoMsgBuffers    = 8D;                 (* no message buffers available   *)
 NoQueueBlocks   = 9D;                 (* no queue blocks available      *)
 ArgOutOfRange   = 10D;                (* arg address outside partition  *)
 SocketFull      = 11D;                (* destinition socket is full     *)
 SocketNotReady  = 12D;                (* destination socket not enabled *)
 MsgTooLong      = 13D;                (* msg length execeeds dest max   *)


PROCEDURE OpenMessage(msglen, msgque: CARDINAL; VAR result: LONGCARD);
 (* Open a message socket to send/receive ITC messages.

    msglen: Size of longest message to be processed.
    msgque: Maximum number of messages that may be pending at one time.
    result: Returns the ITC result code for the operation.
 *)

PROCEDURE CloseMessage(VAR result: LONGCARD);
 (* Close a message socket to end processing.

    result: Returns the ITC result code for the operation.
 *)

PROCEDURE SendMessage(socket: CARDINAL; code: CARDINAL;
                     VAR msg: ARRAY OF BYTE; VAR result: LONGCARD);
 (* Send a message to another socket.

    socket: The socket to send the message to.
    code:   The message application code. This value is set by the
            sender to signal different types of messages within the
            application. This field is ignored by the ITC system.
    msg:    The message to send. It will be truncated if it exceeds the
            size specified with OpenMessage. This is declared as a VAR
            for speed and is not modified by the procedure.
    result: Returns the ITC result code for the operation.
 *)

PROCEDURE SendControlMsg(socket: CARDINAL; code: CARDINAL;
                        VAR result: LONGCARD);
 (* Send a high-priorty message to another socket.

    socket: The socket to send the message to.
    code:   The special control code to send.
    result: Returns the ITC result code for the operation.

    This procedure is used to send application control messages that do
    not require a message data field. These messages are sent with the
    ITC priority flag set so that the message will be inserted at the
    front of the receiver's message queue.
 *)

PROCEDURE RecvMessage(VAR socket: CARDINAL; VAR code: CARDINAL;
                     VAR msg: ARRAY OF BYTE; VAR result: LONGCARD);
 (* Receive a message from another socket.

    socket: Returns the socket number of the sender.
    code:   Returns the message application code given by the sender.
    msg:    The message data received. It will be truncated if msg
            is shorter than the size specified with OpenMessage.
    result: Returns the ITC result code for the operation.
 *)

PROCEDURE WaitMessage(tenths: CARDINAL): BOOLEAN;
 (* Wait for a message to arrive or time-out.

    tenths:  The time to wait in tenths of seconds.
    returns: TRUE if a message has arrived.
 *)

PROCEDURE CheckMessage(VAR msgs: CARDINAL): BOOLEAN;
 (* Check for pending messages.

    msgs:    Returns the number of pending messages.
    returns: TRUE if there are messages pending.
 *)

PROCEDURE FindSocket(name: ARRAY OF CHAR; VAR socket: CARDINAL);
 (* Find the socket number for a job.

    name:   The name of the job to search for.
    socket: The socket number of the job or zero if the job was
            not found.
 *)

END ITCUTL.

================================= CUT HERE =================================

IMPLEMENTATION MODULE ITCUTL;   (*$VER 1.0(101)*)

(* ----------------------------------------------------------------------
               Copyright (c) 1987 by Telepath Systems, Inc.
                           All Rights Reserved

              Inter-Task Communication System Access Library


  History: 1.0 100 12/30/87 MDT Original.
               101 01/12/88 MDT Added VAR to CheckMessage declaration.
  ---------------------------------------------------------------------- *)

FROM SYSTEM   IMPORT ADDRESS, BYTE, ADR, TSIZE, VAL, SHORT, LONG;
FROM SYSITC   IMPORT omske, mspri, OM, MS, MSPTR, OPNMSG, CLSMSG, SNDMSG,
                    RCVMSG, CHKMSG, WTMSG;
FROM SYSCOM   IMPORT SCBPTR;
FROM SYSJOB   IMPORT JCBPTR;
FROM Storage  IMPORT ALLOCATE, DEALLOCATE, Available;
FROM FastData IMPORT Move;
FROM Rad50Lib IMPORT StringToLongRad50;


VAR
 HdrLen: CARDINAL;                     (* message header size            *)
 MsgLen: CARDINAL;                     (* message size without header    *)
 TotLen: CARDINAL;                     (* total message size with header *)
 HdrPtr: MSPTR;                        (* message header pointer         *)
 MsgPtr: POINTER TO BYTE;              (* message buffer pointer         *)


(*** Internal Procedures ***)

PROCEDURE Min(first, second: CARDINAL): CARDINAL;
BEGIN
 IF first < second THEN
   RETURN first;
 ELSE
   RETURN second;
 END;
END Min;


(*** Exported Procedures ***)

PROCEDURE OpenMessage(msglen, msgque: CARDINAL; VAR result: LONGCARD);
VAR
 arg: OM;
BEGIN
 MsgLen := msglen;                     (* save message length            *)
 TotLen := msglen + HdrLen;            (* save size of msg with header   *)

 WITH arg DO                           (* init OPNMSG argument block     *)
   flg := {omske};                     (* enable socket                  *)
   len := TotLen;                      (* message size including header  *)
   max := msgque;                      (* max number of waiting messages *)
   msr := NIL;                         (* no message service routine     *)
 END;

 OPNMSG(ADR(arg), result);             (* open the socket                *)

 IF result = MsgSuccess THEN
   IF Available(TotLen) THEN
     ALLOCATE(HdrPtr,TotLen);          (* allocate msg send/recv buffer  *)
     MsgPtr := ADR(HdrPtr^.spr);       (* AMOS/L 1.3 doesn't have spr    *)
   ELSE
     CLSMSG(result);                   (* close the socket               *)
     result := SocketNotOpen;          (* signal socket didn't open      *)
     HdrPtr := NIL;                    (* invalidate pointer             *)
     MsgPtr := NIL;                    (* invalidate pointer             *)
   END;
 END;
END OpenMessage;

PROCEDURE CloseMessage(VAR result: LONGCARD);
BEGIN
 CLSMSG(result);                       (* close the socket               *)
 IF HdrPtr # NIL THEN
   DEALLOCATE(HdrPtr, TotLen);         (* deallocate msg buffer memory   *)
 END;
END CloseMessage;

PROCEDURE SendMessage(socket: CARDINAL; code: CARDINAL;
                     VAR msg: ARRAY OF BYTE; VAR result: LONGCARD);
VAR
 size: CARDINAL;
BEGIN
 size := Min(MsgLen, SIZE(msg));

 WITH HdrPtr^ DO
   flg := {};                          (* normal message                 *)
   dst.network := 0C;                  (* destination is on same system  *)
   dst.group   := 0C;
   dst.node    := 0;
   dst.socket  := socket;              (* set destination socket         *)
   siz := size + HdrLen;               (* set size of this msg with hdr  *)
   cod := code;                        (* set application message code   *)
 END;

 Move(ADR(msg), MsgPtr, size);         (* put msg in buffer              *)
 SNDMSG(HdrPtr, result, {});           (* send the message               *)
END SendMessage;

PROCEDURE SendControlMsg(socket: CARDINAL; code: CARDINAL;
                        VAR result: LONGCARD);
BEGIN
 WITH HdrPtr^ DO
   flg := {mspri};                     (* set high-priority flag         *)
   dst.network := 0C;                  (* destination is on same system  *)
   dst.group   := 0C;
   dst.node    := 0;
   dst.socket  := socket;              (* set destination socket         *)
   siz := HdrLen;                      (* msg only has a header          *)
   cod := code;                        (* set application message code   *)
 END;

 SNDMSG(HdrPtr, result, {});           (* send the message               *)
END SendControlMsg;

PROCEDURE RecvMessage(VAR socket: CARDINAL; VAR code: CARDINAL;
                     VAR msg: ARRAY OF BYTE; VAR result: LONGCARD);
VAR
 size: CARDINAL;
BEGIN
 RCVMSG(HdrPtr, result, {});           (* attempt to receive the msg     *)

 IF result = MsgSuccess THEN
   WITH HdrPtr^ DO
     socket := src.socket;             (* return sender's socket         *)
     code   := cod;                    (* return sender applicaton code  *)
   END;

   size := Min(MsgLen, SIZE(msg));
   Move(MsgPtr, ADR(msg), size);       (* return msg                     *)
 ELSE
   socket := 0;
   code := 0;
 END;
END RecvMessage;

PROCEDURE WaitMessage(tenths: CARDINAL): BOOLEAN;
BEGIN
 RETURN WTMSG(LONG(tenths) * 1000D);   (* convert 10ths to microseconds  *)
END WaitMessage;

PROCEDURE CheckMessage(VAR msgs: CARDINAL): BOOLEAN;
VAR
 b: BOOLEAN;
 d: LONGCARD;
BEGIN
 b := CHKMSG(d);
 msgs := SHORT(d);
 RETURN b;
END CheckMessage;

PROCEDURE FindSocket(name: ARRAY OF CHAR; VAR socket: CARDINAL);
 (* This procedure returns the socket number for the *)
 (* specified job. This could easily be modified to  *)
 (* match user name or running program name.         *)

VAR
 jcb: JCBPTR;
 jtp: POINTER TO ADDRESS;
 job: LONGCARD;
 cnt: CARDINAL;
 res: BOOLEAN;

BEGIN
 socket := 0; cnt := 1;
 StringToLongRad50(name, job, res);    (* convert name to rad50          *)
 jtp := SCBPTR^.jobtbl;                (* get pointer to job table       *)
 jcb := jtp^;                          (* get first jcb ptr from table   *)

 REPEAT
   IF jcb # NIL THEN                   (* skip unallocated table entries *)
     IF jcb^.jobnam = job THEN         (* found the one we want          *)
       socket := cnt;
     END;
   END;
   INC(cnt);                           (* increment counter              *)
   INC(jtp,4);                         (* increment our job table ptr    *)
   jcb := jtp^;                        (* assign jcb address ptr         *)
 UNTIL (jcb = VAL(ADDRESS,-1D)) OR (socket # 0);
END FindSocket;


BEGIN  (*main*)
 (* The size of HdrLen is apparently changing in AMOS/L 2.0 and may be   *)
 (* different in AMOS/32. Probably will add a procedure to set this for  *)
 (* each version once it is clear what the lengths should be. This will  *)
 (* work for AMOS/L 1.3 and maybe AMOS/32 1.0 for now.                   *)

 HdrLen := TSIZE(MS) - 8;              (* AMOS/L 1.3 doesn't have spr    *)

 HdrPtr := NIL;
 MsgPtr := NIL;
END ITCUTL.