MODULE SocketLib;

(*---------------------------------------------------------------------------
** Wrapping module to BSDSocket. It takes care of the base arbitration
** when using BSDSocket from parallel tasks.
**
** The programming model shows the socket library as a record with a couple
** of type-bound procedures.
**
** The original idea was to protect the calls to the bsdlibrary with
** semaphores, but tests showed deadlocks when doing blocking read/writes
** from parallel tasks. So the arbitration was disabled, but can be
** reactivated by setting the compiler option 'UseSema'.
**
** Copyright © 1993 Lars Düning
** Copyright © 1993 AmiTCP/IP Group, <amitcp@hut.fi>.
**                  Helsinki University of Technology, Finland.
**                  All rights reserved.
**---------------------------------------------------------------------------
** Oberon-2: Amiga-Oberon v3.00, F. Siebert / A+L AG
**---------------------------------------------------------------------------
** 28-Jun-93 [lars]
** 21-Jul-93 [lars] Updated to version 2.0
** 23-Aug-93 [lars] Fixed parameter error in Release(CopyOf)Socket()
** 24-May-94 [lars] Small bugfixes
**---------------------------------------------------------------------------
*)

(* $StackChk- $RangeChk- $ReturnChk- $OvflChk- *)

IMPORT
  bsd:BSDSocket, Errors, NetDB, NetInet, Sockets,
  Conversions, Dos, Exec, Timer, Utility, SYSTEM;

(*-------------------------------------------------------------------------*)

