-- Copyright: Fred Barnes, Mario Schweigler (C) 2000-2006
-- Institution: Computing Laboratory, University of Kent, Canterbury, UK
-- Description: pony internal TCP/IP socket utilities code file

-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or
-- (at your option) any later version.
--
-- This program 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 General Public License for more details.
--
-- You should have received a copy of the GNU 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.

--{{{  Compiler declarations
#INCLUDE "sock.module"
--}}}
--{{{  EXTERNAL declarations
#PRAGMA EXTERNAL "PROC C.pony.int.tcpip.socket.fullwrite.iovecdim (VAL []INT sizes, RESULT INT iovecdim) = 0"
#PRAGMA EXTERNAL "PROC B.pony.int.tcpip.socket.fullwrite.multi (SOCKET sock, VAL []BYTE header, VAL []INT addrs, sizes, RESULT []BYTE iovec.buffer, RESULT INT result) = 3"
#PRAGMA EXTERNAL "PROC C.pony.int.tcpip.socket.fullread.sizes (INT data.dim, sizes.dim) = 0"
#PRAGMA EXTERNAL "PROC B.pony.int.tcpip.socket.fullread.multi (SOCKET sock, RESULT []BYTE header, MOBILE []BYTE data, INT data.size, MOBILE []INT sizes, INT sizes.size, RESULT INT result) = 3"
#PRAGMA EXTERNAL "PROC C.pony.int.tcpip.getcode.eaddrinuse (INT err.code) = 0"
--}}}

--{{{  Auxiliary PROCs

--{{{  PROC pony.int.tcpip.socket.clt.reuse
-- Same as `socket.create.listen.tcp', but set `SO.REUSEADDR' option
-- Parameters: sock   | socket
--             addr   | VAL: IP address
--             port   | VAL: port number
--             result | RESULT: result
PROC pony.int.tcpip.socket.clt.reuse (SOCKET sock, VAL INT addr, VAL INT port, RESULT INT result)
  SEQ   
    socket.create (sock, PF.INET, SOCK.STREAM, IPPROTO.TCP)
    IF
      sock[fd] < 0
        result := (-1)
      TRUE
        SEQ
          socket.setsockopt (sock, SOL.SOCKET, SO.REUSEADDR, OPTION.ON, result)
          IF
            result < 0
              socket.close (sock)
            TRUE
              SEQ
                sock[local.addr] := addr
                sock[local.port] := port
                socket.bind (sock, result)
                IF
                  result < 0
                    INT err.num:
                    SEQ
                      err.num := sock[error]
                      socket.close (sock)
                      -- Ensure the error-code is the same as after the binding
                      sock[error] := err.num
                  TRUE
                    SEQ
                      socket.listen (sock, 32, result)
                      IF
                        result < 0
                          socket.close (sock)
                        TRUE
                          SKIP
:
--}}}
--{{{  PROC pony.int.tcpip.socket.fullwrite.multi
-- Write header plus stuff in address/size-arrays
-- Parameters: sock   | socket
--             header | header
--             addrs  | VAL: address-array
--             sizes  | VAL: size-array
--             result | RESULT: result
PROC pony.int.tcpip.socket.fullwrite.multi (SOCKET sock, []BYTE header, VAL []INT addrs, sizes, RESULT INT result)
  INT iovecdim:
  SEQ
    IF
      (SIZE sizes) = 1
        INITIAL INT size IS sizes[0]:
        []BYTE sizearray RETYPES size:
        [header FROM ((SIZE header) - (2 * BYTESIN (INT))) FOR BYTESIN (INT)] := sizearray
      TRUE
        SKIP
    INITIAL INT ccount IS (SIZE sizes):
    []BYTE ccountarray RETYPES ccount:
    [header FROM ((SIZE header) - BYTESIN (INT)) FOR BYTESIN (INT)] := ccountarray
    C.pony.int.tcpip.socket.fullwrite.iovecdim (sizes, iovecdim)
    IF
      iovecdim = (-1)
        result := -1
      TRUE
        MOBILE []BYTE iovec.buffer:
        SEQ
          iovec.buffer := MOBILE [iovecdim]BYTE
          B.pony.int.tcpip.socket.fullwrite.multi (sock, header, addrs, sizes, iovec.buffer, result)
:
--}}}
--{{{  PROC pony.int.tcpip.socket.fullread.multi
-- Read header and data/size-arrays
-- Parameters: sock   | socket
--             header | RESULT: header
--             data   | RESULT: data-array
--             sizes  | RESULT: size-array
--             result | RESULT: result
PROC pony.int.tcpip.socket.fullread.multi (SOCKET sock, RESULT []BYTE header, RESULT MOBILE []BYTE data, RESULT MOBILE []INT sizes, RESULT INT result)
  MOBILE []INT tmp.sizes:
  MOBILE []BYTE tmp.data:
  INT sdim, ddim:
  SEQ
    C.pony.int.tcpip.socket.fullread.sizes (ddim, sdim)
    tmp.data := MOBILE [ddim]BYTE
    tmp.sizes := MOBILE [sdim]INT
    result := 0
    B.pony.int.tcpip.socket.fullread.multi (sock, header, tmp.data, ddim, tmp.sizes, sdim, result)
    IF
      result = (-3)
        --{{{  not enough room for sizes, try again
        SEQ
          INT any:
          C.pony.int.tcpip.socket.fullread.sizes (any, sdim)
          tmp.sizes := MOBILE [sdim]INT
          B.pony.int.tcpip.socket.fullread.multi (sock, header, tmp.data, ddim, tmp.sizes, sdim, result)
        --}}}
      TRUE
        SKIP
    IF
      result = (-4)
        --{{{  not enough room for data, try again
        SEQ
          INT any:
          C.pony.int.tcpip.socket.fullread.sizes (ddim, any)
          tmp.data := MOBILE [ddim]BYTE
          B.pony.int.tcpip.socket.fullread.multi (sock, header, tmp.data, ddim, tmp.sizes, sdim, result)
        --}}}
      TRUE
        SKIP
    IF
      ddim = 0
        data := MOBILE [0]BYTE
      TRUE
        data := tmp.data
    IF
      sdim = 0
        sizes := MOBILE [0]INT
      TRUE
        sizes := tmp.sizes
:
--}}}

--{{{  PROC pony.int.tcpip.getcode.eaddrinuse
-- Return numeric value for EADDRINUSE error
-- Parameters: err.code | VAL: error-code
PROC pony.int.tcpip.getcode.eaddrinuse (INT err.code)
  C.pony.int.tcpip.getcode.eaddrinuse (err.code)
:
--}}}

--}}}

