(********************************************************************)
(*                                                                  *)
(*  sockbase.s7i  Support for socket address and primitive socket   *)
(*  Copyright (C) 2007 - 2009, 2011  Thomas Mertes                  *)
(*                                                                  *)
(*  This file is part of the Seed7 Runtime Library.                 *)
(*                                                                  *)
(*  The Seed7 Runtime Library is free software; you can             *)
(*  redistribute it and/or modify it under the terms of the GNU     *)
(*  Lesser General Public License as published by the Free Software *)
(*  Foundation; either version 2.1 of the License, or (at your      *)
(*  option) any later version.                                      *)
(*                                                                  *)
(*  The Seed7 Runtime Library is distributed in the hope that it    *)
(*  will be useful, but WITHOUT ANY WARRANTY; without even the      *)
(*  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR *)
(*  PURPOSE.  See the GNU Lesser General Public License for more    *)
(*  details.                                                        *)
(*                                                                  *)
(*  You should have received a copy of the GNU Lesser General       *)
(*  Public License along with this program; if not, write to the    *)
(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
(*                                                                  *)
(********************************************************************)


(**
 *  Abstract data type to store socket addresses.
 *)
const type: socketAddress is newtype;


IN_PARAM_IS_REFERENCE(socketAddress);

const proc: destroy (ref socketAddress: aValue)                             is action "BST_DESTR";
const proc: (ref socketAddress: dest) ::= (in socketAddress: source)        is action "BST_CREATE";
const proc: (inout socketAddress: dest) := (in socketAddress: source)       is action "BST_CPY";

const func socketAddress: _GENERATE_EMPTY_SOCKET_ADDRESS                    is action "BST_EMPTY";
const socketAddress: (attr socketAddress) . value                           is _GENERATE_EMPTY_SOCKET_ADDRESS;

const func boolean: (in socketAddress: addr1) = (in socketAddress: addr2) is action "BST_EQ";
const func boolean: (in socketAddress: addr1) <> (in socketAddress: addr2) is action "BST_NE";

const func integer: addrFamily (in socketAddress: address)                  is action "SOC_ADDR_FAMILY";


(**
 *  Compare two socket addresses.
 *  @return -1, 0 or 1 if the first argument is considered to be
 *          respectively less than, equal to, or greater than the
 *          second.
 *)
const func integer: compare (in socketAddress: addr1, in socketAddress: addr2) is action "BST_CMP";


(**
 *  Compute the hash value of a socket address.
 *  @return the hash value.
 *)
const func integer: hashCode (in socketAddress: address)                    is action "BST_HASHCODE";


(**
 *  Get the numeric (IP) address of the host at ''address''.
 *  IPv4 addresses return the socketAddress in dot notation (e.g.:
 *  "192.0.2.235") and IPv6 addresses return the socketAddress in
 *  colon notation (e.g.: "fe80:0:0:0:202:b3ff:fe1e:8329").
 *  @return the IP address of the specified host.
 *  @exception RANGE_ERROR The address is neither AF_INET nor AF_INET6.
 *)
const func string: numericAddress (in socketAddress: address)               is action "SOC_ADDR_NUMERIC";


const func string: service (in socketAddress: address)                      is action "SOC_ADDR_SERVICE";


(**
 *  Create an internet socket address of a port at a host.
 *  The ''hostName'' is either a host name (e.g.: "www.example.org"),
 *  or an IPv4 address in standard dot notation (e.g.: "192.0.2.235").
 *  Operating systems supporting IPv6 may also accept an IPv6 address
 *  in colon notation.
 *  @return the internet socket address, and socketAddress.value if
 *          the host cannot be found.
 *  @exception FILE_ERROR A system function returns an error.
 *  @exception RANGE_ERROR The port is not in the range 0 to 65535 or
 *             hostName cannot be converted to the system string type.
 *  @exception MEMORY_ERROR Not enough memory to convert ''hostName''.
 *             to the system representation or not enough memory to
 *             represent the result.
 *)
const func socketAddress: inetSocketAddress (in string: hostName,
                                             in integer: port)              is action "SOC_INET_ADDR";


(**
 *  Create an internet socket address of a port at localhost.
 *  @return the internet socket address.
 *  @exception FILE_ERROR A system function returns an error.
 *  @exception RANGE_ERROR The port is not in the range 0 to 65535.
 *  @exception MEMORY_ERROR Not enough memory to represent the result.
 *)
const func socketAddress: inetSocketAddress (in integer: port)              is action "SOC_INET_LOCAL_ADDR";


(**
 *  Create an internet listener socket address of a port at localhost.
 *  @return the internet listener socket address.
 *  @exception FILE_ERROR A system function returns an error.
 *  @exception RANGE_ERROR The port is not in the range 0 to 65535.
 *  @exception MEMORY_ERROR Not enough memory to represent the result.
 *)
const func socketAddress: inetListenerAddress (in integer: port)            is action "SOC_INET_SERV_ADDR";


(**
 *  Determine the hostname.
 *  @return the hostname.
 *  @exception MEMORY_ERROR Not enough memory to represent the result.
 *)
const func string: getHostname                                              is action "SOC_GET_HOSTNAME";


const type: PRIMITIVE_SOCKET is newtype;
IN_PARAM_IS_VALUE(PRIMITIVE_SOCKET);

const proc: destroy (ref PRIMITIVE_SOCKET: aValue)                          is action "GEN_DESTR";
const proc: (ref PRIMITIVE_SOCKET: dest) ::= (in PRIMITIVE_SOCKET: source)  is action "SOC_CREATE";
const proc: (inout PRIMITIVE_SOCKET: dest) := (in PRIMITIVE_SOCKET: source) is action "SOC_CPY";

const func boolean: (in PRIMITIVE_SOCKET: sock1) = (in PRIMITIVE_SOCKET: sock2)  is action "SOC_EQ";
const func boolean: (in PRIMITIVE_SOCKET: sock1) <> (in PRIMITIVE_SOCKET: sock2) is action "SOC_NE";

const func PRIMITIVE_SOCKET: _GENERATE_EMPTY_PRIMITIVE_SOCKET               is action "SOC_EMPTY";
const PRIMITIVE_SOCKET: (attr PRIMITIVE_SOCKET) . value                     is _GENERATE_EMPTY_PRIMITIVE_SOCKET;
const PRIMITIVE_SOCKET: PRIMITIVE_NULL_SOCKET                               is PRIMITIVE_SOCKET.value;

const proc: close (in PRIMITIVE_SOCKET: aSocket)                            is action "SOC_CLOSE";
const func socketAddress: localAddress (in PRIMITIVE_SOCKET: aSocket)       is action "SOC_GET_LOCAL_ADDR";
const func socketAddress: peerAddress (in PRIMITIVE_SOCKET: aSocket)        is action "SOC_GET_PEER_ADDR";
const func integer: ord (in PRIMITIVE_SOCKET: aSocket)                      is action "SOC_ORD";
const func char: getc (in PRIMITIVE_SOCKET: inSocket,
                       inout char: eofIndicator)                            is action "SOC_GETC";
const func string: gets (in PRIMITIVE_SOCKET: inSocket, in integer: maxLength,
                         inout char: eofIndicator)                          is action "SOC_GETS";
const func boolean: hasNext (in PRIMITIVE_SOCKET: inSocket)                 is action "SOC_HAS_NEXT";
const func string: word_read (in PRIMITIVE_SOCKET: inSocket,
                              inout char: terminationChar)                  is action "SOC_WORD_READ";
const func string: line_read (in PRIMITIVE_SOCKET: inSocket,
                              inout char: terminationChar)                  is action "SOC_LINE_READ";
const proc: write (in PRIMITIVE_SOCKET: outSocket, in string: stri)         is action "SOC_WRITE";
const func integer: recv (in PRIMITIVE_SOCKET: inSocket, inout string: stri,
                          in integer: length, in integer: flags)            is action "SOC_RECV";
const func integer: recvfrom (in PRIMITIVE_SOCKET: inSocket, inout string: stri,
                          in integer: length, in integer: flags,
                          inout socketAddress: address)                     is action "SOC_RECVFROM";
const func integer: send (in PRIMITIVE_SOCKET: outSocket, in string: stri,
                          in integer: flags)                                is action "SOC_SEND";
const func integer: sendto (in PRIMITIVE_SOCKET: outSocket, in string: stri,
                            in integer: flags, in socketAddress: address)   is action "SOC_SENDTO";
const proc: setSockOpt (in PRIMITIVE_SOCKET: outSocket, in integer: optname,
                        in boolean: optval)                                 is action "SOC_SET_OPT_BOOL";
const integer: SO_REUSEADDR is 1;

const func PRIMITIVE_SOCKET: PRIMITIVE_SOCKET (in integer: domain,
                                               in integer: sockType,
                                               in integer: protocol)        is action "SOC_SOCKET";


(**
 *  Connect ''aSocket'' to the given ''address''.
 *  @exception FILE_ERROR A system function returns an error.
 *)
const proc: connect (in PRIMITIVE_SOCKET: aSocket,
                     in socketAddress: address)                             is action "SOC_CONNECT";


(**
 *  Create a new accepted connection socket for ''listenerSocket''.
 *  The function waits until at least one connection request is
 *  in the sockets queue of pending connections. Then it extracts
 *  the first connection request from the sockets queue. This
 *  request is accepted and a connection socket is created for it.
 *  @return the accepted connection socket.
 *  @exception FILE_ERROR A system function returns an error.
 *  @exception MEMORY_ERROR An out of memory situation occurred.
 *)
const func PRIMITIVE_SOCKET: accept (in PRIMITIVE_SOCKET: listenerSocket,
                                     inout socketAddress: address)          is action "SOC_ACCEPT";


(**
 *  Assign the specified ''address'' to the ''listenerSocket''.
 *  @param address An internet listener socket address.
 *  @exception FILE_ERROR A system function returns an error.
 *)
const proc: bind (in PRIMITIVE_SOCKET: listenerSocket,
                  in socketAddress: address)                                is action "SOC_BIND";


(**
 *  Listen for socket connections and limit the incoming queue.
 *  The ''backlog'' argument defines the maximum length to which
 *  the queue of pending connections for ''listenerSocket'' may grow.
 *  @exception FILE_ERROR A system function returns an error.
 *)
const proc: listen (in PRIMITIVE_SOCKET: listenerSocket,
                    in integer: backlog)                                    is action "SOC_LISTEN";


const func boolean: inputReady (in PRIMITIVE_SOCKET: inSocket, in integer: seconds,
                                in integer: microSeconds)                   is action "SOC_INPUT_READY";