/*************************************************************************
*									 *
*	 YAP Prolog 	%W% %G%
*									 *
*	Yap Prolog was developed at NCCUP - Universidade do Porto	 *
*									 *
* Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997	 *
*									 *
**************************************************************************
*									 *
* File:		io.h							 *
* Last rev:	19/2/88							 *
* mods:									 *
* comments:	control YAP from sockets.				 *
*									 *
*************************************************************************/


#include "Yap.h"

#include "Yatom.h"
#include "YapHeap.h"
#include "yapio.h"

#if   USE_SOCKET

#if HAVE_UNISTD_H && !defined(__MINGW32__) && !_MSC_VER
#include <unistd.h>
#endif
#if STDC_HEADERS
#include <stdlib.h>
#endif
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if HAVE_SYS_TIME_H && !defined(__MINGW32__) && !_MSC_VER
#include <sys/time.h>
#endif
#ifdef _WIN32
#if HAVE_IO_H
#include <io.h>
#endif
#endif
#if _MSC_VER || defined(__MINGW32__)
#include <io.h>
#include <winsock2.h>
#else
#if HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#if HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#if HAVE_NETDB_H
#include <netdb.h>
#endif
#if HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#if HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#if HAVE_FCNTL_H
#include <fcntl.h>
#endif
#if HAVE_STRING_H
#include <string.h>
#endif
#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#endif

/* make sure we can compile in any platform */
#ifndef AF_UNSPEC
#define AF_UNSPEC 0
#endif
#ifndef AF_LOCAL
#define AF_LOCAL AF_UNSPEC
#endif
#ifndef AF_AAL5
#define AF_AAL5 AF_UNSPEC
#endif
#ifndef AF_APPLETALK
#define AF_APPLETALK AF_UNSPEC
#endif
#ifndef AF_AX25
#define AF_AX25 AF_UNSPEC
#endif
#ifndef AF_BRIDGE
#define AF_BRIDGE AF_UNSPEC
#endif
#ifndef AF_DECnet
#define AF_DECnet AF_UNSPEC
#endif
#ifndef AF_FILE
#define AF_FILE AF_UNSPEC
#endif
#ifndef AF_INET
#define AF_INET AF_UNSPEC
#endif
#ifndef AF_INET6
#define AF_INET6 AF_UNSPEC
#endif
#ifndef AF_IPX
#define AF_IPX AF_UNSPEC
#endif
#ifndef AF_LOCAL
#define AF_LOCAL AF_UNSPEC
#endif
#ifndef AF_NETBEUI
#define AF_NETBEUI AF_UNSPEC
#endif
#ifndef AF_NETLINK
#define AF_NETLINK AF_UNSPEC
#endif
#ifndef AF_NETROM
#define AF_NETROM AF_UNSPEC
#endif
#ifndef AF_OSINET
#define AF_OSINET AF_UNSPEC
#endif
#ifndef AF_PACKET
#define AF_PACKET AF_UNSPEC
#endif
#ifndef AF_ROSE
#define AF_ROSE AF_UNSPEC
#endif
#ifndef AF_ROUTE
#define AF_ROUTE AF_UNSPEC
#endif
#ifndef AF_SECURITY
#define AF_SECURITY AF_UNSPEC
#endif
#ifndef AF_SNA
#define AF_SNA AF_UNSPEC
#endif
#ifndef AF_UNIX
#define AF_UNIX AF_UNSPEC
#endif
#ifndef AF_X25
#define AF_X25 AF_UNSPEC
#endif

#ifndef SOCK_STREAM
#define SOCK_STREAM -1
#endif
#ifndef SOCK_DGRAM
#define SOCK_DGRAM -1
#endif
#ifndef SOCK_RAW
#define SOCK_RAW -1
#endif
#ifndef SOCK_RDM
#define SOCK_RDM -1
#endif
#ifndef SOCK_SEQPACKET
#define SOCK_SEQPACKET -1
#endif
#ifndef SOCK_PACKET
#define SOCK_PACKET -1
#endif

#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 256
#endif

#ifndef BUFSIZ
#define BUFSIZ 256
#endif

#if _MSC_VER || defined(__MINGW32__)
#define socket_errno WSAGetLastError()
#define invalid_socket_fd(fd) (fd) == INVALID_SOCKET
#else
#define socket_errno errno
#define invalid_socket_fd(fd) (fd) < 0
#endif

