/***********************************************************************
 *
 *      C interface to BSD sockets.
 *
 *      $Revision: 1.7.5$
 *      $Date: 2000/05/28 16:56:52$
 *      $Author: pb$
 *
 ***********************************************************************/

/***********************************************************************
 *
 * Copyright 1988-92, 1994-95, 1999, 2000 Free Software Foundation, Inc.
 * Written by Steve Byrne and Paolo Bonzini.
 *
 * This file is part of GNU Smalltalk.
 *
 * GNU Smalltalk 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, or (at your option) any later 
 * version.
 * 
 * GNU Smalltalk 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
 * GNU Smalltalk; see the file COPYING.  If not, write to the Free Software
 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  
 *
 ***********************************************************************/


#include "gstpub.h"

#if defined(HAVE_SYS_TIME_H) || defined(TIME_WITH_SYS_TIME)
# include <sys/time.h>
#endif
#if !defined(HAVE_SYS_TIME_H) || defined(TIME_WITH_SYS_TIME)
# include <time.h>
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#if __STDC__
#include <string.h>
#include <stdlib.h>
#endif /* STDC_HEADERS */

#ifndef HAVE_INET_SOCKETS
#error Internet sockets not available.
#endif

#include <stdio.h>
#include <errno.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>

#ifdef HAVE_SYS_UTSNAME_H
#include <sys/utsname.h>
#endif

#ifndef ntohl
#if WORDS_BIGENDIAN
  #define ntohl(x) (x)
  #define ntohs(x) (x)
#else
  #define ntohl(x) \
        ((unsigned long int)((((unsigned long int)(x) & 0x000000ffU) << 24) | \
                             (((unsigned long int)(x) & 0x0000ff00U) <<  8) | \
                             (((unsigned long int)(x) & 0x00ff0000U) >>  8) | \
                             (((unsigned long int)(x) & 0xff000000U) >> 24)))

  #define ntohs(x) \
        ((unsigned short int)((((unsigned short int)(x) & 0x00ff) << 8) | \
                              (((unsigned short int)(x) & 0xff00) >> 8)))
#endif
#endif /* ntohl */


#ifndef FD_SET
typedef int fd_set;
#define FD_ZERO(fdsetp)      (((fd_set *) fdsetp) = 0)
#define FD_SET(fd, fdsetp)   (((fd_set *) fdsetp) |=   1 << ((fd) - 1) )
#define FD_ISSET(fd, fdsetp) (((fd_set *) fdsetp) &    1 << ((fd) - 1) )
#define FD_CLR(fd, fdsetp)   (((fd_set *) fdsetp) &= ~(1 << ((fd) - 1)))
#define FD_SETSIZE	     (sizeof(fd_set) * 8)
#endif

#ifndef FD_SETSIZE
# define FD_SETSIZE getdtablesize()
#endif

/* This code exists because of a highly questionable decision by someone to
 * make the storage returned by gethostbyname be malloc'ed and then freed
 * before returning the value.  Gee..I hope you allocated enough storage for
 * your copy BEFORE you try to copy hostEnt, because  afterwards, if you
 * malloc, you'll get back the storage that I was using.
 * 
 * What a great design.  Makes your programs buggy as hell, but *IT DOESN'T
 * LEAK STORAGE*. Grr.......
 */
static char *
myGetHostByName(name)
     char *name;
{
  struct hostent *hostEnt;
  char   data[512], **h, *i, *result;
  extern int h_errno;

  hostEnt = gethostbyname(name);
  if (!hostEnt) {
    return (NULL);
  }

  h = hostEnt->h_addr_list;
  i = data;
  while (*h) {
    memcpy(i, *h++, 4);
    i += 4;
  }

  result = malloc(i - data);
  memcpy(result, data, i - data);
  return (result);
}

static char *
myGetHostByAddr(addr)
     char *addr;
{
  struct hostent *hostEnt;
  char *result;

  hostEnt = gethostbyaddr(addr, 4, PF_INET);
  if (hostEnt) {
    result = malloc(128);				/* out of a hat */
    strncpy(result, hostEnt->h_name, 128);
  } else {
    result = NULL;
  }

  return (result);
}