CONST

    (* Result codes from Open() *)

  success    *= bsd.success;     (* Success *)
  wrongOS    *= bsd.wrongOS;     (* Failure: wrong OS-Version. *)
  cantOpen   *= bsd.cantOpen;    (* Failure: OpenLibrary() failed. *)
  cantObtain *= bsd.cantObtain;  (* Semi-Failure: can't obtain socket from launcher *)


(*-------------------------------------------------------------------------*)

TYPE
  LibPtr *= POINTER TO Lib;

    (* Record used to store the relevant data for one task *)
  Lib *= RECORD
    next-, prev- : LibPtr;
    task   -: Exec.TaskPtr;     (* the task this record is associated with *)
    base   -: Exec.LibraryPtr;  (* the task's bsdsocket library pointer *)
    server -: LONGINT;          (* for daemons: the socket provided by launcher *)
    open   -: LONGINT;          (* open count *)
    errno  *: LONGINT;          (* this tasks errno *)
    daemon -: BOOLEAN;          (* TRUE if this is for a daemon *)
  END;

VAR
  list -: LibPtr;  (* Ring of all records *)

(*-------------------------------------------------------------------------*)
PROCEDURE Open * (daemon : BOOLEAN) : LibPtr;

(* FUNCTION
**    Creates a Lib record for this task, opens the bsdsocket.library and
**    stores the base pointer into the Lib record.
**    Multiple calls to Open() are nested.
**
** INPUT
**    daemon: TRUE for daemon-started programs, FALSE else.
**
** RESULT
**    Pointer to the Lib record for this task.
**    Lib.base is set to the bsdsocket.library base on success, else it is NIL.
**    Lib.errno holds the success code (suitable for BSDSocket.OpenFailure()):
**      success   : Success
**      wrongOS   : Failure: wrong OS-Version.
**      cantOpen  : Failure: OpenLibrary() failed.
**      cantObtain: Semi-Failure: Library opened, but can't obtain socket from
**                                launcher
*)

  VAR
    lib : LibPtr;
    me  : Dos.ProcessPtr;
    dm  : bsd.DaemonMessagePtr;
BEGIN
  Exec.Forbid();
    me := Exec.FindTask(NIL);
    lib := list;
    WHILE (lib.task # me) & (lib.next # list) DO lib := lib.next; END;
    IF lib.task # me THEN
      NEW(lib);
      lib.next := list.next; lib.prev := list;
      list.next.prev := lib; list.next := lib;
      lib.task := me;
      lib.base := NIL;
      lib.errno := success;
      lib.server := -1;
      lib.open := 0;
      lib.daemon := FALSE;
    END;
  Exec.Permit();

  IF lib.open > 0 THEN INC(lib.open); RETURN lib; END;

    (* Check OS version *)
  IF Exec.exec.libNode.version < 37 THEN lib.errno := wrongOS; RETURN lib; END;

    (* Open bsdsocket.library *)
  lib.base := Exec.OpenLibrary (bsd.Name, bsd.Version);
  IF lib.base = NIL THEN lib.errno := cantOpen; RETURN lib; END;

    (* Successfull. Now tell bsdsocket.library the address of our errno *)

  INC(lib.open);
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    bsd.SetErrnoPtr (lib.errno, SIZE(lib.errno));
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)

  IF ~daemon THEN RETURN lib; END;

    (* Get the socket from the inetd daemon. *)

  lib.daemon := TRUE;
  dm := SYSTEM.VAL(bsd.DaemonMessagePtr, me.exitData);
  IF (dm # NIL) & (lib.server = -1) THEN
    lib.server := bsd.ObtainSocket(dm.id, dm.family, dm.type, 0);
    IF lib.server = -1 THEN lib.errno := cantObtain; END;
  END;
  RETURN lib;
END Open;

(*-------------------------------------------------------------------------*)
PROCEDURE (lib : LibPtr) OpenFailure *;

(* Take the result from Open() and if its a failure, pop up a
** requester with an approbiate message, then HALT().
*)

BEGIN
  bsd.OpenFailure(SHORT(lib.errno), lib.daemon);
END OpenFailure;

(*-------------------------------------------------------------------------*)
PROCEDURE (lib : LibPtr) Close *;

(* FUNCTION
**     Close the bsdsocket.library opened for this task.
**     Nesting against Open() is taken into account.
**
** COMMENT
**     The record is deallocated when the open count reaches zero.
**     The only exception is the <list> record.
**
** RESULT
**     lib becomes invalid.
*)

BEGIN
  IF lib.open > 0 THEN
    DEC(lib.open);
    IF lib.open = 0 THEN
      Exec.CloseLibrary(lib.base);
      lib.base := NIL;
      lib.server := -1;
      lib.daemon := FALSE;
    END;
  END;
  IF (lib.open = 0) & (lib # list) THEN
    lib.prev.next := lib.next;
    lib.next.prev := lib.prev;
    (* $IFNOT GarbageCollector *)
      DISPOSE(lib);
    (* $END *)
  ELSE
    lib.errno := 0;
  END;
END Close;

(*-------------------------------------------------------------------------*)
PROCEDURE (lib : LibPtr) Socket * (domain, type, protocol : LONGINT) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.Socket(domain,type,protocol);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END Socket;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) Bind * (       s : LONGINT
                                ;    name : ARRAY OF SYSTEM.BYTE (* Sockets.SockAddr *)
                                ; namelen : LONGINT) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.Bind(s, name, namelen);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END Bind;

(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) bind * (    s : LONGINT
                                ; name : ARRAY OF SYSTEM.BYTE (* Sockets.SockAddr *)
                                ) : LONGINT;
BEGIN
  RETURN lib.Bind(s, name, LEN(name));
END bind;

(*-------------------------------------------------------------------------*)
PROCEDURE (lib : LibPtr) Listen * (s, backlog : LONGINT) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.Listen(s, backlog);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END Listen;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) Accept * (           s : LONGINT
                                  ; VAR    addr : ARRAY OF SYSTEM.BYTE (* Sockets.SockAddr *)
                                  ; VAR addrlen : LONGINT) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.Accept(s, addr, addrlen);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END Accept;

(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) accept * (           s : LONGINT
                                  ; VAR    addr : ARRAY OF SYSTEM.BYTE (* Sockets.SockAddr *)
                                  ; VAR addrlen : LONGINT) : LONGINT;
BEGIN
  addrlen := LEN(addr);
  RETURN lib.Accept(s, addr, addrlen);
END accept;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) Connect * (       s : LONGINT
                                   ;    name : ARRAY OF SYSTEM.BYTE (* Sockets.SockAddr *)
                                   ; namelen : LONGINT) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.Connect(s, name, namelen);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END Connect;

(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) connect * (       s : LONGINT
                                   ;    name : ARRAY OF SYSTEM.BYTE (* Sockets.SockAddr *)
                                   ) : LONGINT;
BEGIN
  RETURN lib.Connect(s, name, LEN(name));
END connect;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) SendTo * (     s : LONGINT
                                  ;   msg : ARRAY OF SYSTEM.BYTE
                                  ;   len : LONGINT
                                  ; flags : LONGSET
                                  ;    to : ARRAY OF SYSTEM.BYTE (* Sockets.SockAddr *)
                                  ; tolen : LONGINT) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.SendTo(s, msg, len, flags, to, tolen);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END SendTo;