void
Yap_init_socks(char *host, long interface_port)
{
   int s;
   int r;
   struct sockaddr_in soadr;
   struct in_addr adr;
   struct hostent *he; 


#if   USE_SOCKET
   he = gethostbyname(host);
   if (he == NULL) {
#if HAVE_STRERROR
     Yap_Error(SYSTEM_ERROR, TermNil, "can not get address for host %s: %s", host, strerror(h_errno));
#else
     Yap_Error(SYSTEM_ERROR, TermNil, "can not get address for host");
#endif
     return;
   }	
   
   (void) memset((void *) &soadr, '\0', sizeof(struct sockaddr_in));
   soadr.sin_family = AF_INET;
   soadr.sin_port = htons((short) interface_port);
  
   if (he != NULL) {
	memcpy((char *) &adr,
	    (char *) he->h_addr_list[0], (size_t) he->h_length);
   } else {
	adr.s_addr = inet_addr(host);
   }
   soadr.sin_addr.s_addr = adr.s_addr;

   s = socket ( AF_INET, SOCK_STREAM, 0);
   if (s<0) {
#if HAVE_STRERROR
     Yap_Error(SYSTEM_ERROR, TermNil, "could not create socket: %s", strerror(errno));
#else
     Yap_Error(SYSTEM_ERROR, TermNil, "could not create socket");
#endif
     return;
   }

#if ENABLE_SO_LINGER
   struct linger ling;			/* disables socket lingering. */
   ling.l_onoff = 1;
   ling.l_linger = 0;
   if (setsockopt(s, SOL_SOCKET, SO_LINGER, (void *) &ling,
		  sizeof(ling)) < 0) {
#if HAVE_STRERROR
      Yap_Error(SYSTEM_ERROR, TermNil, 
	    "socket_connect/3 (setsockopt_linger: %s)", strerror(socket_errno));
#else
      Yap_Error(SYSTEM_ERROR, TermNil,
	    "socket_connect/3 (setsockopt_linger)");
#endif
      return;
   }
#endif

   r = connect ( s, (struct sockaddr *) &soadr, sizeof(soadr));
   if (r<0) {
#if HAVE_STRERROR
     Yap_Error(SYSTEM_ERROR, TermNil, "connect failed, could not connect to interface: %s", strerror(errno));
#else
     Yap_Error(SYSTEM_ERROR, TermNil, "connect failed, could not connect to interface");
#endif
     return;
   }
   /* now reopen stdin stdout and stderr */
#if HAVE_DUP2 && !defined(__MINGW32__)
   if(dup2(s,0)<0) {
#if HAVE_STRERROR
     Yap_Error(SYSTEM_ERROR, TermNil, "could not dup2 stdin: %s", strerror(errno));
#else
     Yap_Error(SYSTEM_ERROR, TermNil, "could not dup2 stdin");
#endif
     return;
   }
   if(dup2(s,1)<0) {
#if HAVE_STRERROR
     Yap_Error(SYSTEM_ERROR, TermNil, "could not dup2 stdout: %s", strerror(errno));
#else
     Yap_Error(SYSTEM_ERROR, TermNil, "could not dup2 stdout");
#endif
     return;
   }
   if(dup2(s,2)<0) {
#if HAVE_STRERROR
     Yap_Error(SYSTEM_ERROR, TermNil, "could not dup2 stderr: %s", strerror(errno));
#else
     Yap_Error(SYSTEM_ERROR, TermNil, "could not dup2 stderr");
#endif
     return;
   }
#elif _MSC_VER || defined(__MINGW32__)
   if(_dup2(s,0)<0) {
   	fprintf(stderr,"could not dup2 stdin\n");
   	return;
   }
   if(_dup2(s,1)<0) {
   	fprintf(stderr,"could not dup2 stdout\n");
   	return;
   }
   if(_dup2(s,2)<0) {
   	fprintf(stderr,"could not dup2 stderr\n");
   	return;
   }
#else
   if(dup2(s,0)<0) {
   	fprintf(stderr,"could not dup2 stdin\n");
   	return;
   }
   yp_iob[0].cnt = 0;
   yp_iob[0].flags = _YP_IO_SOCK | _YP_IO_READ;
   if(dup2(s,1)<0) {
   	fprintf(stderr,"could not dup2 stdout\n");
   	return;
   }
   yp_iob[1].cnt = 0;
   yp_iob[1].flags = _YP_IO_SOCK | _YP_IO_WRITE;
   if(dup2(s,2)<0) {
   	fprintf(stderr,"could not dup2 stderr\n");
   	return;
   }
   yp_iob[2].cnt = 0;
   yp_iob[2].flags = _YP_IO_SOCK | _YP_IO_WRITE;
#endif
   Yap_sockets_io = 1;
#if _MSC_VER || defined(__MINGW32__)
   _close(s);
#else
   close(s);
#endif
#else /* USE_SOCKET */
   Yap_Error(SYSTEM_ERROR, TermNil, "sockets not installed", strerror(errno));
#endif /* USE_SOCKET */
}

