#include <winsock.h>
#include <stdio.h>

#include "wslib.h"


int make_socket(unsigned short int port, SOCKET *sock)
{
   struct sockaddr_in name;
   int status;

   /* Create the socket. */
   *sock = socket(PF_INET, SOCK_STREAM, 0);
   if (*sock == INVALID_SOCKET)
               return -1;

   if (set_blocked(*sock, 0))
               goto error;

   /* Give the socket a name. */
   name.sin_family = AF_INET;
   name.sin_port = htons(port);
   name.sin_addr.s_addr = htonl(INADDR_ANY);

   status = 1;
   (void)setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, (char *) &status,
           sizeof(status));

   if (bind (*sock, (struct sockaddr*)&name, sizeof(name)) < 0)
               goto error;

   return 0;

error:
   closesocket(*sock);
   return -1;
}


int init_sockaddr(struct sockaddr_in *name, const char *host_name,
       unsigned short int port)
{
   struct hostent *host_info;
       unsigned long addr;

       name->sin_family = AF_INET;
       name->sin_port = htons(port);

       addr = inet_addr(host_name);

       if (addr == INADDR_NONE) {
       if (! (host_info = gethostbyname(host_name)))
               return -1;
       } else {
               if (! (host_info = gethostbyaddr((char*)&addr, 4, AF_INET)))
                       return -1;
       }

       name->sin_addr = *(struct in_addr*)host_info->h_addr;
       return 0;
}


int conn_sock(char *server, int port, SOCKET *sock)
{
   struct sockaddr_in name;
       int err;

   *sock = socket(PF_INET, SOCK_STREAM, 0);
   if (*sock == INVALID_SOCKET)
               return 0;

   if (init_sockaddr(&name, server, port))
       goto error;

   if (connect(*sock, (struct sockaddr *) &name, sizeof(name)) == -1)
               goto error;

       if (set_blocked(*sock, 0))
               goto error;

   return 1;

error:
       err = WSAGetLastError();
   closesocket(*sock);
       WSASetLastError(err);
       *sock = INVALID_SOCKET;
   return 0;
}


int write_sock(SOCKET s, char *data, int len)
{
   int err, opt_len = sizeof(int);

   if (write_chars(s, data, len) == -1)
               return -1;
   if (getsockopt(s, SOL_SOCKET, SO_ERROR,
               (char*)&err, &opt_len) == -1)
               return -1;
   if (err != 0)
               return -1;
   return 0;
}


int set_blocked(SOCKET s, int blocked)
{
   unsigned long state;

       state = (unsigned long)(! blocked);
       return ioctlsocket(s, FIONBIO, &state);
}


char* read_err(int r)
{
   if (r == 0)
               return "EOF";
   else
               return ws_strerror();
}


int read_chars(SOCKET s, void *ptr, int to_read)
{
   char *p = (char*)ptr;
   int copied_now, copied;

   for (copied = 0; (unsigned)to_read > 0; ) {
               copied_now = recv(s, p, to_read, 0);
               if (copied_now <= 0) {
               if (copied_now == -1) {
                               if (WSAGetLastError() != WSAEWOULDBLOCK)
                               return copied_now;
                               copied_now = 0;
               } else
                               return copied_now;
               }
               copied += copied_now;
               to_read -= copied_now;
               p += copied_now;
   }
   return copied;
}


int write_chars(SOCKET s, void *ptr, int to_write)
{
   char *p = (char*)ptr;
   int copied_now, copied;

   for (copied = 0; (unsigned)to_write > 0; ) {
               copied_now = send(s, p, to_write, 0);
               if (copied_now < 0) {
               if (WSAGetLastError() != WSAEWOULDBLOCK)
                               return copied_now;
               copied_now = 0;
               }
               copied += copied_now;
               to_write -= copied_now;
               p += copied_now;
   }
   return copied;
}