(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) sendTo * (     s : LONGINT
                                  ;   msg : ARRAY OF SYSTEM.BYTE
                                  ;   len : LONGINT
                                  ; flags : LONGSET
                                  ;    to : ARRAY OF SYSTEM.BYTE (* Sockets.SockAddr *)
                                  ) : LONGINT;
BEGIN
  RETURN lib.SendTo(s, msg, len, flags, to, LEN(to));
END sendTo;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) SendBufTo * (     s : LONGINT
                                     ;   buf : ARRAY OF SYSTEM.BYTE
                                     ; flags : LONGSET
                                     ;    to : ARRAY OF SYSTEM.BYTE (* Sockets.SockAddr *)
                                     ; tolen : LONGINT) : LONGINT;
BEGIN
  RETURN lib.SendTo(s, buf, LEN(buf), flags, to, LEN(to));
END SendBufTo;

(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) sendBufTo * (     s : LONGINT
                                     ;   buf : ARRAY OF SYSTEM.BYTE
                                     ; flags : LONGSET
                                     ;    to : ARRAY OF SYSTEM.BYTE (* Sockets.SockAddr *)
                                     ) : LONGINT;
BEGIN
  RETURN lib.SendTo(s, buf, LEN(buf), flags, to, LEN(to));
END sendBufTo;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) Send * (     s : LONGINT
                                ;   msg : ARRAY OF SYSTEM.BYTE
                                ;   len : LONGINT
                                ; flags : LONGSET) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.Send(s, msg, len, flags);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END Send;

(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) SendBuf * (     s : LONGINT
                                   ;   buf : ARRAY OF SYSTEM.BYTE
                                   ; flags : LONGSET) : LONGINT;
BEGIN
  RETURN lib.Send(s, buf, LEN(buf), flags);
END SendBuf;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) RecvFrom * (           s : LONGINT
                                    ; VAR     buf : ARRAY OF SYSTEM.BYTE
                                    ;         len : LONGINT
                                    ;       flags : LONGSET
                                    ; VAR    from : ARRAY OF SYSTEM.BYTE (* Sockets.SockAddr *)
                                    ; VAR fromlen : LONGINT) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.RecvFrom(s, buf, len, flags, from, fromlen);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END RecvFrom;

(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) recvFrom * (           s : LONGINT
                                    ; VAR     buf : ARRAY OF SYSTEM.BYTE
                                    ;         len : LONGINT
                                    ;       flags : LONGSET
                                    ; VAR    from : ARRAY OF SYSTEM.BYTE (* Sockets.SockAddr *)
                                    ; VAR fromlen : LONGINT
                                    ) : LONGINT;
BEGIN
  fromlen := LEN(from);
  RETURN lib.RecvFrom(s, buf, len, flags, from, fromlen);
END recvFrom;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) RecvBufFrom * (           s : LONGINT
                                       ; VAR     buf : ARRAY OF SYSTEM.BYTE
                                       ;       flags : LONGSET
                                       ; VAR    from : ARRAY OF SYSTEM.BYTE (* Sockets.SockAddr *)
                                       ; VAR fromlen : LONGINT) : LONGINT;
BEGIN
  RETURN lib.RecvFrom(s, buf, LEN(buf), flags, from, fromlen);
END RecvBufFrom;

(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) recvBufFrom * (           s : LONGINT
                                       ; VAR     buf : ARRAY OF SYSTEM.BYTE
                                       ;       flags : LONGSET
                                       ; VAR    from : ARRAY OF SYSTEM.BYTE (* Sockets.SockAddr *)
                                       ; VAR fromlen : LONGINT
                                       ) : LONGINT;
BEGIN
  fromlen := LEN(from);
  RETURN lib.RecvFrom(s, buf, LEN(buf), flags, from, fromlen);