static Int
p_socket(void)
{
  Term t1 = Deref(ARG1);
  Term t2 = Deref(ARG2);
  Term t3 = Deref(ARG3);
  Int t;
  char *sdomain, *stype;
  Int domain = AF_UNSPEC, type, protocol;
  int fd;
  Term out;

  if (IsVarTerm(t1)) {
    Yap_Error(INSTANTIATION_ERROR,t1,"socket/4");
    return(FALSE);
  }
  if (!IsAtomTerm(t1)) {
    Yap_Error(TYPE_ERROR_ATOM,t1,"socket/4");
    return(FALSE);
  }
  if (IsVarTerm(t2)) {
    Yap_Error(INSTANTIATION_ERROR,t2,"socket/4");
    return(FALSE);
  }
  if (!IsAtomTerm(t2)) {
    Yap_Error(TYPE_ERROR_ATOM,t2,"socket/4");
    return(FALSE);
  }
  if (IsVarTerm(t3)) {
    Yap_Error(INSTANTIATION_ERROR,t3,"socket/4");
    return(FALSE);
  }
  if (!IsIntTerm(t3)) {
    Yap_Error(TYPE_ERROR_ATOM,t3,"socket/4");
    return(FALSE);
  }
  sdomain = RepAtom(AtomOfTerm(t1))->StrOfAE;
  if (sdomain[0] != 'A' || sdomain[1] != 'F' || sdomain[2] != '_')
    return(FALSE); /* Error */
  sdomain += 3;
  switch (sdomain[0]) {
  case 'A':
    if (strcmp(sdomain, "AAL5") == 0)
      domain = AF_AAL5;
    else if (strcmp(sdomain, "APPLETALK") == 0)
      domain = AF_APPLETALK;
    else if (strcmp(sdomain, "AX25") == 0)
      domain = AF_AX25;
    break;
  case 'B':
    if (strcmp(sdomain, "BRIDGE") == 0)
      domain = AF_APPLETALK;
    break; 
  case 'D':
    if (strcmp(sdomain, "DECnet") == 0)
      domain = AF_DECnet;
    break;
  case 'F':
    if (strcmp(sdomain, "FILE") == 0)
      domain = AF_FILE;
    break;
  case 'I':
    if (strcmp(sdomain, "INET") == 0)
      domain = AF_INET;
    else if (strcmp(sdomain, "INET6") == 0)
      domain = AF_INET6;
    else if (strcmp(sdomain, "IPX") == 0)
      domain = AF_IPX;
    break;
  case 'L':
    if (strcmp(sdomain, "LOCAL") == 0)
      domain = AF_LOCAL;
    break;
  case 'N':
    if (strcmp(sdomain, "NETBEUI") == 0)
      domain = AF_NETBEUI;
    else if (strcmp(sdomain, "NETLINK") == 0)
      domain = AF_NETLINK;
    else if (strcmp(sdomain, "NETROM") == 0)
      domain = AF_NETROM;
    break;
  case 'O':
    if (strcmp(sdomain, "OSINET") == 0)
      domain = AF_OSINET;
    break;
  case 'P':
    if (strcmp(sdomain, "PACKET") == 0)
      domain = AF_PACKET;
    break;
  case 'R':
    if (strcmp(sdomain, "ROSE") == 0)
      domain = AF_ROSE;
    else if (strcmp(sdomain, "ROUTE") == 0)
      domain = AF_ROUTE;
    break;
  case 'S':
    if (strcmp(sdomain, "SECURITY") == 0)
      domain = AF_SECURITY;
    else if (strcmp(sdomain, "SNA") == 0)
      domain = AF_SNA;
    break;
  case 'U':
    if (strcmp(sdomain, "UNIX") == 0)
      domain = AF_UNIX;
    break;
  case 'X':
    if (strcmp(sdomain, "X25") == 0)
      domain = AF_X25;
    break;
  }
  stype = RepAtom(AtomOfTerm(t2))->StrOfAE;
  if (stype[0] != 'S' || stype[1] != 'O' || stype[2] != 'C' || stype[3] != 'K'  || stype[4] != '_')
    return(FALSE); /* Error */
  stype += 5;
  if (strcmp(stype,"STREAM") == 0)
    type = SOCK_STREAM;
  else if (strcmp(stype,"DGRAM") == 0)
    type = SOCK_DGRAM;
  else if (strcmp(stype,"RAW") == 0)
    type = SOCK_RAW;
  else if (strcmp(stype,"RDM") == 0)
    type = SOCK_RDM;
  else if (strcmp(stype,"SEQPACKET") == 0)
    type = SOCK_SEQPACKET;
  else if (strcmp(stype,"PACKET") == 0)
    type = SOCK_PACKET;
  else
    return(FALSE);
  protocol = IntOfTerm(t3);
  if (protocol < 0)
    return(FALSE);
  fd = socket(domain, type, protocol);
  if (invalid_socket_fd(fd)) {
#if HAVE_STRERROR
    Yap_Error(SYSTEM_ERROR, TermNil, 
	"socket/4 (socket: %s)", strerror(socket_errno));
#else
    Yap_Error(SYSTEM_ERROR, TermNil,
	  "socket/4 (socket)");
#endif
    return(FALSE);
  }
  if (domain == AF_UNIX || domain == AF_LOCAL ) {
    sout = Sfdopen(fd, "r+");
  } else if (domain == AF_INET )
    sout = Sfdopen(fd, "r+");
  else {
    /* ok, we currently don't support these sockets */
#if _MSC_VER || defined(__MINGW32__)
   _close(fd);
#else
    close(fd);
#endif
    return(FALSE);
  }
  if (!out) return FALSE;
  t = Yap_PutInSlot(ARG4);
  return PL_unify_stream_or_alias(t, sout);
}

static Int
p_socket_close(void)
{
  Int t1 = Yap_PutInSlot(ARG1);
  IOSTREAM *s;

  if (!PL_get_stream_handle(t1, &s)) {
    return (FALSE);
  }
  Yap_CloseStream(sno);
  return(TRUE);
}

