/* wrapsock.c provides access to socket related system calls.

Copyright (C) 2008-2022 Free Software Foundation, Inc.
Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.

This file is part of GNU Modula-2.

GNU Modula-2 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 3, or (at your option)
any later version.

GNU Modula-2 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.

Under Section 7 of GPL version 3, you are granted additional
permissions described in the GCC Runtime Library Exception, version
3.1, as published by the Free Software Foundation.

You should have received a copy of the GNU General Public License and
a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
<http://www.gnu.org/licenses/>.  */

#include "config.h"
#include <stdbool.h>

#define EXPORT(FUNC) m2iso ## _wrapsock_ ## FUNC
#define IMPORT(MODULE,FUNC) m2iso ## _ ## MODULE ## _ ## FUNC
#define M2EXPORT(FUNC) m2iso ## _M2_wrapsock_ ## FUNC
#define M2LIBNAME "m2iso"

/* This module should be rewritten to use C++.  */

typedef void (*proc_con) (int, char **, char **);
typedef void (*proc_dep) (void);

extern void m2iso_M2RTS_RequestDependant (const char *modulename, const char *libname, const char *dependancy);
extern void m2iso_M2RTS_RegisterModule (const char *modulename, const char *libname,
                                        proc_con init, proc_con fini, proc_dep dependencies);

#if defined(HAVE_SYS_TYPES_H)
#include "sys/types.h"
#endif

#if defined(HAVE_SYS_SOCKET_H)
#include "sys/socket.h"
#endif

#if defined(HAVE_NETINET_IN_H)
#include "netinet/in.h"
#endif

#if defined(HAVE_NETDB_H)
#include "netdb.h"
#endif

#if defined(HAVE_UNISTD_H)
#include "unistd.h"
#endif

#if defined(HAVE_SIGNAL_H)
#include "signal.h"
#endif

#if defined(HAVE_SYS_ERRNO_H)
#include "sys/errno.h"
#endif

#if defined(HAVE_ERRNO_H)
#include "errno.h"
#endif

#if defined(HAVE_MALLOC_H)
#include "malloc.h"
#endif

#if defined(HAVE_MALLOC_H)
#include "signal.h"
#endif

#if defined(HAVE_STRING_H)
#include "string.h"
#endif

#if defined(HAVE_STDLIB_H)
#include "stdlib.h"
#endif

#include "ChanConsts.h"

#define MAXHOSTNAME 1024
#define MAXPBBUF 1024

#if defined(HAVE_NETINET_IN_H)

typedef struct
{
  char hostname[MAXHOSTNAME];
  struct hostent *hp;
  struct sockaddr_in sa;
  int sockFd;
  int portNo;
  int hasChar;
  char pbChar[MAXPBBUF];
} clientInfo;

static openResults clientConnect (clientInfo *c);

/* clientOpen - returns an ISO Modula-2 OpenResult.  It attempts to
   connect to: hostname:portNo.  If successful then the data
   structure, c, will have its fields initialized.  */

openResults
EXPORT(clientOpen) (clientInfo *c, char *hostname, unsigned int length,
                     int portNo)
{
  /* remove SIGPIPE which is raised on the server if the client is killed.  */
  signal (SIGPIPE, SIG_IGN);

  c->hp = gethostbyname (hostname);
  if (c->hp == NULL)
    return noSuchFile;

  memset ((void *)&c->sa, 0, sizeof (c->sa));
  c->sa.sin_family = AF_INET;
  memcpy ((void *)&c->sa.sin_addr, (void *)c->hp->h_addr, c->hp->h_length);
  c->portNo = portNo;
  c->sa.sin_port = htons (portNo);
  c->hasChar = 0;
  /* Open a TCP socket (an Internet stream socket) */

  c->sockFd = socket (c->hp->h_addrtype, SOCK_STREAM, 0);
  return clientConnect (c);
}

/* clientOpenIP - returns an ISO Modula-2 OpenResult.  It attempts to
   connect to: ipaddress:portNo.  If successful then the data
   structure, c, will have its fields initialized.  */

openResults
EXPORT(clientOpenIP) (clientInfo *c, unsigned int ip, int portNo)
{
  /* remove SIGPIPE which is raised on the server if the client is killed.  */
  signal (SIGPIPE, SIG_IGN);

  memset ((void *)&c->sa, 0, sizeof (c->sa));
  c->sa.sin_family = AF_INET;
  memcpy ((void *)&c->sa.sin_addr, (void *)&ip, sizeof (ip));
  c->portNo = portNo;
  c->sa.sin_port = htons (portNo);

  /* Open a TCP socket (an Internet stream socket) */

  c->sockFd = socket (PF_INET, SOCK_STREAM, 0);
  return clientConnect (c);
}

/* clientConnect - returns an ISO Modula-2 OpenResult once a connect
   has been performed.  If successful the clientInfo will include the
   file descriptor ready for read/write operations.  */

static openResults
clientConnect (clientInfo *c)
{
  if (connect (c->sockFd, (struct sockaddr *)&c->sa, sizeof (c->sa)) < 0)
    return noSuchFile;

  return opened;
}

/* getClientPortNo - returns the portNo from structure, c.  */

int
EXPORT(getClientPortNo) (clientInfo *c)
{
  return c->portNo;
}

/* getClientHostname - fills in the hostname of the server the to
   which the client is connecting.  */

void
EXPORT(getClientHostname) (clientInfo *c, char *hostname, unsigned int high)
{
  strncpy (hostname, c->hostname, high + 1);
}

/* getClientSocketFd - returns the sockFd from structure, c.  */

int
EXPORT(getClientSocketFd) (clientInfo *c)
{
  return c->sockFd;
}

/* getClientIP - returns the sockFd from structure, s.  */

unsigned int
EXPORT(getClientIP) (clientInfo *c)
{
#if 0
  printf("client ip = %s\n", inet_ntoa (c->sa.sin_addr.s_addr));
#endif
  return c->sa.sin_addr.s_addr;
}

/* getPushBackChar returns true if a pushed back character is
   available.  */

bool
EXPORT(getPushBackChar) (clientInfo *c, char *ch)
{
  if (c->hasChar > 0)
    {
      c->hasChar--;
      *ch = c->pbChar[c->hasChar];
      return true;
    }
  return false;
}

/* setPushBackChar returns true if it is able to push back a
   character.  */

bool
EXPORT(setPushBackChar) (clientInfo *c, char ch)
{
  if (c->hasChar == MAXPBBUF)
    return false;
  c->pbChar[c->hasChar] = ch;
  c->hasChar++;
  return true;
}

/* getSizeOfClientInfo - returns the sizeof (opaque data type).  */

unsigned int
EXPORT(getSizeOfClientInfo) (void)
{
  return sizeof (clientInfo);
}

#endif


/* GNU Modula-2 linking hooks.  */

void
M2EXPORT(init) (int, char **, char **)
{
}

void
M2EXPORT(fini) (int, char **, char **)
{
}

void
M2EXPORT(dep) (void)
{
}

void __attribute__((__constructor__))
M2EXPORT(ctor) (void)
{
  m2iso_M2RTS_RegisterModule ("wrapsock", M2LIBNAME,
			      M2EXPORT(init), M2EXPORT(fini),
			      M2EXPORT(dep));
}