tOops! Network and error modules added. ;) (Plus, link to "dopewars -h" added to Win32 installer) - vaccinewars - be a doctor and try to vaccinate the world
 (HTM) git clone git://src.adamsgaard.dk/vaccinewars
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 2d06fc42ffd5356336f0c442bffa716cdf1e5980
 (DIR) parent 75487908600e5379fe2f8412948355e8df683006
 (HTM) Author: Ben Webb <ben@salilab.org>
       Date:   Mon,  1 Oct 2001 18:14:27 +0000
       
       Oops! Network and error modules added. ;) (Plus, link to "dopewars -h" added to Win32 installer)
       
       
       Diffstat:
         A src/error.c                         |     162 ++++++++++++++++++++++++++++++
         A src/error.h                         |      58 ++++++++++++++++++++++++++++++
         A src/network.c                       |     657 +++++++++++++++++++++++++++++++
         A src/network.h                       |     152 +++++++++++++++++++++++++++++++
         M win32/filelist                      |       4 +++-
       
       5 files changed, 1032 insertions(+), 1 deletion(-)
       ---
 (DIR) diff --git a/src/error.c b/src/error.c
       t@@ -0,0 +1,162 @@
       +/* error.c        Error-handling routines for dopewars                  */
       +/* Copyright (C)  1998-2001  Ben Webb                                   */
       +/*                Email: ben@bellatrix.pcl.ox.ac.uk                     */
       +/*                WWW: http://bellatrix.pcl.ox.ac.uk/~ben/dopewars/     */
       +
       +/* This program 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       */
       +/* of the License, or (at your option) any later version.               */
       +
       +/* This program 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 this program; if not, write to the Free Software          */
       +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston,               */
       +/*                   MA  02111-1307, USA.                               */
       +
       +#ifdef HAVE_CONFIG_H
       +#include <config.h>
       +#endif
       +
       +#include <glib.h>      /* For GString functions */
       +#include <string.h>    /* For strerror */
       +
       +#ifdef CYGWIN
       +#include <windows.h>   /* For FormatMessage() etc. */
       +#include <winsock.h>   /* For WSAxxx constants */
       +#else
       +#include <netdb.h>     /* For h_errno error codes */
       +#endif
       +
       +#include "error.h"
       +#include "nls.h"
       +
       +typedef struct _ErrTable {
       +  gint code;
       +  gchar *string;
       +} ErrTable;
       +
       +void ClearError(LastError *error) {
       +  error->type=NULL;
       +}
       +
       +gboolean IsError(LastError *error) {
       +  return (error->type!=NULL);
       +}
       +
       +void SetError(LastError *error,ErrorType *type,gint code) {
       +  error->type=type;
       +  error->code=code;
       +}
       +
       +static void LookupErrorCode(GString *str,gint code,ErrTable *table,
       +                            gchar *fallbackstr) {
       +  for (;table && table->string;table++) {
       +    if (code==table->code) {
       +      g_string_append(str,_(table->string));
       +      return;
       +    }
       +  }
       +  g_string_sprintfa(str,fallbackstr,code);
       +}
       +
       +/* "Custom" error handling */
       +static ErrTable CustomErrStr[] = {
       +  { E_FULLBUF,N_("Connection dropped due to full buffer") },
       +  { 0,NULL }
       +};
       +
       +void CustomAppendError(GString *str,LastError *error) {
       +  LookupErrorCode(str,error->code,CustomErrStr,_("Internal error code %d"));
       +}
       +
       +static ErrorType ETCustom = { CustomAppendError };
       +ErrorType *ET_CUSTOM = &ETCustom;
       +
       +/* "errno" error handling */
       +void ErrnoAppendError(GString *str,LastError *error) {
       +  g_string_append(str,strerror(error->code));
       +}
       +
       +static ErrorType ETErrno = { ErrnoAppendError };
       +ErrorType *ET_ERRNO = &ETErrno;
       +
       +#ifdef CYGWIN
       +
       +/* Winsock error handling */
       +static ErrTable WSAErrStr[] = {
       +/* These are the explanations of the various Windows Sockets error codes */
       +  { WSANOTINITIALISED,N_("WinSock has not been properly initialised") },
       +  { WSAENETDOWN,N_("The network subsystem has failed") },
       +  { WSAEADDRINUSE,N_("Address already in use") },
       +  { WSAENETDOWN,N_("Cannot reach the network") },
       +  { WSAETIMEDOUT,N_("The connection timed out") },
       +  { WSAEMFILE,N_("Out of file descriptors") },
       +  { WSAENOBUFS,N_("Out of buffer space") },
       +  { WSAEOPNOTSUPP,N_("Operation not supported") },
       +  { WSAECONNABORTED,N_("Connection aborted due to failure") },
       +  { WSAECONNRESET,N_("Connection reset by remote host") },
       +  { WSAECONNREFUSED,N_("Connection refused") },
       +  { WSAEAFNOSUPPORT,N_("Address family not supported") },
       +  { WSAEPROTONOSUPPORT,N_("Protocol not supported") },
       +  { WSAESOCKTNOSUPPORT,N_("Socket type not supported") },
       +  { WSAHOST_NOT_FOUND,N_("Host not found") },
       +  { WSATRY_AGAIN,N_("Temporary name server error - try again later") },
       +  { WSANO_RECOVERY,N_("Failed to contact nameserver") },
       +  { WSANO_DATA,N_("Valid name, but no DNS data record present") },
       +  { 0,NULL }
       +};
       +
       +void WinsockAppendError(GString *str,LastError *error) {
       +  LookupErrorCode(str,error->code,WSAErrStr,_("Network error code %d"));
       +}
       +
       +static ErrorType ETWinsock = { WinsockAppendError };
       +ErrorType *ET_WINSOCK = &ETWinsock;
       +
       +/* Standard Win32 "GetLastError" handling */
       +void Win32AppendError(GString *str,LastError *error) {
       +  LPTSTR lpMsgBuf;
       +
       +  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
       +                NULL,error->code,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
       +                (LPTSTR)&lpMsgBuf,0,NULL);
       +  g_string_append(str,lpMsgBuf);
       +  LocalFree(lpMsgBuf);
       +}
       +
       +static ErrorType ETWin32 = { Win32AppendError };
       +ErrorType *ET_WIN32 = &ETWin32;
       +
       +#else
       +
       +/* h_errno error handling */
       +static ErrTable DNSErrStr[] = {
       +/* These are the explanations of the various name server error codes */
       +  { HOST_NOT_FOUND,N_("Host not found") },
       +  { TRY_AGAIN,N_("Temporary name server error - try again later") },
       +  { 0,NULL }
       +};
       +
       +void HErrnoAppendError(GString *str,LastError *error) {
       +  LookupErrorCode(str,error->code,DNSErrStr,_("Name server error code %d"));
       +}
       +
       +static ErrorType ETHErrno = { HErrnoAppendError };
       +ErrorType *ET_HERRNO = &ETHErrno;
       +
       +#endif /* CYGWIN */
       +
       +void g_string_assign_error(GString *str,LastError *error) {
       +  g_string_truncate(str,0);
       +  g_string_append_error(str,error);
       +}
       +
       +void g_string_append_error(GString *str,LastError *error) {
       +  if (!error->type) return;
       +  (*error->type->AppendErrorString)(str,error);
       +}
 (DIR) diff --git a/src/error.h b/src/error.h
       t@@ -0,0 +1,58 @@
       +/* error.h        Header file for dopewars error-handling routines      */
       +/* Copyright (C)  1998-2001  Ben Webb                                   */
       +/*                Email: ben@bellatrix.pcl.ox.ac.uk                     */
       +/*                WWW: http://bellatrix.pcl.ox.ac.uk/~ben/dopewars/     */
       +
       +/* This program 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       */
       +/* of the License, or (at your option) any later version.               */
       +
       +/* This program 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 this program; if not, write to the Free Software          */
       +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston,               */
       +/*                   MA  02111-1307, USA.                               */
       +
       +
       +#ifndef __ERROR_H__
       +#define __ERROR_H__
       +
       +#ifdef HAVE_CONFIG_H
       +#include <config.h>
       +#endif
       +
       +#include <glib.h>
       +
       +struct _LastError;
       +typedef struct _ErrorType {
       +   void (*AppendErrorString)(GString *str,struct _LastError *error);
       +} ErrorType;
       +
       +typedef struct _LastError {
       +  gint code;
       +  ErrorType *type;
       +} LastError;
       +
       +extern ErrorType *ET_CUSTOM,*ET_ERRNO;
       +#ifdef CYGWIN
       +extern ErrorType *ET_WIN32,*ET_WINSOCK;
       +#else
       +extern ErrorType *ET_HERRNO;
       +#endif
       +
       +typedef enum {
       +   E_FULLBUF
       +} CustomErrorCode;
       +
       +void ClearError(LastError *error);
       +gboolean IsError(LastError *error);
       +void SetError(LastError *error,ErrorType *type,gint code);
       +void g_string_assign_error(GString *str,LastError *error);
       +void g_string_append_error(GString *str,LastError *error);
       +
       +#endif /* __ERROR_H__ */
 (DIR) diff --git a/src/network.c b/src/network.c
       t@@ -0,0 +1,657 @@
       +/* network.c      Low-level networking routines                         */
       +/* Copyright (C)  1998-2001  Ben Webb                                   */
       +/*                Email: ben@bellatrix.pcl.ox.ac.uk                     */
       +/*                WWW: http://bellatrix.pcl.ox.ac.uk/~ben/dopewars/     */
       +
       +/* This program 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       */
       +/* of the License, or (at your option) any later version.               */
       +
       +/* This program 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 this program; if not, write to the Free Software          */
       +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston,               */
       +/*                   MA  02111-1307, USA.                               */
       +
       +#ifdef HAVE_CONFIG_H
       +#include <config.h>
       +#endif
       +
       +#ifdef NETWORKING
       +
       +#ifdef CYGWIN
       +#include <windows.h>    /* For datatypes such as BOOL */
       +#include <winsock.h>    /* For network functions */
       +#else
       +#include <sys/types.h>  /* For size_t etc. */
       +#include <sys/socket.h> /* For struct sockaddr etc. */
       +#include <netinet/in.h> /* For struct sockaddr_in etc. */
       +#include <arpa/inet.h>  /* For socklen_t */
       +#include <string.h>     /* For memcpy, strlen etc. */
       +#ifdef HAVE_UNISTD_H
       +#include <unistd.h>     /* For close(), various types and constants */
       +#endif
       +#ifdef HAVE_FCNTL_H
       +#include <fcntl.h>      /* For fcntl() */
       +#endif
       +#include <netdb.h>      /* For gethostbyname() */
       +#endif /* CYGWIN */
       +
       +#include <glib.h>
       +#include <errno.h>      /* For errno and Unix error codes */
       +#include <stdlib.h>     /* For exit() and atoi() */
       +#include <stdio.h>      /* For perror() */
       +
       +#include "error.h"
       +#include "network.h"
       +#include "nls.h"
       +
       +/* Maximum sizes (in bytes) of read and write buffers - connections should
       +   be dropped if either buffer is filled */
       +#define MAXREADBUF   (32768)
       +#define MAXWRITEBUF  (65536)
       +
       +#ifdef CYGWIN
       +
       +void StartNetworking() {
       +  WSADATA wsaData;
       +  if (WSAStartup(MAKEWORD(1,0),&wsaData)!=0) {
       +    g_warning(_("Cannot initialise WinSock!"));
       +    exit(1);
       +  }
       +}
       +
       +void StopNetworking() {
       +  WSACleanup();
       +}
       +
       +void SetReuse(SOCKET sock) {
       +  BOOL i=TRUE;
       +  if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char *)&i,sizeof(i))==-1) {
       +    perror("setsockopt"); exit(1);
       +  }
       +}
       +
       +void SetBlocking(SOCKET sock,gboolean blocking) {
       +  unsigned long param;
       +  param = blocking ? 0 : 1;
       +  ioctlsocket(sock,FIONBIO,&param);
       +}
       +
       +#else
       +
       +void StartNetworking() {}
       +void StopNetworking() {}
       +
       +void SetReuse(int sock) {
       +  int i=1;
       +  if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(i))==-1) {
       +    perror("setsockopt"); exit(1);
       +  }
       +}
       +
       +void SetBlocking(int sock,gboolean blocking) {
       +  fcntl(sock,F_SETFL,blocking ? 0 : O_NONBLOCK);
       +}
       +
       +#endif /* CYGWIN */
       +
       +static gboolean FinishConnect(int fd,LastError *error);
       +
       +static void NetBufCallBack(NetworkBuffer *NetBuf) {
       +   if (NetBuf && NetBuf->CallBack) {
       +      (*NetBuf->CallBack)(NetBuf,!NetBuf->WaitConnect,
       +                          NetBuf->WriteBuf.DataPresent || NetBuf->WaitConnect);
       +   }
       +}
       +
       +static void NetBufCallBackStop(NetworkBuffer *NetBuf) {
       +   if (NetBuf && NetBuf->CallBack) (*NetBuf->CallBack)(NetBuf,FALSE,FALSE);
       +}
       +
       +void InitNetworkBuffer(NetworkBuffer *NetBuf,char Terminator,char StripChar) {
       +/* Initialises the passed network buffer, ready for use. Messages sent */
       +/* or received on the buffered connection will be terminated by the    */
       +/* given character, and if they end in "StripChar" it will be removed  */
       +/* before the messages are sent or received.                           */
       +   NetBuf->fd=-1;
       +   NetBuf->InputTag=0;
       +   NetBuf->CallBack=NULL;
       +   NetBuf->CallBackData=NULL;
       +   NetBuf->Terminator=Terminator;
       +   NetBuf->StripChar=StripChar;
       +   NetBuf->ReadBuf.Data=NetBuf->WriteBuf.Data=NULL;
       +   NetBuf->ReadBuf.Length=NetBuf->WriteBuf.Length=0;
       +   NetBuf->ReadBuf.DataPresent=NetBuf->WriteBuf.DataPresent=0;
       +   NetBuf->WaitConnect=FALSE;
       +   ClearError(&NetBuf->error);
       +}
       +
       +void SetNetworkBufferCallBack(NetworkBuffer *NetBuf,NBCallBack CallBack,
       +                              gpointer CallBackData) {
       +   NetBufCallBackStop(NetBuf);
       +   NetBuf->CallBack=CallBack;
       +   NetBuf->CallBackData=CallBackData;
       +   NetBufCallBack(NetBuf);
       +}
       +
       +void BindNetworkBufferToSocket(NetworkBuffer *NetBuf,int fd) {
       +/* Sets up the given network buffer to handle data being sent/received */
       +/* through the given socket                                            */
       +   NetBuf->fd=fd;
       +}
       +
       +gboolean IsNetworkBufferActive(NetworkBuffer *NetBuf) {
       +/* Returns TRUE if the pointer is to a valid network buffer, and it's */
       +/* connected to an active socket.                                     */
       +   return (NetBuf && NetBuf->fd>=0);
       +}
       +
       +gboolean StartNetworkBufferConnect(NetworkBuffer *NetBuf,gchar *RemoteHost,
       +                                   unsigned RemotePort) {
       +  ShutdownNetworkBuffer(NetBuf);
       +  if (StartConnect(&NetBuf->fd,RemoteHost,RemotePort,TRUE,&NetBuf->error)) {
       +    NetBuf->WaitConnect=TRUE;
       +
       +/* Notify the owner if necessary to check for the connection completing */
       +    NetBufCallBack(NetBuf);
       +
       +    return TRUE;
       +  } else {
       +    return FALSE;
       +  }
       +}
       +
       +void ShutdownNetworkBuffer(NetworkBuffer *NetBuf) {
       +/* Frees the network buffer's data structures (leaving it in the  */
       +/* 'initialised' state) and closes the accompanying socket.       */
       +
       +   NetBufCallBackStop(NetBuf);
       +
       +   if (NetBuf->fd>=0) CloseSocket(NetBuf->fd);
       +
       +   g_free(NetBuf->ReadBuf.Data);
       +   g_free(NetBuf->WriteBuf.Data);
       +
       +   InitNetworkBuffer(NetBuf,NetBuf->Terminator,NetBuf->StripChar);
       +}
       +
       +void SetSelectForNetworkBuffer(NetworkBuffer *NetBuf,fd_set *readfds,
       +                               fd_set *writefds,fd_set *errorfds,int *MaxSock) {
       +/* Updates the sets of read and write file descriptors to monitor    */
       +/* input to/output from the given network buffer. MaxSock is updated */
       +/* with the highest-numbered file descriptor (plus 1) for use in a   */
       +/* later select() call.                                              */
       +   if (!NetBuf || NetBuf->fd<=0) return;
       +   FD_SET(NetBuf->fd,readfds);
       +   if (errorfds) FD_SET(NetBuf->fd,errorfds);
       +   if (NetBuf->fd >= *MaxSock) *MaxSock=NetBuf->fd+1;
       +   if (NetBuf->WriteBuf.DataPresent || NetBuf->WaitConnect) {
       +      FD_SET(NetBuf->fd,writefds);
       +   }
       +}
       +
       +static gboolean DoNetworkBufferStuff(NetworkBuffer *NetBuf,gboolean ReadReady,
       +                                     gboolean WriteReady,gboolean ErrorReady,
       +                                     gboolean *ReadOK,gboolean *WriteOK,
       +                                     gboolean *ErrorOK) {
       +/* Reads and writes data if the network connection is ready. Sets the  */
       +/* various OK variables to TRUE if no errors occurred in the relevant  */
       +/* operations, and returns TRUE if data was read and is waiting for    */
       +/* processing.                                                         */
       +   gboolean DataWaiting=FALSE,ConnectDone=FALSE;
       +   gboolean retval;
       +   *ReadOK=*WriteOK=*ErrorOK=TRUE;
       +
       +   if (ErrorReady) *ErrorOK=FALSE;
       +   else if (NetBuf->WaitConnect) {
       +      if (WriteReady) {
       +         retval=FinishConnect(NetBuf->fd,&NetBuf->error);
       +         ConnectDone=TRUE;
       +         NetBuf->WaitConnect=FALSE;
       +
       +         if (!retval) {
       +            *WriteOK=FALSE;
       +         }
       +      }
       +   } else {
       +      if (WriteReady) *WriteOK=WriteDataToWire(NetBuf);
       +
       +      if (ReadReady) {
       +         *ReadOK=ReadDataFromWire(NetBuf);
       +         if (NetBuf->ReadBuf.DataPresent>0) DataWaiting=TRUE;
       +      }
       +   }
       +
       +   if (!(*ErrorOK && *WriteOK && *ReadOK)) {
       +/* We don't want to check the socket any more */
       +      NetBufCallBackStop(NetBuf);
       +/* If there were errors, then the socket is now useless - so close it */
       +      CloseSocket(NetBuf->fd);
       +      NetBuf->fd=-1;
       +   } else if (ConnectDone) {
       +/* If we just connected, then no need to listen for write-ready status
       +   any more */
       +      NetBufCallBack(NetBuf);
       +   } else if (WriteReady && NetBuf->WriteBuf.DataPresent==0) {
       +/* If we wrote out everything, then tell the owner so that the socket no
       +   longer needs to be checked for write-ready status */
       +      NetBufCallBack(NetBuf);
       +   }
       +
       +   return DataWaiting;
       +}
       +
       +gboolean RespondToSelect(NetworkBuffer *NetBuf,fd_set *readfds,
       +                         fd_set *writefds,fd_set *errorfds,
       +                         gboolean *DoneOK) {
       +/* Responds to a select() call by reading/writing data as necessary.   */
       +/* If any data were read, TRUE is returned. "DoneOK" is set TRUE       */
       +/* unless a fatal error (i.e. the connection was broken) occurred.     */
       +   gboolean ReadOK,WriteOK,ErrorOK;
       +   gboolean DataWaiting=FALSE;
       +
       +   *DoneOK=TRUE;
       +   if (!NetBuf || NetBuf->fd<=0) return DataWaiting;
       +   DataWaiting=DoNetworkBufferStuff(NetBuf,FD_ISSET(NetBuf->fd,readfds),
       +                        FD_ISSET(NetBuf->fd,writefds),
       +                        errorfds ? FD_ISSET(NetBuf->fd,errorfds) : FALSE,
       +                        &ReadOK,&WriteOK,&ErrorOK);
       +   *DoneOK=(WriteOK && ErrorOK && ReadOK);
       +   return DataWaiting;
       +}
       +
       +gboolean NetBufHandleNetwork(NetworkBuffer *NetBuf,gboolean ReadReady,
       +                             gboolean WriteReady,gboolean *DoneOK) {
       +   gboolean ReadOK,WriteOK,ErrorOK;
       +   gboolean DataWaiting=FALSE;
       +
       +   *DoneOK=TRUE;
       +   if (!NetBuf || NetBuf->fd<=0) return DataWaiting;
       +
       +   DataWaiting=DoNetworkBufferStuff(NetBuf,ReadReady,WriteReady,FALSE,
       +                                    &ReadOK,&WriteOK,&ErrorOK);
       +
       +   *DoneOK=(WriteOK && ErrorOK && ReadOK);
       +   return DataWaiting;
       +}
       +
       +gint CountWaitingMessages(NetworkBuffer *NetBuf) {
       +/* Returns the number of complete (terminated) messages waiting in the   */
       +/* given network buffer. This is the number of times that                */
       +/* GetWaitingMessage() can be safely called without it returning NULL.   */
       +   ConnBuf *conn;
       +   gint i,msgs=0;
       +
       +   conn=&NetBuf->ReadBuf;
       +
       +   if (conn->Data) for (i=0;i<conn->DataPresent;i++) {
       +      if (conn->Data[i]==NetBuf->Terminator) msgs++;
       +   }
       +   return msgs;
       +}
       +
       +gchar *GetWaitingMessage(NetworkBuffer *NetBuf) {
       +/* Reads a complete (terminated) message from the network buffer. The    */
       +/* message is removed from the buffer, and returned as a null-terminated */
       +/* string (the network terminator is removed). If no complete message is */
       +/* waiting, NULL is returned. The string is dynamically allocated, and   */
       +/* so must be g_free'd by the caller.                                    */
       +   ConnBuf *conn;
       +   int MessageLen;
       +   char *SepPt;
       +   gchar *NewMessage;
       +   conn=&NetBuf->ReadBuf;
       +   if (!conn->Data || !conn->DataPresent) return NULL;
       +   SepPt=memchr(conn->Data,NetBuf->Terminator,conn->DataPresent);
       +   if (!SepPt) return NULL;
       +   *SepPt='\0';
       +   MessageLen=SepPt-conn->Data+1;
       +   SepPt--;
       +   if (NetBuf->StripChar && *SepPt==NetBuf->StripChar) *SepPt='\0';
       +   NewMessage=g_new(gchar,MessageLen);
       +   memcpy(NewMessage,conn->Data,MessageLen);
       +   if (MessageLen<conn->DataPresent) {
       +      memmove(&conn->Data[0],&conn->Data[MessageLen],
       +              conn->DataPresent-MessageLen);
       +   }
       +   conn->DataPresent-=MessageLen;
       +   return NewMessage;
       +}
       +
       +gboolean ReadDataFromWire(NetworkBuffer *NetBuf) {
       +/* Reads any waiting data on the given network buffer's TCP/IP connection */
       +/* into the read buffer. Returns FALSE if the connection was closed, or   */
       +/* if the read buffer's maximum size was reached.                         */
       +   ConnBuf *conn;
       +   int CurrentPosition,BytesRead;
       +   conn=&NetBuf->ReadBuf;
       +   CurrentPosition=conn->DataPresent;
       +   while(1) {
       +      if (CurrentPosition>=conn->Length) {
       +         if (conn->Length==MAXREADBUF) {
       +            SetError(&NetBuf->error,ET_CUSTOM,E_FULLBUF);
       +            return FALSE; /* drop connection */
       +         }
       +         if (conn->Length==0) conn->Length=256; else conn->Length*=2;
       +         if (conn->Length>MAXREADBUF) conn->Length=MAXREADBUF;
       +         conn->Data=g_realloc(conn->Data,conn->Length);
       +      }
       +      BytesRead=recv(NetBuf->fd,&conn->Data[CurrentPosition],
       +                     conn->Length-CurrentPosition,0);
       +      if (BytesRead==SOCKET_ERROR) {
       +#ifdef CYGWIN
       +         int Error = WSAGetLastError();
       +         if (Error==WSAEWOULDBLOCK) break;
       +         else { SetError(&NetBuf->error,ET_WINSOCK,Error); return FALSE; }
       +#else
       +         if (errno==EAGAIN) break;
       +         else if (errno!=EINTR) {
       +            SetError(&NetBuf->error,ET_ERRNO,errno);
       +            return FALSE;
       +         }
       +#endif
       +      } else if (BytesRead==0) {
       +         return FALSE;
       +      } else {
       +         CurrentPosition+=BytesRead;
       +         conn->DataPresent=CurrentPosition;
       +      }
       +   }
       +   return TRUE;
       +}
       +
       +void QueueMessageForSend(NetworkBuffer *NetBuf,gchar *data) {
       +/* Writes the null-terminated string "data" to the network buffer, ready   */
       +/* to be sent to the wire when the network connection becomes free. The    */
       +/* message is automatically terminated. Fails to write the message without */
       +/* error if the buffer reaches its maximum size (although this error will  */
       +/* be detected when an attempt is made to write the buffer to the wire).   */
       +   int AddLength,NewLength;
       +   ConnBuf *conn;
       +   conn=&NetBuf->WriteBuf;
       +   AddLength=strlen(data)+1;
       +   NewLength=conn->DataPresent+AddLength;
       +   if (NewLength > conn->Length) {
       +      conn->Length*=2;
       +      conn->Length=MAX(conn->Length,NewLength);
       +      if (conn->Length > MAXWRITEBUF) conn->Length=MAXWRITEBUF;
       +      if (NewLength > conn->Length) return;
       +      conn->Data=g_realloc(conn->Data,conn->Length);
       +   }
       +   memcpy(&conn->Data[conn->DataPresent],data,AddLength);
       +   conn->DataPresent=NewLength;
       +   conn->Data[NewLength-1]=NetBuf->Terminator;
       +
       +/* If the buffer was empty before, we may need to tell the owner to check
       +   the socket for write-ready status */
       +   if (NewLength==AddLength) NetBufCallBack(NetBuf);
       +}
       +
       +gboolean WriteDataToWire(NetworkBuffer *NetBuf) {
       +/* Writes any waiting data in the network buffer to the wire. Returns */
       +/* TRUE on success, or FALSE if the buffer's maximum length is        */
       +/* reached, or the remote end has closed the connection.              */
       +   ConnBuf *conn;
       +   int CurrentPosition,BytesSent;
       +   conn=&NetBuf->WriteBuf;
       +   if (!conn->Data || !conn->DataPresent) return TRUE;
       +   if (conn->Length==MAXWRITEBUF) {
       +      SetError(&NetBuf->error,ET_CUSTOM,E_FULLBUF);
       +      return FALSE;
       +   }
       +   CurrentPosition=0;
       +   while (CurrentPosition<conn->DataPresent) {
       +      BytesSent=send(NetBuf->fd,&conn->Data[CurrentPosition],
       +                     conn->DataPresent-CurrentPosition,0);
       +      if (BytesSent==SOCKET_ERROR) {
       +#ifdef CYGWIN
       +         int Error=WSAGetLastError();
       +         if (Error==WSAEWOULDBLOCK) break;
       +         else { SetError(&NetBuf->error,ET_WINSOCK,Error); return FALSE; }
       +#else
       +         if (errno==EAGAIN) break;
       +         else if (errno!=EINTR) {
       +            SetError(&NetBuf->error,ET_ERRNO,errno);
       +            return FALSE;
       +         }
       +#endif
       +      } else {
       +         CurrentPosition+=BytesSent;
       +      }
       +   }
       +   if (CurrentPosition>0 && CurrentPosition<conn->DataPresent) {
       +      memmove(&conn->Data[0],&conn->Data[CurrentPosition],
       +              conn->DataPresent-CurrentPosition);
       +   }
       +   conn->DataPresent-=CurrentPosition;
       +   return TRUE;
       +}
       +
       +static void SendHttpRequest(HttpConnection *conn) {
       +   GString *text;
       +
       +   conn->Tries++;
       +   conn->StatusCode=0;
       +   conn->Status=HS_CONNECTING;
       +
       +   text=g_string_new("");
       +
       +   if (conn->Redirect) {
       +      g_string_sprintf(text,"%s %s HTTP/1.0",conn->Method,conn->Redirect);
       +      g_free(conn->Redirect); conn->Redirect=NULL;
       +   } else {
       +      g_string_sprintf(text,"%s http://%s:%u%s HTTP/1.0",
       +                       conn->Method,conn->HostName,conn->Port,conn->Query);
       +   }
       +   QueueMessageForSend(&conn->NetBuf,text->str);
       +
       +   if (conn->Headers) QueueMessageForSend(&conn->NetBuf,conn->Headers);
       +
       +   g_string_sprintf(text,"User-Agent: dopewars/%s",VERSION);
       +   QueueMessageForSend(&conn->NetBuf,text->str);
       +
       +/* Insert a blank line between headers and body */
       +   QueueMessageForSend(&conn->NetBuf,"");
       +
       +   if (conn->Body) QueueMessageForSend(&conn->NetBuf,conn->Body);
       +
       +   g_string_free(text,TRUE);
       +}
       +
       +static gboolean StartHttpConnect(HttpConnection *conn) {
       +   gchar *ConnectHost;
       +   unsigned ConnectPort;
       +
       +   if (conn->Proxy) {
       +      ConnectHost=conn->Proxy; ConnectPort=conn->ProxyPort;
       +   } else {
       +      ConnectHost=conn->HostName; ConnectPort=conn->Port;
       +   }
       +      
       +   if (!StartNetworkBufferConnect(&conn->NetBuf,ConnectHost,ConnectPort)) {
       +      return FALSE;
       +   }
       +   return TRUE;
       +}
       +
       +gboolean OpenHttpConnection(HttpConnection **connpt,gchar *HostName,
       +                            unsigned Port,gchar *Proxy,unsigned ProxyPort,
       +                            gchar *Method,gchar *Query,gchar *Headers,
       +                            gchar *Body) {
       +   HttpConnection *conn;
       +   g_assert(HostName && Method && Query && connpt);
       +
       +   conn=g_new0(HttpConnection,1);
       +   InitNetworkBuffer(&conn->NetBuf,'\n','\r');
       +   conn->HostName=g_strdup(HostName);
       +   if (Proxy && Proxy[0]) conn->Proxy=g_strdup(Proxy);
       +   conn->Method=g_strdup(Method);
       +   conn->Query=g_strdup(Query);
       +   if (Headers && Headers[0]) conn->Headers=g_strdup(Headers);
       +   if (Body && Body[0]) conn->Body=g_strdup(Body);
       +   conn->Port = Port;
       +   conn->ProxyPort = ProxyPort;
       +   *connpt = conn;
       +
       +   if (StartHttpConnect(conn)) {
       +     SendHttpRequest(conn);
       +     return TRUE;
       +   } else {
       +     return FALSE;
       +   }
       +}
       +
       +void CloseHttpConnection(HttpConnection *conn) {
       +   ShutdownNetworkBuffer(&conn->NetBuf);
       +   g_free(conn->HostName);
       +   g_free(conn->Proxy);
       +   g_free(conn->Method);
       +   g_free(conn->Query);
       +   g_free(conn->Headers);
       +   g_free(conn->Body);
       +   g_free(conn->Redirect);
       +   g_free(conn);
       +}
       +
       +gboolean IsHttpError(HttpConnection *conn) {
       +   return IsError(&conn->NetBuf.error);
       +}
       +
       +gchar *ReadHttpResponse(HttpConnection *conn) {
       +   gchar *msg,**split;
       +
       +   msg=GetWaitingMessage(&conn->NetBuf);
       +   if (msg) switch(conn->Status) {
       +      case HS_CONNECTING:    /* OK, we should have the HTTP status line */
       +         conn->Status=HS_READHEADERS;
       +         split=g_strsplit(msg," ",2);
       +         if (split[0] && split[1]) {
       +            conn->StatusCode=atoi(split[1]);
       +            g_print("HTTP status code %d returned\n",conn->StatusCode);
       +         } else g_warning("Invalid HTTP status line %s",msg);
       +         g_strfreev(split);
       +         break;
       +      case HS_READHEADERS:
       +         if (msg[0]==0) conn->Status=HS_READSEPARATOR;
       +         else {
       +            split=g_strsplit(msg," ",1);
       +            if (split[0] && split[1]) {
       +               if (conn->StatusCode==302 && strcmp(split[0],"Location:")==0) {
       +                  g_print("Redirect to %s\n",split[1]);
       +                  g_free(conn->Redirect);
       +                  conn->Redirect = g_strdup(split[1]);
       +               }
       +/*             g_print("Header %s (value %s) read\n",split[0],split[1]);*/
       +            }
       +            g_strfreev(split);
       +         }
       +         break;
       +      case HS_READSEPARATOR:
       +         conn->Status=HS_READBODY;
       +         break;
       +      case HS_READBODY:   /* At present, we do nothing special with the body */
       +         break;
       +   }
       +   return msg;
       +}
       +
       +gboolean HandleHttpCompletion(HttpConnection *conn) {
       +   NBCallBack CallBack;
       +   gpointer CallBackData;
       +   if (conn->Redirect) {
       +      g_print("Following redirect\n");
       +      CallBack=conn->NetBuf.CallBack;
       +      CallBackData=conn->NetBuf.CallBackData;
       +      ShutdownNetworkBuffer(&conn->NetBuf);
       +      if (StartHttpConnect(conn)) {
       +         SendHttpRequest(conn);
       +         SetNetworkBufferCallBack(&conn->NetBuf,CallBack,CallBackData);
       +         return FALSE;
       +      }
       +   }
       +   return TRUE;
       +}
       +
       +gboolean StartConnect(int *fd,gchar *RemoteHost,unsigned RemotePort,
       +                      gboolean NonBlocking,LastError *error) {
       +   struct sockaddr_in ClientAddr;
       +   struct hostent *he;
       +
       +   if ((he=gethostbyname(RemoteHost))==NULL) {
       +#ifdef CYGWIN
       +      if (error) SetError(error,ET_WINSOCK,WSAGetLastError());
       +#else
       +      if (error) SetError(error,ET_HERRNO,h_errno);
       +#endif
       +      return FALSE;
       +   }
       +   *fd=socket(AF_INET,SOCK_STREAM,0);
       +   if (*fd==SOCKET_ERROR) {
       +#ifdef CYGWIN
       +      if (error) SetError(error,ET_WINSOCK,WSAGetLastError());
       +#else
       +      if (error) SetError(error,ET_ERRNO,errno);
       +#endif
       +      return FALSE;
       +   }
       +
       +   ClientAddr.sin_family=AF_INET;
       +   ClientAddr.sin_port=htons(RemotePort);
       +   ClientAddr.sin_addr=*((struct in_addr *)he->h_addr);
       +   memset(ClientAddr.sin_zero,0,sizeof(ClientAddr.sin_zero));
       +
       +   SetBlocking(*fd,!NonBlocking);
       +
       +   if (connect(*fd,(struct sockaddr *)&ClientAddr,
       +       sizeof(struct sockaddr))==SOCKET_ERROR) {
       +#ifdef CYGWIN
       +      int errcode=WSAGetLastError();
       +      if (errcode==WSAEWOULDBLOCK) return TRUE;
       +      else if (error) SetError(error,ET_WINSOCK,errcode);
       +#else
       +      if (errno==EINPROGRESS) return TRUE;
       +      else if (error) SetError(error,ET_ERRNO,errno);
       +#endif
       +      CloseSocket(*fd); *fd=-1;
       +      return FALSE;
       +   } else {
       +      SetBlocking(*fd,FALSE); /* All connected sockets should be nonblocking */
       +   }
       +   return TRUE;
       +}
       +
       +gboolean FinishConnect(int fd,LastError *error) {
       +   int errcode;
       +#ifdef CYGWIN
       +   errcode = WSAGetLastError();
       +   if (errcode==0) return TRUE;
       +   else {
       +     if (error) { SetError(error,ET_WINSOCK,errcode); }
       +     return FALSE;
       +   }
       +#else
       +#ifdef HAVE_SOCKLEN_T
       +   socklen_t optlen;
       +#else
       +   int optlen;
       +#endif
       +
       +   optlen=sizeof(errcode);
       +   if (getsockopt(fd,SOL_SOCKET,SO_ERROR,&errcode,&optlen)==-1) {
       +      errcode = errno;
       +   }
       +   if (errcode==0) return TRUE;
       +   else {
       +     if (error) { SetError(error,ET_ERRNO,errcode); }
       +     return FALSE;
       +   }
       +#endif /* CYGWIN */
       +}
       +
       +#endif /* NETWORKING */
 (DIR) diff --git a/src/network.h b/src/network.h
       t@@ -0,0 +1,152 @@
       +/* network.h      Header file for low-level networking routines         */
       +/* Copyright (C)  1998-2001  Ben Webb                                   */
       +/*                Email: ben@bellatrix.pcl.ox.ac.uk                     */
       +/*                WWW: http://bellatrix.pcl.ox.ac.uk/~ben/dopewars/     */
       +
       +/* This program 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       */
       +/* of the License, or (at your option) any later version.               */
       +
       +/* This program 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 this program; if not, write to the Free Software          */
       +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston,               */
       +/*                   MA  02111-1307, USA.                               */
       +
       +
       +#ifndef __NETWORK_H__
       +#define __NETWORK_H__
       +
       +#ifdef HAVE_CONFIG_H
       +#include <config.h>
       +#endif
       +
       +/* Various includes necessary for select() calls */
       +#include <sys/types.h>
       +/* Be careful not to include both sys/time.h and time.h on those systems */
       +/* which don't like it */
       +#if TIME_WITH_SYS_TIME
       +#include <sys/time.h>
       +#include <time.h>
       +#else
       +#if HAVE_SYS_TIME_H
       +#include <sys/time.h>
       +#else
       +#include <time.h>
       +#endif
       +#endif
       +
       +#ifdef HAVE_UNISTD_H
       +#include <unistd.h>
       +#endif
       +
       +#include <glib.h>
       +
       +#include "error.h"
       +
       +#ifdef NETWORKING
       +
       +#ifndef SOCKET_ERROR
       +#define SOCKET_ERROR -1
       +#endif
       +
       +typedef struct _ConnBuf {
       +   gchar *Data;      /* bytes waiting to be read/written      */
       +   gint Length;      /* allocated length of the "Data" buffer */
       +   gint DataPresent; /* number of bytes currently in "Data"   */
       +} ConnBuf;
       +
       +typedef struct _NetworkBuffer NetworkBuffer;
       +
       +typedef void (*NBCallBack)(NetworkBuffer *NetBuf,gboolean Read,gboolean Write);
       +
       +/* Handles reading and writing messages from/to a network connection */
       +struct _NetworkBuffer {
       +   int fd;                /* File descriptor of the socket */
       +   gint InputTag;         /* Identifier for gdk_input routines */
       +   NBCallBack CallBack;   /* Function called when the socket read- or
       +                             write-able status changes */
       +   gpointer CallBackData; /* Data accessible to the callback function */
       +   char Terminator;       /* Character that separates messages */
       +   char StripChar;        /* Character that should be removed from messages */
       +   ConnBuf ReadBuf;       /* New data, waiting for the application */
       +   ConnBuf WriteBuf;      /* Data waiting to be written to the wire */
       +   gboolean WaitConnect;  /* TRUE if a non-blocking connect is in progress */
       +   LastError error;       /* Any error from the last operation */
       +};
       +
       +/* Keeps track of the progress of an HTTP connection */
       +typedef enum {
       +   HS_CONNECTING, HS_READHEADERS, HS_READSEPARATOR, HS_READBODY
       +} HttpStatus;
       +
       +/* A structure used to keep track of an HTTP connection */
       +typedef struct _HttpConnection {
       +   gchar *HostName;       /* The machine on which the desired page resides */
       +   unsigned Port;         /* The port */
       +   gchar *Proxy;          /* If non-NULL, a web proxy to use */
       +   unsigned ProxyPort;    /* The port to use for talking to the proxy */
       +   gchar *Method;         /* e.g. GET, POST */
       +   gchar *Query;          /* e.g. the path of the desired webpage */
       +   gchar *Headers;        /* if non-NULL, e.g. Content-Type */
       +   gchar *Body;           /* if non-NULL, data to send */
       +   gchar *Redirect;       /* if non-NULL, a URL to redirect to */
       +   NetworkBuffer NetBuf;  /* The actual network connection itself */
       +   gint Tries;            /* Number of requests actually sent so far */
       +   gint StatusCode;       /* 0=no status yet, otherwise an HTTP status code */
       +   HttpStatus Status;
       +} HttpConnection;
       +
       +void InitNetworkBuffer(NetworkBuffer *NetBuf,char Terminator,char StripChar);
       +void SetNetworkBufferCallBack(NetworkBuffer *NetBuf,NBCallBack CallBack,
       +                              gpointer CallBackData);
       +gboolean IsNetworkBufferActive(NetworkBuffer *NetBuf);
       +void BindNetworkBufferToSocket(NetworkBuffer *NetBuf,int fd);
       +gboolean StartNetworkBufferConnect(NetworkBuffer *NetBuf,gchar *RemoteHost,
       +                                   unsigned RemotePort);
       +void ShutdownNetworkBuffer(NetworkBuffer *NetBuf);
       +void SetSelectForNetworkBuffer(NetworkBuffer *NetBuf,fd_set *readfds,
       +                               fd_set *writefds,fd_set *errorfds,int *MaxSock);
       +gboolean RespondToSelect(NetworkBuffer *NetBuf,fd_set *readfds,
       +                         fd_set *writefds,fd_set *errorfds,
       +                         gboolean *DoneOK);
       +gboolean NetBufHandleNetwork(NetworkBuffer *NetBuf,gboolean ReadReady,
       +                             gboolean WriteReady,gboolean *DoneOK);
       +gboolean ReadDataFromWire(NetworkBuffer *NetBuf);
       +gboolean WriteDataToWire(NetworkBuffer *NetBuf);
       +void QueueMessageForSend(NetworkBuffer *NetBuf,gchar *data);
       +gint CountWaitingMessages(NetworkBuffer *NetBuf);
       +gchar *GetWaitingMessage(NetworkBuffer *NetBuf);
       +
       +gboolean OpenHttpConnection(HttpConnection **conn,gchar *HostName,
       +                            unsigned Port,gchar *Proxy,unsigned ProxyPort,
       +                            gchar *Method,gchar *Query,
       +                            gchar *Headers,gchar *Body);
       +void CloseHttpConnection(HttpConnection *conn);
       +gboolean IsHttpError(HttpConnection *conn);
       +gchar *ReadHttpResponse(HttpConnection *conn);
       +gboolean HandleHttpCompletion(HttpConnection *conn);
       +
       +gboolean StartConnect(int *fd,gchar *RemoteHost,unsigned RemotePort,
       +                      gboolean NonBlocking,LastError *error);
       +void StartNetworking(void);
       +void StopNetworking(void);
       +
       +#ifdef CYGWIN
       +#define CloseSocket(sock) closesocket(sock)
       +void SetReuse(SOCKET sock);
       +void SetBlocking(SOCKET sock,gboolean blocking);
       +#else
       +#define CloseSocket(sock) close(sock)
       +void SetReuse(int sock);
       +void SetBlocking(int sock,gboolean blocking);
       +#endif
       +
       +#endif /* NETWORKING */
       +
       +#endif /* __NETWORK_H__ */
 (DIR) diff --git a/win32/filelist b/win32/filelist
       t@@ -40,7 +40,9 @@ dopewars.exe
        dopewars server.lnk
        dopewars.exe
        -s
       -
       +dopewars command line options.lnk
       +dopewars.exe
       +-h
        dopewars help.lnk
        index.html