static Int
p_socket_bind(void)
{
  Term t1 = Deref(ARG1);
  Term t2 = Deref(ARG2);
  int sno;
  Functor fun;
  socket_info status;
  int fd;

  if ((sno = Yap_CheckSocketStream(t1, "socket_bind/2")) < 0) {
    return (FALSE);
  }
  status = Yap_GetSocketStatus(sno);
  fd = Yap_GetStreamFd(sno);
  if (status != new_socket) {
    /* ok, this should be an error, as you are trying to bind  */
    return(FALSE);
  }
  if (IsVarTerm(t2)) {
    Yap_Error(INSTANTIATION_ERROR,t2,"socket_bind/2");
    return(FALSE);
  }
  if (!IsApplTerm(t2)) {
    Yap_Error(DOMAIN_ERROR_STREAM,t2,"socket_bind/2");
    return(FALSE);
  }
  fun = FunctorOfTerm(t2);
#if HAVE_SYS_UN_H
  if (fun == FunctorAfUnix || fun == FunctorAfLocal) {
    struct sockaddr_un sock;
    Term taddr = ArgOfTerm(1, t2);
    char *s;
    int len;
    
    if (IsVarTerm(taddr)) {
      Yap_Error(INSTANTIATION_ERROR,t2,"socket_bind/2");
      return(FALSE);
    }
    if (!IsAtomTerm(taddr)) {
      Yap_Error(TYPE_ERROR_ATOM,taddr,"socket_bind/2");
      return(FALSE);
    }
    s = RepAtom(AtomOfTerm(taddr))->StrOfAE;
    sock.sun_family = AF_UNIX;
    if ((len = strlen(s)) > 107) /* hit me with a broomstick */ {
      Yap_Error(DOMAIN_ERROR_STREAM,taddr,"socket_bind/2");
      return(FALSE);
    }      
    sock.sun_family=AF_UNIX;
    strcpy(sock.sun_path,s);
    if (bind(fd,
	     (struct sockaddr *)(&sock),
	     ((size_t) (((struct sockaddr_un *) 0)->sun_path) + len))
	< 0) {
#if HAVE_STRERROR
      Yap_Error(SYSTEM_ERROR, TermNil, 
	    "socket_bind/2 (bind: %s)", strerror(socket_errno));
#else
      Yap_Error(SYSTEM_ERROR, TermNil,
	    "socket_bind/2 (bind)");
#endif
      return(FALSE);
    }
    Yap_UpdateSocketStream(sno, server_socket, af_unix);
    return(TRUE);
  } else
#endif
  if (fun == FunctorAfInet) {
    Term thost = ArgOfTerm(1, t2);
    Term tport = ArgOfTerm(2, t2);
    char *shost;
    struct hostent *he;
    struct sockaddr_in saddr;
   Int port;

    memset((void *)&saddr,(int) 0, sizeof(saddr));
    if (IsVarTerm(thost)) {
      saddr.sin_addr.s_addr = INADDR_ANY;
    } else if (!IsAtomTerm(thost)) {
      Yap_Error(TYPE_ERROR_ATOM,thost,"socket_bind/2");
      return(FALSE);
    } else {
      shost = RepAtom(AtomOfTerm(thost))->StrOfAE;
      if((he=gethostbyname(shost))==NULL) {
#if HAVE_STRERROR
	Yap_Error(SYSTEM_ERROR, TermNil, 
	      "socket_bind/2 (gethostbyname: %s)", strerror(socket_errno));
#else
	Yap_Error(SYSTEM_ERROR, TermNil,
	      "socket_bind/2 (gethostbyname)");
#endif
	return(FALSE);
      }
      memcpy((void *)&saddr.sin_addr, (void *)he->h_addr_list[0], he->h_length);
    }
    if (IsVarTerm(tport)) {
      port = 0;
    } else {
      port = IntOfTerm(tport);
    }
    saddr.sin_port = htons(port); 
    saddr.sin_family = AF_INET;
    if(bind(fd,(struct sockaddr *)&saddr, sizeof(saddr))==-1) {
#if HAVE_STRERROR
      Yap_Error(SYSTEM_ERROR, TermNil, 
	    "socket_bind/2 (bind: %s)", strerror(socket_errno));
#else
      Yap_Error(SYSTEM_ERROR, TermNil,
	    "socket_bind/2 (bind)");
#endif
      return(FALSE);
    }

    if (IsVarTerm(tport)) {
      /* get the port number */
#if _WIN32 || defined(__MINGW32__)
      int namelen;
#else
      unsigned int namelen;
#endif
      Term t;
      if (getsockname(fd, (struct sockaddr *)&saddr, &namelen) < 0) {
#if HAVE_STRERROR
	Yap_Error(SYSTEM_ERROR, TermNil, 
	      "socket_bind/2 (getsockname: %s)", strerror(socket_errno));
#else
	Yap_Error(SYSTEM_ERROR, TermNil,
	      "socket_bind/2 (getsockname)");
#endif
	return(FALSE);
      } 
      t = MkIntTerm(ntohs(saddr.sin_port));
      Yap_unify(ArgOfTermCell(2, t2),t);
    }
    Yap_UpdateSocketStream(sno, server_socket, af_inet);
    return(TRUE);
  } else
    return(FALSE);
}