END recvBufFrom;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) Recv * (         s : LONGINT
                                ; VAR   buf : ARRAY OF SYSTEM.BYTE
                                ;       len : LONGINT
                                ;     flags : LONGINT) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.Recv(s, buf, len, flags);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END Recv;

(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) RecvBuf * (         s : LONGINT
                                   ; VAR   buf : ARRAY OF SYSTEM.BYTE
                                   ;     flags : LONGINT) : LONGINT;
BEGIN
  RETURN lib.Recv(s, buf, LEN(buf), flags);
END RecvBuf;

(*-------------------------------------------------------------------------*)
PROCEDURE (lib : LibPtr) Shutdown * (s, how : LONGINT) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.Shutdown(s, how);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END Shutdown;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) SetSockOpt * (       s : LONGINT
                                      ;   level : LONGINT
                                      ; optname : LONGINT
                                      ;  optval : ARRAY OF SYSTEM.BYTE
                                      ;  optlen : LONGINT) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.SetSockOpt(s, level, optname, optval, optlen);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END SetSockOpt;

(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) setSockOpt * (       s : LONGINT
                                      ;   level : LONGINT
                                      ; optname : LONGINT
                                      ;  optval : ARRAY OF SYSTEM.BYTE
                                      ) : LONGINT;
BEGIN
  RETURN lib.SetSockOpt(s, level, optname, optval, LEN(optval));
END setSockOpt;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) GetSockOpt * (           s : LONGINT
                                      ;       level : LONGINT
                                      ;     optname : LONGINT
                                      ; VAR  optval : ARRAY OF SYSTEM.BYTE
                                      ; VAR  optlen : LONGINT) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.GetSockOpt(s, level, optname, optval, optlen);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END GetSockOpt;

(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) getSockOpt * (           s : LONGINT
                                      ;       level : LONGINT
                                      ;     optname : LONGINT
                                      ; VAR  optval : ARRAY OF SYSTEM.BYTE
                                      ; VAR  optlen : LONGINT) : LONGINT;
BEGIN
  optlen := LEN(optval);
  RETURN lib.GetSockOpt(s, level, optname, optval, optlen);
END getSockOpt;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) GetSockName * (           s : LONGINT
                                       ; VAR    name : ARRAY OF SYSTEM.BYTE (* Sockets.SockAddr *)
                                       ; VAR namelen : LONGINT) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.GetSockName(s, name, namelen);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END GetSockName;

(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) getSockName * (           s : LONGINT
                                       ; VAR    name : ARRAY OF SYSTEM.BYTE (* Sockets.SockAddr *)
                                       ; VAR namelen : LONGINT) : LONGINT;
BEGIN
  namelen := LEN(name);
  RETURN lib.GetSockName(s, name, namelen);
END getSockName;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) GetPeerName * (           s : LONGINT
                                       ; VAR    name : ARRAY OF SYSTEM.BYTE (* Sockets.SockAddr *)
                                       ; VAR namelen : LONGINT) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.GetPeerName(s, name, namelen);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END GetPeerName;

(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) getPeerName * (           s : LONGINT
                                       ; VAR    name : ARRAY OF SYSTEM.BYTE (* Sockets.SockAddr *)
                                       ; VAR namelen : LONGINT) : LONGINT;
BEGIN
  namelen := LEN(name);
  RETURN lib.GetPeerName(s, name, namelen);
END getPeerName;

(*-------------------------------------------------------------------------*)
PROCEDURE (lib : LibPtr) CloseSocket * (d : LONGINT) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.CloseSocket(d);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END CloseSocket;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) WaitSelect * (         nfds : LONGINT
                                      ; VAR  readfds : ARRAY OF bsd.FdWord
                                      ; VAR writefds : ARRAY OF bsd.FdWord
                                      ; VAR exeptfds : ARRAY OF bsd.FdWord
                                      ;      timeout : Timer.TimeVal
                                      ; VAR    maskp : LONGSET) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.WaitSelect(nfds, readfds, writefds, exeptfds, timeout, maskp);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END WaitSelect;