static char *
myGetHostName()
{
  char *result;

  result = malloc(128);
#ifdef HAVE_UNAME
  {
    struct utsname utsname;
    int ret;

    ret = uname (&utsname);
    if (ret < 0)
      return NULL;

    strncpy (result, utsname.nodename, 128);
    result[127] = '\0';
  }
#else
#ifdef HAVE_GETHOSTNAME
  {
    extern int gethostname();
    gethostname(result, 128);
  }
#else
  strcpy (result, "localhost");			/* terrible guess */
#endif
#endif
  return (result);
}

static void
getAnyLocalAddress(name, whereToPut)
     char *name;
     char *whereToPut;
{
  struct hostent *hostEnt;

  hostEnt = gethostbyname(name);
  memcpy(whereToPut, hostEnt->h_addr, 4);
}

static int
available(s, reading)
     int s;
     mst_Boolean reading;
{
  struct timeval time = {0, 0};
  int result;

  fd_set	fds;
  FD_ZERO(&fds);
  FD_SET(s, &fds);

  do {
    errno = 0;
    result = reading
      ? select(FD_SETSIZE, &fds, NULL, NULL, &time)
      : select(FD_SETSIZE, NULL, &fds, NULL, &time);
  } while ((result == -1) && (errno == EINTR));

  return (result);
}

#define constantFunction(name, constant) \
  static long name() { return (constant); }

constantFunction(getAF, 	   AF_INET);
constantFunction(getPF, 	   PF_INET);
constantFunction(ipMulticastTtl,   IP_MULTICAST_TTL);
constantFunction(ipMulticastIf,    IP_MULTICAST_IF);
constantFunction(ipAddMembership,  IP_ADD_MEMBERSHIP);
constantFunction(ipDropMembership, IP_DROP_MEMBERSHIP);
constantFunction(solSocket,        SOL_SOCKET);
constantFunction(ipprotoIp,        IPPROTO_IP);
constantFunction(ipprotoTcp,       IPPROTO_TCP);

/* UnixStream functions ------------------------------- */

static int			fullWrite();

static OOP			readChar();
static OOP			readUChar();
static OOP			readShort();
static OOP			readUShort();
static OOP			readLong();
static OOP			readULong();
static OOP			readDouble();
static OOP			readFloat();

static void			writeChar();
static void			writeShort();
static void			writeLong();
static void			writeDouble();
static void			writeFloat();

#ifndef HAVE_IOCTL
static void			ioctl();

void
ioctl()
{
}
#endif

#ifdef unused
/**//* Like `read' but keeps trying until it gets SIZE bytes or reaches eof.  */
/**/int
/**/fullRead (fd, buf, size)
/**/     int fd;
/**/     char *buf;
/**/     int size;
/**/{
/**/  int num, sofar = 0;
/**/
/**/  while ((num = read (fd, buf + sofar, size - sofar)) > 0) {
/**/    sofar += num;
/**/  }
/**/
/**/  return (num < 0 ? num : sofar);
/**/}
#endif

#ifdef DEBUG_READ
int
myRead(fd, buf, size)
     int	fd;
     voidPtr    buf;
     int        size;
{
  int		result;

  result = read(fd, buf, size);
  if (result > -1) {
    write(1, buf, result);
  }
  return (result);
}
#else
#define myRead          read
#endif


OOP
readChar(fd)
     int	fd;
{
  char		c;

  if (read(fd, &c, sizeof(c)) != sizeof(c)) {
    return (nilOOP);
  }

  return (charToOOP(c));
}

OOP
readUChar(fd)
     int	fd;
{
  unsigned char	c;

  if (read(fd, &c, sizeof(c)) != sizeof(c)) {
    return (nilOOP);
  }

  return (charToOOP(c));
}

OOP
readShort(fd)
     int	fd;
{
  short		s;

  if (read(fd, &s, sizeof(s)) != sizeof(s)) {
    return (nilOOP);
  }

  return (intToOOP(s));
}

OOP
readUShort(fd)
     int	fd;
{
  unsigned short s;

  if (read(fd, &s, sizeof(s)) != sizeof(s)) {
    return (nilOOP);
  }

  return (intToOOP(s));
}

OOP
readLong(fd)
     int	fd;
{
  long		l;

  if (read(fd, &l, sizeof(l)) != sizeof(l)) {
    return (nilOOP);
  }

  return (intToOOP(l));
}

OOP
readULong(fd)
     int	fd;
{
  unsigned long l;

  if (read(fd, &l, sizeof(l)) != sizeof(l)) {
    return (nilOOP);
  }

  return (intToOOP(l));
}