static Int
p_socket_connect(void)
{
  Term t1 = Deref(ARG1);
  Term t2 = Deref(ARG2);
  Functor fun;
  int sno;
  socket_info status;
  int fd;
  int flag;
  Term out;

  if ((sno = Yap_CheckSocketStream(t1, "socket_connect/3")) < 0) {
    return (FALSE);
  }
  if (IsVarTerm(t2)) {
    Yap_Error(INSTANTIATION_ERROR,t2,"socket_connect/3");
    return(FALSE);
  }
  if (!IsApplTerm(t2)) {
    Yap_Error(DOMAIN_ERROR_STREAM,t2,"socket_connect/3");
    return(FALSE);
  }
  fun = FunctorOfTerm(t2);
  fd = Yap_GetStreamFd(sno);
  status = Yap_GetSocketStatus(sno);
  if (status != new_socket) {
    /* ok, this should be an error, as you are trying to bind  */
    return(FALSE);
  }
#if HAVE_SYS_UN_H
  if (fun == FunctorAfUnix) {
    struct sockaddr_un sock;
    Term taddr = ArgOfTerm(1, t2);
    char *s;
    int len;
    
    if (IsVarTerm(taddr)) {
      Yap_Error(INSTANTIATION_ERROR,t2,"socket_connect/3");
      return(FALSE);
    }
    if (!IsAtomTerm(taddr)) {
      Yap_Error(TYPE_ERROR_ATOM,taddr,"socket_connect/3");
      return(FALSE);
    }
    s = RepAtom(AtomOfTerm(taddr))->StrOfAE;
    sock.sun_family = AF_UNIX;
    if ((len = strlen(s)) > 107) /* beat me with a broomstick */ {
      Yap_Error(DOMAIN_ERROR_STREAM,taddr,"socket_connect/3");
      return(FALSE);
    }      
    sock.sun_family=AF_UNIX;
    strcpy(sock.sun_path,s);
    if ((flag = connect(fd,
		   (struct sockaddr *)(&sock),
		   ((size_t) (((struct sockaddr_un *) 0)->sun_path) + len)))
	< 0) {
#if HAVE_STRERROR
      Yap_Error(SYSTEM_ERROR, TermNil, 
	    "socket_connect/3 (connect: %s)", strerror(socket_errno));
#else
      Yap_Error(SYSTEM_ERROR, TermNil,
	    "socket_connect/3 (connect)");
#endif
      return(FALSE);
    }
    Yap_UpdateSocketStream(sno, client_socket, af_unix);
  } else
#endif
  if (fun == FunctorAfInet) {
    Term thost = ArgOfTerm(1, t2);
    Term tport = ArgOfTerm(2, t2);
    char *shost;
    struct hostent *he;
    struct sockaddr_in saddr;
    unsigned short int port;

    memset((void *)&saddr,(int) 0, sizeof(saddr));
    if (IsVarTerm(thost)) {
      Yap_Error(INSTANTIATION_ERROR,thost,"socket_connect/3");
      return(FALSE);
    } else if (!IsAtomTerm(thost)) {
      Yap_Error(TYPE_ERROR_ATOM,thost,"socket_connect/3");
      return(FALSE);
    } else {
      shost = RepAtom(AtomOfTerm(thost))->StrOfAE;
      if((he=gethostbyname(shost))==NULL) {
#if HAVE_STRERROR
	Yap_Error(SYSTEM_ERROR, TermNil, 
	      "socket_connect/3 (gethostbyname: %s)", strerror(socket_errno));
#else
	Yap_Error(SYSTEM_ERROR, TermNil,
	      "socket_connect/3 (gethostbyname)");
#endif
	return(FALSE);
      }
      memcpy((void *)&saddr.sin_addr, (void *)he->h_addr_list[0], he->h_length);
    }
    if (IsVarTerm(tport)) {
      Yap_Error(INSTANTIATION_ERROR,tport,"socket_connect/3");
      return(FALSE);
    } else if (!IsIntegerTerm(tport)) {
      Yap_Error(TYPE_ERROR_INTEGER,tport,"socket_connect/3");
      return(FALSE);
    } else {
      port = (unsigned short int)IntegerOfTerm(tport);
    }
    saddr.sin_port = htons(port); 
    saddr.sin_family = AF_INET;
#if ENABLE_SO_LINGER
    { 
      struct linger ling;			/* For making sockets linger. */
      /* disabled: I see why no reason why we should throw things away by default!! */
      ling.l_onoff = 1;
      ling.l_linger = 0;
      if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (void *) &ling,
		     sizeof(ling)) < 0) {
#if HAVE_STRERROR
	Yap_Error(SYSTEM_ERROR, TermNil, 
		  "socket_connect/3 (setsockopt_linger: %s)", strerror(socket_errno));
#else
	Yap_Error(SYSTEM_ERROR, TermNil,
		  "socket_connect/3 (setsockopt_linger)");
#endif
	return FALSE;
      }
    }
#endif

    {
      int one = 1;			/* code by David MW Powers */

      if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (void *)&one, sizeof(one))) {
#if HAVE_STRERROR
	Yap_Error(SYSTEM_ERROR, TermNil, 
		  "socket_connect/3 (setsockopt_broadcast: %s)", strerror(socket_errno));
#else
	Yap_Error(SYSTEM_ERROR, TermNil,
		  "socket_connect/3 (setsockopt_broadcast)");
#endif
	return FALSE;
      }
    }

    flag = connect(fd,(struct sockaddr *)&saddr, sizeof(saddr));
    if(flag<0) {
#if HAVE_STRERROR
      Yap_Error(SYSTEM_ERROR, TermNil, 
	    "socket_connect/3 (connect: %s)", strerror(socket_errno));
#else
      Yap_Error(SYSTEM_ERROR, TermNil,
	    "socket_connect/3 (connect)");
#endif
      return FALSE;
    }
    Yap_UpdateSocketStream(sno, client_socket, af_inet);
  } else 
    return(FALSE);
  out = t1;
  return(Yap_unify(out,ARG3));
}