(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) waitSelect * (         nfds : LONGINT
                                      ; VAR  readfds : ARRAY OF bsd.FdWord
                                      ; VAR writefds : ARRAY OF bsd.FdWord
                                      ; VAR exeptfds : ARRAY OF bsd.FdWord
                                      ;      timeout : Timer.TimeValPtr
                                      ; VAR    maskp : LONGSET) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.waitSelect(nfds, readfds, writefds, exeptfds, timeout, maskp);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END waitSelect;

(*-------------------------------------------------------------------------*)
PROCEDURE (lib : LibPtr) SetDTableSize * (size : LONGINT) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.SetDTableSize(size);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END SetDTableSize;

(*-------------------------------------------------------------------------*)
PROCEDURE (lib : LibPtr) SetSocketSignals * (sigIntr, sigIO, sigUrg : LONGSET);
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    bsd.SetSocketSignals(sigIntr, sigIO, sigUrg);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
END SetSocketSignals;

(*-------------------------------------------------------------------------*)
PROCEDURE (lib : LibPtr) Errno * () : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.Errno();
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END Errno;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) SetErrnoPtr * ( errnop : ARRAY OF SYSTEM.BYTE
                                       ;   size : LONGINT);
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    bsd.SetErrnoPtr(errnop, size);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
END SetErrnoPtr;

(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) setErrnoPtr * (errnop : ARRAY OF SYSTEM.BYTE);
BEGIN
  lib.SetErrnoPtr(errnop, LEN(errnop));
END setErrnoPtr;

(*-------------------------------------------------------------------------*)
PROCEDURE (lib : LibPtr) ReleaseSocket * (fd, id : LONGINT) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.ReleaseSocket(fd, id);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END ReleaseSocket;

(*-------------------------------------------------------------------------*)
PROCEDURE (lib : LibPtr) ObtainSocket * ( id, domain, type, protocol : LONGINT
                                        ) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.ObtainSocket(id, domain, type, protocol);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END ObtainSocket;

(*-------------------------------------------------------------------------*)
PROCEDURE (lib : LibPtr) ReleaseCopyOfSocket * (fd, id : LONGINT) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.ReleaseCopyOfSocket(fd, id);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END ReleaseCopyOfSocket;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) Ioctl * (           d : LONGINT
                                 ;     request : LONGINT
                                 ; VAR    argp : ARRAY OF SYSTEM.BYTE
                                 ) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.Ioctl(d, request, argp);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END Ioctl;

(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) IoctlSocket * (           d : LONGINT
                                       ;     request : LONGINT
                                       ; VAR    argp : ARRAY OF SYSTEM.BYTE
                                       ) : LONGINT;
BEGIN RETURN lib.Ioctl (d, request, argp); END IoctlSocket;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) InetAddr * (name : ARRAY OF CHAR) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.InetAddr(name);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END InetAddr;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) InetNetwork * (name : ARRAY OF CHAR) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.InetNetwork(name);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END InetNetwork;

(*-------------------------------------------------------------------------*)
PROCEDURE (lib : LibPtr) InetNtoA * (addr : LONGINT) : Exec.STRPTR;
  VAR rc : Exec.STRPTR;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.InetNtoA(addr);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END InetNtoA;

(*-------------------------------------------------------------------------*)
PROCEDURE (lib : LibPtr) InetMakeAddr * (net, host : LONGINT) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.InetMakeAddr(net, host);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END InetMakeAddr;

(*-------------------------------------------------------------------------*)
PROCEDURE (lib : LibPtr) InetLnaOf * (addr : LONGINT) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.InetLnaOf(addr);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END InetLnaOf;

(*-------------------------------------------------------------------------*)
PROCEDURE (lib : LibPtr) InetNetOf * (addr : LONGINT) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.InetNetOf(addr);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END InetNetOf;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) GetHostByName * (name : ARRAY OF CHAR) : NetDB.HostEntPtr;
  VAR rc : NetDB.HostEntPtr;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.GetHostByName(name);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END GetHostByName;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) GetHostByAddr * ( addr : ARRAY OF SYSTEM.BYTE
                                         ;  len : LONGINT
                                         ; type : LONGINT) : NetDB.HostEntPtr;
  VAR rc : NetDB.HostEntPtr;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.GetHostByAddr(addr, len, type);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END GetHostByAddr;

