(* Library module defined by the International Standard
   Information technology - programming languages
   BS ISO/IEC 10514-1:1996E Part 1: Modula-2, Base Language.

   Copyright ISO/IEC (International Organization for Standardization
   and International Electrotechnical Commission) 1996-2021.

   It may be freely copied for the purpose of implementation (see page
   707 of the Information technology - Programming languages Part 1:
   Modula-2, Base Language.  BS ISO/IEC 10514-1:1996).  *)

DEFINITION MODULE IOLink;

(* Types and procedures for the standard implementation of channels *)

IMPORT IOChan, IOConsts, ChanConsts, SYSTEM;

TYPE
  DeviceId;
    (* Values of this type are used to identify new device modules,
       and are normally obtained by them during their initialization.
    *)

PROCEDURE AllocateDeviceId (VAR did: DeviceId);
  (* Allocates a unique value of type DeviceId, and assigns this
     value to did. *)

PROCEDURE MakeChan (did: DeviceId; VAR cid: IOChan.ChanId);
  (* Attempts to make a new channel for the device module identified
     by did. If no more channels can be made, the identity of
     the invalid channel is assigned to cid.  Otherwise, the identity
     of a new channel is assigned to cid.
  *)

PROCEDURE UnMakeChan (did: DeviceId; VAR cid: IOChan.ChanId);
  (* If the device module identified by did is not the module that
     made the channel identified by cid, the exception wrongDevice is
     raised; otherwise the channel is deallocated, and the value
     identifying the invalid channel is assigned to cid.
  *)

TYPE
  DeviceTablePtr = POINTER TO DeviceTable;
    (* Values of this type are used to refer to device tables *)

TYPE
  LookProc      = PROCEDURE (DeviceTablePtr, VAR CHAR, VAR IOConsts.ReadResults) ;
  SkipProc      = PROCEDURE (DeviceTablePtr) ;
  SkipLookProc  = PROCEDURE (DeviceTablePtr, VAR CHAR, VAR IOConsts.ReadResults) ;
  WriteLnProc   = PROCEDURE (DeviceTablePtr) ;
  TextReadProc  = PROCEDURE (DeviceTablePtr, SYSTEM.ADDRESS, CARDINAL, VAR CARDINAL) ;
  TextWriteProc = PROCEDURE (DeviceTablePtr, SYSTEM.ADDRESS, CARDINAL) ;
  RawReadProc   = PROCEDURE (DeviceTablePtr, SYSTEM.ADDRESS, CARDINAL, VAR CARDINAL) ;
  RawWriteProc  = PROCEDURE (DeviceTablePtr, SYSTEM.ADDRESS, CARDINAL) ;
  GetNameProc   = PROCEDURE (DeviceTablePtr, VAR ARRAY OF CHAR) ;
  ResetProc     = PROCEDURE (DeviceTablePtr) ;
  FlushProc     = PROCEDURE (DeviceTablePtr) ;
  FreeProc      = PROCEDURE (DeviceTablePtr) ;
     (* Carry out the operations involved in closing the corresponding
        channel, including flushing buffers, but do not unmake the
        channel.
     *)


TYPE
  DeviceData = SYSTEM.ADDRESS;

  DeviceTable =
    RECORD                         (* Initialized by MakeChan to: *)
      cd: DeviceData;              (* the value NIL *)
      did: DeviceId;               (* the value given in the call of MakeChan *)
      cid: IOChan.ChanId;          (* the identity of the channel *)
      result: IOConsts.ReadResults;(* the value notKnown *)
      errNum: IOChan.DeviceErrNum; (* undefined *)
      flags: ChanConsts.FlagSet;   (* ChanConsts.FlagSet{} *)
      doLook: LookProc;            (* raise exception notAvailable *)
      doSkip: SkipProc;            (* raise exception notAvailable *)
      doSkipLook: SkipLookProc;    (* raise exception notAvailable *)
      doLnWrite: WriteLnProc;      (* raise exception notAvailable *)
      doTextRead: TextReadProc;    (* raise exception notAvailable *)
      doTextWrite: TextWriteProc;  (* raise exception notAvailable *)
      doRawRead: RawReadProc;      (* raise exception notAvailable *)
      doRawWrite: RawWriteProc;    (* raise exception notAvailable *)
      doGetName: GetNameProc;      (* return the empty string *)
      doReset: ResetProc;          (* do nothing *)
      doFlush: FlushProc;          (* do nothing *)
      doFree: FreeProc;            (* do nothing *)
    END;


  (* The pointer to the device table for a channel is obtained using the
     following procedure: *)

(*
   If the device module identified by did is not the module that made
   the channel identified by cid, the exception wrongDevice is raised.
*)

PROCEDURE DeviceTablePtrValue (cid: IOChan.ChanId; did: DeviceId): DeviceTablePtr;


(*
   Tests if the device module identified by did is the module
   that made the channel identified by cid.
*)

PROCEDURE IsDevice (cid: IOChan.ChanId; did: DeviceId) : BOOLEAN;


TYPE
  DevExceptionRange = IOChan.ChanExceptions;

(*
  ISO standard states defines

  DevExceptionRange = [IOChan.notAvailable ..  IOChan.textParseError];

  however this must be a bug as other modules need to raise
  IOChan.wrongDevice exceptions.
*)

PROCEDURE RAISEdevException (cid: IOChan.ChanId; did: DeviceId;
                             x: DevExceptionRange; s: ARRAY OF CHAR) <* noreturn *> ;

  (* If the device module identified by did is not the module that made the channel
     identified by cid, the exception wrongDevice is raised; otherwise the given exception
     is raised, and the string value in s is included in the exception message.
  *)

PROCEDURE IsIOException () : BOOLEAN;
  (* Returns TRUE if the current coroutine is in the exceptional execution state
     because of the raising af an exception from ChanExceptions;
     otherwise FALSE.
  *)

PROCEDURE IOException () : IOChan.ChanExceptions;
  (* If the current coroutine is in the exceptional execution state because of the
     raising af an exception from ChanExceptions, returns the corresponding
     enumeration value, and otherwise raises an exception.
  *)

END IOLink.