static Int
p_socket_listen(void)
{
  Term t1 = Deref(ARG1);
  Term t2 = Deref(ARG2);
  int sno;
  socket_info status;
  int fd;
  Int j;

  if ((sno = Yap_CheckSocketStream(t1, "socket_listen/2")) < 0) {
    return (FALSE);
  }
  if (IsVarTerm(t2)) {
    Yap_Error(INSTANTIATION_ERROR,t2,"socket_listen/2");
    return(FALSE);
  }
  if (!IsIntTerm(t2)) {
    Yap_Error(TYPE_ERROR_INTEGER,t2,"socket_listen/2");
    return(FALSE);
  }
  j = IntOfTerm(t2);
  if (j < 0) {
    Yap_Error(DOMAIN_ERROR_STREAM,t1,"socket_listen/2");
    return(FALSE);
  }
  fd = Yap_GetStreamFd(sno);
  status = Yap_GetSocketStatus(sno);
  if (status != server_socket) {
    /* ok, this should be an error, as you are trying to bind  */
    return(FALSE);
  }
  if (listen(fd,j) < 0) {
#if HAVE_STRERROR
      Yap_Error(SYSTEM_ERROR, TermNil, 
	    "socket_listen/2 (listen: %s)", strerror(socket_errno));
#else
      Yap_Error(SYSTEM_ERROR, TermNil,
	    "socket_listen/2 (listen)");
#endif
  }
  return(TRUE);
}

static Int
p_socket_accept(void)
{
  Term t1 = Deref(ARG1);
  int sno;
  socket_info status;
  socket_domain domain;
  int ofd, fd;
  Term out;

  if ((sno = Yap_CheckSocketStream(t1, "socket_accept/3")) < 0) {
    return (FALSE);
  }
  ofd = Yap_GetStreamFd(sno);
  status = Yap_GetSocketStatus(sno);
  if (status != server_socket) {
    /* ok, this should be an error, as you are trying to bind  */
    return(FALSE);
  }
  domain = Yap_GetSocketDomain(sno);
#if HAVE_SYS_UN_H
  if (domain == af_unix) {
    struct sockaddr_un caddr;
    unsigned int len;

    memset((void *)&caddr,(int) 0, sizeof(caddr));
    if ((fd=accept(ofd, (struct sockaddr *)&caddr, &len)) < 0) {
#if HAVE_STRERROR
      Yap_Error(SYSTEM_ERROR, TermNil, 
	    "socket_accept/3 (accept: %s)", strerror(socket_errno));
#else
      Yap_Error(SYSTEM_ERROR, TermNil,
	       "socket_accept/3 (accept)");
#endif
    }
    /* ignore 2nd argument */
    out = Yap_InitSocketStream(fd, server_session_socket, af_unix );
  } else
#endif
  if (domain == af_inet)  {
    struct sockaddr_in caddr;
    Term tcli;
    char *s;
#if _WIN32 || defined(__MINGW32__)
    int len;
#else
    unsigned int len;
#endif

    len = sizeof(caddr);
    memset((void *)&caddr,(int) 0, sizeof(caddr));
    if (invalid_socket_fd(fd=accept(ofd, (struct sockaddr *)&caddr, &len))) {
#if HAVE_STRERROR
      Yap_Error(SYSTEM_ERROR, TermNil, 
	    "socket_accept/3 (accept: %s)", strerror(socket_errno));
#else
      Yap_Error(SYSTEM_ERROR, TermNil,
	    "socket_accept/3 (accept)");
#endif
      return(FALSE);
    }
    if ((s = inet_ntoa(caddr.sin_addr)) == NULL) {
#if HAVE_STRERROR
      Yap_Error(SYSTEM_ERROR, TermNil, 
	    "socket_accept/3 (inet_ntoa: %s)", strerror(socket_errno));
#else
      Yap_Error(SYSTEM_ERROR, TermNil,
	    "socket_accept/3 (inet_ntoa)");
#endif
    }
    tcli = MkAtomTerm(Yap_LookupAtom(s));
    if (!Yap_unify(ARG2,tcli))
      return(FALSE);
    out = Yap_InitSocketStream(fd, server_session_socket, af_inet );
  } else
      return(FALSE);
  if (out == TermNil) return(FALSE);
  return(Yap_unify(out,ARG3));
}