(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) getHostByAddr * ( addr : ARRAY OF SYSTEM.BYTE
                                         ; type : LONGINT) : NetDB.HostEntPtr;
BEGIN
  RETURN lib.GetHostByAddr(addr, LEN(addr), type);
END getHostByAddr;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) GetNetByName * (name : ARRAY OF CHAR) : NetDB.NetEntPtr;
  VAR rc : NetDB.NetEntPtr;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.GetNetByName(name);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END GetNetByName;

(*-------------------------------------------------------------------------*)
PROCEDURE (lib : LibPtr) GetNetByAddr * (net, type : LONGINT) : NetDB.NetEntPtr;
  VAR rc : NetDB.NetEntPtr;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.GetNetByAddr(net, type);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END GetNetByAddr;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) GetServByName * ( name : ARRAY OF CHAR
                                         ; proto : ARRAY OF CHAR
                                         ) : NetDB.ServEntPtr;
  VAR rc : NetDB.ServEntPtr;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.GetServByName(name, proto);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END GetServByName;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) GetServByPort * ( port : LONGINT
                                         ; proto : ARRAY OF CHAR
                                         ) : NetDB.ServEntPtr;
  VAR rc : NetDB.ServEntPtr;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.GetServByPort(port, proto);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END GetServByPort;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) GetProtoByName * (name : ARRAY OF CHAR) : NetDB.ProtoEntPtr;
  VAR rc : NetDB.ProtoEntPtr;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.GetProtoByName(name);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END GetProtoByName;

(*-------------------------------------------------------------------------*)
PROCEDURE (lib : LibPtr) GetProtoByNumber * (proto : LONGINT) : NetDB.ProtoEntPtr;
  VAR rc : NetDB.ProtoEntPtr;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.GetProtoByNumber(proto);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END GetProtoByNumber;

(*-------------------------------------------------------------------------*)
PROCEDURE SyslogPrivate * (    lib{1} : LibPtr
                          ;  level{0} : LONGINT
                          ; format{8} : Exec.STRPTR
                          ;   data{9} : SYSTEM.ADDRESS
                          );
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    bsd.syslog(level, format, data);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
END SyslogPrivate;

PROCEDURE syslog * {"SocketLib.SyslogPrivate"} (    lib{1}   : LibPtr
                                               ;  level{0}   : LONGINT
                                               ; format{8}   : Exec.STRPTR
                                               ;   tag1{9}.. : Utility.Tag);
