/*
  INet Socket Code

  by Johnson Earls
  version 1.0
  created 9-Dec-90

  changed by cknight
  version 1.1
  [changed it to fit in with SpiderBot,
  also made it a linked-list implementation]
  2-Mar-91
*/

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <ctype.h>
#include <sys/time.h>
#include "stringops.h"
#include "socket.h"

sockrec *socket_allocate(sockrec *s, long data)
{
	sockrec *new;

	#ifdef FUNCTIONS
	puts ("**socket_allocate");
	#endif

	new = allocate (sockrec);
	new->data = data;
	new->next = s;
	return new;
}

sockrec *socket_deallocate(sockrec *s, sockrec *d)
{
	sockrec *list;

	#ifdef FUNCTIONS
	puts ("**socket_deallocate");
	#endif

	if (s == d)
	{
		list = s->next;
		free (s);
		return list;
	}
	else
	{
		sockrec *temp;
		for (list = s; (list->next != s) && (list->next != NULL);
			list = list->next);

		if (list->next != NULL)
		{
			temp = list->next;
			list->next = temp->next;
			free (temp);
		}
		return s;
	}
}

int socket_connect(sockrec *s, char *name, int port)
{
	struct hostent *host;
	struct sockaddr_in sin;
	unsigned char IPadr[4], lp, flag;
	char *p;

	#ifdef FUNCTIONS
	puts ("**socket_connect");
	#endif

	if (isdigit(*(name)))
	{
		lp = flag = IPadr[0] = IPadr[1] = IPadr[2] = IPadr[3] = 0;
		p = name;
		while (*p)
		{
			if (*p == '.')
			        if ((!flag) || (lp == 3))
					return(ERR_SOCKBADADDR);
				else
					lp++;
			else
			if (isdigit(*p))
			{
				IPadr[lp] = IPadr[lp] * 10 + (*p) - '0';
				flag = 1;
			}
			else
				return(ERR_SOCKBADADDR);
			p++;
		}
		if (lp < 3)
			return(ERR_SOCKBADADDR);
	}
	else
	{
		host = gethostbyname(name);
		if (host == (struct hostent *)0)
			return(ERR_SOCKBADADDR);

		(void)bcopy(host->h_addr, IPadr, 4);
	}

	if ((s->sd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		return(ERR_SOCKCANTCREATE);

	sin.sin_family = AF_INET;
	sin.sin_port = htons(port);
	(void)bcopy(IPadr, (char *)&sin.sin_addr, 4);
	if (connect(s->sd, &sin, sizeof(struct sockaddr_in)) < 0)
		return(ERR_SOCKCANTCONNECT);

	return(0);
}

void socket_disconnect(sockrec *s)
{
	#ifdef FUNCTIONS
	puts ("**socket_disconnect");
	#endif

  close(*s);
}

int socket_read(sockrec *s, char **str)
{
  char buf[65536]; /* longer than will be needed [hopefully] */
  char *p, ch;
  int ret;

	#ifdef FUNCTIONS
	puts ("**socket_read");
	#endif

  p = buf;
  while(1) {
    if ((ret=recv(s->sd, &ch, 1, 0)) < 0) 
      return(ERR_SOCKERRREAD);
    else if (!ret)
      return(ERR_SOCKCLOSED);
    if (ch == '\n') break;
    if (ch != '\r')
      *(p++) = ch;
  }
  *p = '\0';
  copystring (*str, buf);
  return(0);
}

int socket_write(sockrec *s, char *str)
{
  char nl = '\n';
	#ifdef FUNCTIONS
	puts ("**socket_write");
	#endif

  if ((write(s->sd, str, strlen(str)) < 0) || (write(s->sd, &nl, 1)))
    return(ERR_SOCKERRWRITE);
  return(0);
}

int socket_write_noret(sockrec *s, char *str)
{
	#ifdef FUNCTIONS
	puts ("**socket_write_noret");
	#endif

  if ((write(s->sd, str, strlen(str)) < 0))
    return(ERR_SOCKERRWRITE);
  return(0);
}

int socket_ready(sockrec *s)
{
	#ifdef FUNCTIONS
	puts ("**socket_ready");
	#endif

  return(socket_wait(s, 0L, 50L));
}

int socket_wait(sockrec *s, long timeout, long utimeout)
{
  struct timeval wait;
  fd_set readfds;

#ifdef FUNCTIONS
puts ("**socket_wait");
#endif

  wait.tv_sec = timeout;
  wait.tv_usec = utimeout;

  FD_ZERO(&readfds);
  FD_SET(s->sd, &readfds);

	if (timeout < 0) {
		if (select(FD_SETSIZE, &readfds, (fd_set *)0, (fd_set *)0,
			(struct timeval *)0) < 0)
		return(ERR_SOCKSELECT);
	} else
    if (select(FD_SETSIZE, &readfds, (fd_set *)0, (fd_set *)0, &wait) < 0)
      return(ERR_SOCKSELECT);
  if (FD_ISSET(s->sd, &readfds)) return(1);
  return(0);
}

int socket_wait_kbd(sockrec *s, long timeout, long utimeout)
{
  struct timeval wait;
  fd_set readfds;

	#ifdef FUNCTIONS
	puts ("**socket_wait_kbd");
	#endif

  wait.tv_sec = timeout;
  wait.tv_usec = utimeout;

  FD_ZERO(&readfds);
  FD_SET(s->sd, &readfds);
  FD_SET(0, &readfds);

  if (timeout < 0) {
    if (select(FD_SETSIZE, &readfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0) < 0)
      return(ERR_SOCKSELECT);
  } else
    if (select(FD_SETSIZE, &readfds, (fd_set *)0, (fd_set *)0, &wait) < 0)
      return(ERR_SOCKSELECT);
  if (FD_ISSET(s->sd, &readfds)) return(1);
  if (FD_ISSET(0, &readfds)) return(2);
  return(0);
}