static Int
p_socket_buffering(void)
{
  Term t1 = Deref(ARG1);
  Term t2 = Deref(ARG2);
  Term t4 = Deref(ARG4);
  Atom mode;
  int fd;
  int writing;
#if _WIN32 || defined(__MINGW32__)
  int bufsize;
  int len;
#else
  unsigned int bufsize;
  unsigned int len;
#endif
  int sno;

  if ((sno = Yap_CheckSocketStream(t1, "socket_buffering/4")) < 0) {
    return (FALSE);
  }
  if (IsVarTerm(t2)) {
    Yap_Error(INSTANTIATION_ERROR,t2,"socket_buffering/4");
    return(FALSE);
  }
  if (!IsAtomTerm(t2)) {
    Yap_Error(TYPE_ERROR_ATOM,t2,"socket_buffering/4");
    return(FALSE);
  }
  mode = AtomOfTerm(t2);
  if (mode == AtomRead) 
    writing = FALSE;
  else if (mode == AtomWrite) 
    writing = TRUE;
  else {
    Yap_Error(DOMAIN_ERROR_IO_MODE,t2,"socket_buffering/4");
    return(FALSE);
  }
  fd = Yap_GetStreamFd(sno);
  if (writing) {
    getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void *)&bufsize, &len);
  } else {
    getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *)&bufsize, &len);
  }
  if (!Yap_unify(ARG3,MkIntegerTerm(bufsize)))
    return(FALSE);
  if (IsVarTerm(t4)) {
    bufsize = BUFSIZ;
  } else {
    Int siz;
    if (!IsIntegerTerm(t4)) {
      Yap_Error(TYPE_ERROR_INTEGER,t4,"socket_buffering/4");
      return(FALSE);
    }
    siz = IntegerOfTerm(t4);
    if (siz < 0) {
      Yap_Error(DOMAIN_ERROR_NOT_LESS_THAN_ZERO,t4,"socket_buffering/4");
      return(FALSE);
    }
    bufsize = siz; 
  }
  if (writing) {
    setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void *)&bufsize, sizeof(bufsize));
  } else {
    setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *)&bufsize, sizeof(bufsize));
  }
  return(TRUE);
}

static Term 
select_out_list(Term t1, fd_set *readfds_ptr)
{
  if (t1 == TermNil) {
    return(TermNil);
  } else {
    int fd;
    int sno;
    Term next = select_out_list(TailOfTerm(t1), readfds_ptr);
    Term Head = HeadOfTerm(t1);

    sno  = Yap_CheckIOStream(Head,"stream_select/5");
    fd = Yap_GetStreamFd(sno);
    if (FD_ISSET(fd, readfds_ptr))
      return(MkPairTerm(Head,next));
    else 
      return(MkPairTerm(TermNil,next));
  }
}

static Int
p_socket_select(void)
{
  Term t1 = Deref(ARG1);
  Term t2 = Deref(ARG2);
  Term t3 = Deref(ARG3);
  fd_set readfds, writefds, exceptfds;
  struct timeval timeout, *ptime;

#if _MSC_VER || defined(__MINGW32__)
  u_int fdmax=0;
#else
  int fdmax=0;
#endif
  Int tsec, tusec;
  Term tout = TermNil, ti, Head;

  if (IsVarTerm(t1)) {
    Yap_Error(INSTANTIATION_ERROR,t1,"socket_select/5");
    return(FALSE);
  }
  if (!IsPairTerm(t1)) {
    Yap_Error(TYPE_ERROR_LIST,t1,"socket_select/5");
    return(FALSE);
  }
  if (IsVarTerm(t2)) {
    Yap_Error(INSTANTIATION_ERROR,t2,"socket_select/5");
    return(FALSE);
  }
  if (!IsIntegerTerm(t2)) {
    Yap_Error(TYPE_ERROR_INTEGER,t2,"socket_select/5");
    return(FALSE);
  }
  if (IsVarTerm(t3)) {
    Yap_Error(INSTANTIATION_ERROR,t3,"socket_select/5");
    return(FALSE);
  }
  if (!IsIntegerTerm(t3)) {
    Yap_Error(TYPE_ERROR_INTEGER,t3,"socket_select/5");
    return(FALSE);
  }
  FD_ZERO(&readfds);
  FD_ZERO(&writefds);
  FD_ZERO(&exceptfds);
  /* fetch the input streams */
  ti = t1;
  while (ti != TermNil) {
#if _MSC_VER || defined(__MINGW32__)
	u_int fd;
#else
    int fd;
#endif
    int sno;

    Head = HeadOfTerm(ti);
    sno  = Yap_CheckIOStream(Head,"stream_select/5");
    if (sno < 0)
      return(FALSE);
    fd = Yap_GetStreamFd(sno);
    FD_SET(fd, &readfds);
    if (fd > fdmax)
      fdmax = fd;
    ti = TailOfTerm(ti);
  }
  /* now, check the time */
  tsec = IntegerOfTerm(t2);
  tusec = IntegerOfTerm(t3);
  if (tsec < 0) /* off */ {
    ptime = NULL;
  } else {
    timeout.tv_sec = tsec;
    timeout.tv_usec = tusec;
    ptime = &timeout;
  }
  /* do the real work */
  if (select(fdmax+1, &readfds, &writefds, &exceptfds, ptime) < 0) {
#if HAVE_STRERROR
      Yap_Error(SYSTEM_ERROR, TermNil, 
	    "socket_select/5 (select: %s)", strerror(socket_errno));
#else
      Yap_Error(SYSTEM_ERROR, TermNil,
	    "socket_select/5 (select)");
#endif
  }
  tout = select_out_list(t1, &readfds);
  /* we're done, just pass the info back */
  return(Yap_unify(ARG4,tout));
}