(* ..you don't want to know the inner details.. *)

(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) Syslog * (   level : LONGINT
                                  ;  format : Exec.STRPTR
                                  ; tagList : ARRAY OF Utility.TagItem);
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    bsd.Syslog(level, format, tagList);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
END Syslog;

(*-------------------------------------------------------------------------*)
PROCEDURE (lib : LibPtr) Dup2Socket * (fd1, fd2 : LONGINT) : LONGINT;
  VAR rc : LONGINT;
BEGIN
(* $IF UseSema *)
  Exec.ObtainSemaphore(bsd.baseSema);
(* $END *)
    bsd.base := lib.base;
    rc := bsd.Dup2Socket(fd1, fd2);
(* $IF UseSema *)
  Exec.ReleaseSemaphore(bsd.baseSema);
(* $END *)
  RETURN rc;
END Dup2Socket;

(*-------------------------------------------------------------------------*)
PROCEDURE (lib : LibPtr) GetHostName * (VAR name : ARRAY OF CHAR) : BOOLEAN;

(*   NAME
**       GetHostName -- get the name of the host.
**
**   FUNCTION
**       Get the name of the host to the buffer name of length namelen.
**       The name is taken from the environment variable "HOSTNAME"
**       where it SHOULD reside.
**
**   INPUTS
**       name    - Buffer where the name should be stored.
**
**   RESULT
**       error   - TRUE on success, FALSE in case of an error. The global
**                 variable <errno> will be set to indicate the error as
**                 follows:
**
**                 Errors.noEnt - The environment variable "HOSTNAME" is not
**                                found.
*)

BEGIN
  IF Dos.GetVar("HOSTNAME", name, LEN(name), LONGSET{}) < 0 THEN
    lib.errno := Errors.noEnt;
    RETURN FALSE;
  END;
  RETURN TRUE;
END GetHostName;

(*-------------------------------------------------------------------------*)
(* $CopyArrays- *)
PROCEDURE (lib : LibPtr) ServerAccept * (     name : ARRAY OF CHAR
                                        ; VAR peer : NetInet.SockAddr
                                        ) : LONGINT;

(*  FUNCTION
**       The serveraccept() library call binds a socket to the named Internet
**       TCP port. Then it listens the socket and accepts the connection to
**       the port. The peer's socket address is returned in sockaddr pointed
**       by sockaddr argument.
**
**       The port name is resolved by getservbyname() call. A numeric value
**       for port name is also accepted.
**
**       This module is meant for daemon developing.
**
**  INPUTS
**       name     - port name or numeric string.
**
**  RESULT
**        socket - positive socket id for success or -1 for failure.
**
**        peer   - Sockets.Addr structure containing peer's internet address.
**                 Note that on error, the structure containing peer address
**                 is not necessarily updated.
*)

  VAR
    sin     : NetInet.SockAddr;
    peerLen : LONGINT;
    s, sa   : LONGINT;
    port    : LONGINT;
    sp      : NetDB.ServEntPtr;
    on      : LONGINT;
BEGIN
    (* Create address corresponding our service *)
  sin.a.len := SIZE(NetInet.SockAddr);
  sin.a.family := Sockets.afInet;

    (* A port must be in the range 1 - 65535 *)
  IF Conversions.StrToInt (name, port, 10) & (port < 65536) & (port >= 0)
  THEN
    sin.port := SHORT(port);
  ELSE
    sp := lib.GetServByName(name, "tcp");
    IF sp = NIL THEN RETURN -1; END;
    sin.port := SHORT(sp.port);
  END;
  sin.addr := NetInet.AddrAny;

  s := lib.Socket(Sockets.afInet, Sockets.stream, 0);
  IF s < 0 THEN
    Errors.WriteFault (lib.Errno(), "ServerAccept.Socket");
    RETURN -1;
  END;

    (* Reuse this port *)
  on := 1;
  IF lib.SetSockOpt(s, Sockets.socket, Sockets.reuseAddr, on, SIZE(on)) < 0 THEN
    Errors.WriteFault(lib.Errno(), "ServerAccept.SetSockOpt");
    SYSTEM.SETREG(0, lib.CloseSocket(s));
    RETURN -1;
  END;

    (* Bind it to socket *)
  IF lib.Bind(s, sin, SIZE(NetInet.SockAddr)) < 0 THEN
    Errors.WriteFault(lib.Errno(), "ServerAccept.Bind");
    SYSTEM.SETREG(0, lib.CloseSocket(s));
    RETURN -1;
  END;

  IF lib.Listen(s, 1) < 0 THEN
    Errors.WriteFault(lib.Errno(), "ServerAccept.Listen");
    SYSTEM.SETREG(0, lib.CloseSocket(s));
    RETURN -1;
  END;

  peerLen := SIZE(NetInet.SockAddr);
  sa := lib.Accept(s, peer, peerLen);
  IF sa >= 0 THEN
    RETURN sa;
  END;
  Errors.WriteFault(lib.Errno(), "ServerAccept.Accept");
  SYSTEM.SETREG(0, lib.CloseSocket(s));
  RETURN -1;
END ServerAccept;

(*-------------------------------------------------------------------------*)

BEGIN
  NEW(list);
  list.next := list;
  list.prev := list;
  list.task := Exec.FindTask(NIL);
  list.base := NIL;
  list.open := 0;
  list.daemon := FALSE;
  list.server := -1;

CLOSE
  IF list # NIL THEN
    WHILE list.next # list DO
      IF list.next.open > 0 THEN Exec.CloseLibrary(list.next.base); END;
      list.next := list.next.next;
      (* $IFNOT GarbageCollector *)
        DISPOSE(list.next.prev);
      (* $END *)
    END;
    IF list.open > 0 THEN Exec.CloseLibrary(list.base); END;
    (* $IFNOT GarbageCollector *)
      DISPOSE(list);
    (* $END *)
  END;
  bsd.base := NIL; (* ! *)
END SocketLib.

(***************************************************************************)