OOP
readDouble(fd)
int	fd;
{
  double 	d;

  if (read(fd, &d, sizeof(d)) != sizeof(d)) {
    return (nilOOP);
  }

  return (floatToOOP(d));
}

OOP
readFloat(fd)
int	fd;
{
  float		f;

  if (read(fd, &f, sizeof(f)) != sizeof(f)) {
    return (nilOOP);
  }

  return (floatToOOP((double)f));
}

/* Like `write' but keeps trying until it writes SIZE bytes.  */
int
fullWrite (fd, buf, size)
     int fd;
     char *buf;
     int size;
{
  static mst_Boolean signalOk = false;
  int num = 0, sofar = 0;

  if (!signalOk) {
    setSignalHandler(SIGPIPE, SIG_IGN);
    signalOk = true;
  }
  for(; size; buf += num, size -= num, sofar += num) {
    num = write (fd, buf, size);
    if (num <= 0) {
      break;
    }
  }

  return (num < 0 ? num : sofar);
}

void
writeChar(fd, c)
     int	fd;
     char	c;
{
  fullWrite(fd, &c, sizeof(c));
}

void
writeShort(fd, s)
     int	fd;
     short	s;
{
  fullWrite(fd, &s, sizeof(s));
}

void
writeLong(fd, l)
     int	fd;
     long	l;
{
  fullWrite(fd, &l, sizeof(l));
}

void
writeFloat(fd, f)
     int	fd;
     float	f;
{
  fullWrite(fd, &f, sizeof(f));
}

void
writeDouble(fd, d)
     int	fd;
     double	d;
{
  fullWrite(fd, &d, sizeof(d));
}

/* end UnixStream functions --------------------------- */

void
initTCP()
{
  extern void open();
#if defined(HAVE_IOCTL) && !defined(IOCTL_IN_UNISTD_H)
  extern void ioctl();
#endif

  defineCFunc("TCPlookupAllHostAddr", myGetHostByName);
  defineCFunc("TCPgetHostByAddr", myGetHostByAddr);
  defineCFunc("TCPgetLocalName", myGetHostName);
  defineCFunc("TCPgetAnyLocalAddress", getAnyLocalAddress);

  defineCFunc("TCPaccept", accept);
  defineCFunc("TCPbind", bind);
  defineCFunc("TCPconnect", connect);
  defineCFunc("TCPgetpeername", getpeername);
  defineCFunc("TCPgetsockname", getsockname);
  defineCFunc("TCPlisten", listen);
  defineCFunc("TCPrecvfrom", recvfrom);
  defineCFunc("TCPsendto", sendto);
  defineCFunc("TCPsetsockopt", setsockopt);
  defineCFunc("TCPgetsockopt", setsockopt);
  defineCFunc("TCPsocket", socket);

  defineCFunc("TCPgetPF", getPF);
  defineCFunc("TCPgetAF", getAF);
  defineCFunc("TCPipMulticastTtl",   ipMulticastTtl);
  defineCFunc("TCPipMulticastIf",    ipMulticastIf);
  defineCFunc("TCPipAddMembership",  ipAddMembership);
  defineCFunc("TCPipDropMembership", ipDropMembership);
  defineCFunc("TCPsolSocket",        solSocket);
  defineCFunc("TCPipprotoIp",        ipprotoIp);
  defineCFunc("TCPipprotoTcp",       ipprotoTcp);

  defineCFunc("available", available);
  defineCFunc("open", open);
  defineCFunc("close", close);
  defineCFunc("read", myRead);
  defineCFunc("write", fullWrite);
  defineCFunc("ioctl", ioctl);
  defineCFunc("lseek", lseek);

  /* just to round out the set */
  defineCFunc("readChar", readChar);
  defineCFunc("readUChar", readUChar);
  defineCFunc("readShort", readShort);
  defineCFunc("readUShort", readUShort);
  defineCFunc("readLong", readLong);
  defineCFunc("readULong", readULong);
  defineCFunc("readFloat", readFloat);
  defineCFunc("readDouble", readDouble);

  defineCFunc("writeChar", writeChar);
  defineCFunc("writeShort", writeShort);
  defineCFunc("writeLong", writeLong);
  defineCFunc("writeFloat", writeFloat);
  defineCFunc("writeDouble", writeDouble);
}