static Int
p_current_host(void) {
  char oname[MAXHOSTNAMELEN], *name;
  Term t1 = Deref(ARG1), out;

  if (!IsVarTerm(t1) && !IsAtomTerm(t1)) {
    Yap_Error(TYPE_ERROR_ATOM,t1,"current_host/2");
    return(FALSE);
  }
  name = oname;
  if (gethostname(name, sizeof(oname)) < 0) {
#if HAVE_STRERROR
    Yap_Error(SYSTEM_ERROR, TermNil, 
	  "current_host/2 (gethostname: %s)", strerror(socket_errno));
#else
    Yap_Error(SYSTEM_ERROR, TermNil,
	  "current_host/2 (gethostname)");
#endif
    return(FALSE);
  }
  if ((strrchr(name,'.') == NULL)) {
    struct hostent *he;

    /* not a fully qualified name, ask the name server */
    if((he=gethostbyname(name))==NULL) {
#if HAVE_STRERROR
      Yap_Error(SYSTEM_ERROR, TermNil, 
	    "current_host/2 (gethostbyname: %s)", strerror(socket_errno));
#else
      Yap_Error(SYSTEM_ERROR, TermNil,
	    "current_host/2 (gethostbyname)");
#endif
      return(FALSE);
    }
    name = (char *)(he->h_name);
  }
  if (IsAtomTerm(t1)) {
    char *sin = RepAtom(AtomOfTerm(t1))->StrOfAE;
    int faq = (strrchr(sin,'.') != NULL);
    
    if (faq)
#if _MSC_VER || defined(__MINGW32__)
     return(_stricmp(name,sin) == 0);
#else
      return(strcasecmp(name,sin) == 0);
#endif
    else {
      int isize = strlen(sin);
      if (isize >= 256) {
	Yap_Error(SYSTEM_ERROR, ARG1,
	      "current_host/2 (input longer than longest FAQ host name)");
	return(FALSE);
      }
      if (name[isize] != '.') return(FALSE);
      name[isize] = '\0';
#if _MSC_VER || defined(__MINGW32__)
      return(_stricmp(name,sin) == 0);
#else
      return(strcasecmp(name,sin) == 0);
#endif
    }
  } else {
    out = MkAtomTerm(Yap_LookupAtom(name));
    return(Yap_unify(ARG1,out));
  }
}

static Int
p_hostname_address(void) {
  char *s;
  Term t1 = Deref(ARG1);
  Term t2 = Deref(ARG2);
  Term tin, out;
  struct hostent *he;

  if (!IsVarTerm(t1)) {
    if (!IsAtomTerm(t1)) {
      Yap_Error(TYPE_ERROR_ATOM,t1,"hostname_address/2");
      return(FALSE);
    } else tin = t1;
  } else if (IsVarTerm(t2)) {
    Yap_Error(INSTANTIATION_ERROR,t1,"hostname_address/5");
    return(FALSE);
  } else if (!IsAtomTerm(t2)) {
    Yap_Error(TYPE_ERROR_ATOM,t2,"hostname_address/2");
    return(FALSE);
  } else tin = t2;
  s = RepAtom(AtomOfTerm(tin))->StrOfAE;
  if (IsVarTerm(t1)) {
    if ((he = gethostbyaddr(s, strlen(s), AF_INET)) == NULL) {
#if HAVE_STRERROR
      Yap_Error(SYSTEM_ERROR, TermNil, 
	    "hostname_address/2 (gethostbyname: %s)", strerror(socket_errno));
#else
      Yap_Error(SYSTEM_ERROR, TermNil,
	    "hostname_address/2 (gethostbyname)");
#endif
    }
    out = MkAtomTerm(Yap_LookupAtom((char *)(he->h_name)));
    return(Yap_unify(out, ARG1));
  } else {
    struct in_addr adr;
    if ((he = gethostbyname(s)) == NULL) {
#if HAVE_STRERROR
      Yap_Error(SYSTEM_ERROR, TermNil, 
	    "hostname_address/2 (gethostbyname: %s)", strerror(socket_errno));
#else
      Yap_Error(SYSTEM_ERROR, TermNil,
	    "hostname_address/2 (gethostbyname)");
#endif
    }
    memcpy((char *) &adr,
	   (char *) he->h_addr_list[0], (size_t) he->h_length);
    out = MkAtomTerm(Yap_LookupAtom(inet_ntoa(adr)));
    return(Yap_unify(out, ARG2));
  }
}
#endif

void
Yap_InitSockets(void)
{
#ifdef   USE_SOCKET
  Yap_InitCPred("socket", 4, p_socket, SafePredFlag|SyncPredFlag);
  Yap_InitCPred("socket_close", 1, p_socket_close, SafePredFlag|SyncPredFlag);
  Yap_InitCPred("socket_bind", 2, p_socket_bind, SafePredFlag|SyncPredFlag);
  Yap_InitCPred("socket_connect", 3, p_socket_connect, SafePredFlag|SyncPredFlag);
  Yap_InitCPred("socket_listen", 2, p_socket_listen, SafePredFlag|SyncPredFlag);
  Yap_InitCPred("socket_accept", 3, p_socket_accept, SafePredFlag|SyncPredFlag);
  Yap_InitCPred("$socket_buffering", 4, p_socket_buffering, SafePredFlag|SyncPredFlag|HiddenPredFlag);
  Yap_InitCPred("$socket_select", 4, p_socket_select, SafePredFlag|SyncPredFlag|HiddenPredFlag);
  Yap_InitCPred("current_host", 1, p_current_host, SafePredFlag);
  Yap_InitCPred("hostname_address", 2, p_hostname_address, SafePredFlag);
#if _MSC_VER || defined(__MINGW32__)
  {
    WSADATA info;
    if (WSAStartup(MAKEWORD(2,1), &info) != 0)
      exit(1);
  }
#endif
#endif
}

