/* c4com.c   (c)Copyright Sequiter Software Inc., 1988-1996.  All rights reserved. */

/* CONNECTION4::connectionFailure indicates an irrecoverable connection error.
   In the case of only a CODE4::errorCode condition, communications will still
   operate normally */

#include "d4all.h"
#ifndef S4UNIX
   #ifdef __TURBOC__
      #pragma hdrstop
   #endif  /* __TUROBC__ */
#endif  /* S4UNIX */

/* cannot be inline due to DLL CODE4 structure potential mismatches */
/* available whether or not communications are available */
long S4FUNCTION code4timeout( CODE4 *c4 )
{
   #ifdef E4PARM_LOW
      if ( c4 == 0 )
         return error4( 0, e4parm_null, E91006 ) ;
   #endif
   return c4->timeout ;
}

/* available whether or not communications are available */
void S4FUNCTION code4timeoutSet( CODE4 *c4, long val )
{
   #ifdef E4PARM_LOW
      if ( c4 == 0 )
      {
         error4( 0, e4parm_null, E91007 ) ;
         return ;
      }
   #endif
   c4->timeout = val ;
}

#ifndef S4OFF_COMMUNICATIONS

/*#include <conio.h>*/
#ifndef S4SERVER
   #ifndef S4UNIX
      #include <sys\timeb.h>
   #else
      #ifndef S4NO_FTIME
         #include <sys/timeb.h>
      #else
         #include <sys/time.h>
      #endif
   #endif
#endif

#ifdef S4COM_PRINT
static void s4connectionPrint( const int ) ;
#endif

#ifndef E4OFF_STRING
   extern long error4seek( long ) ;
#endif

#ifdef E4PARM_LOW
static long packet4len( const PACKET4 *packet )
{
   if ( packet == 0 )
      return error4( 0, e4parm_null, E90159 ) ;

   return packet->dataLen ;
}

static int packet4setLen( PACKET4 *packet, const long int dataLen )
{
   if ( packet == 0 )
      return error4( 0, e4parm_null, E90159 ) ;

   packet->dataLen = dataLen ;

   return 0 ;
}

static void packet4setStatus( PACKET4 *packet, int status )
{
   if ( packet == 0 )
   {
      error4( 0, e4parm_null, E90159 ) ;
      return ;
   }

   packet->status = status ;
}

static void packet4setType( PACKET4 *packet, const int type )
{
   if ( packet == 0 )
   {
      error4( 0, e4parm_null, E90159 ) ;
      return ;
   }

   packet->type = type ;
}

static int packet4status( const PACKET4 *packet )
{
   if ( packet == 0 )
      return error4( 0, e4parm_null, E90159 ) ;

   return packet->status ;
}

static long packet4errCode2( const PACKET4 *packet )
{
   if ( packet == 0 )
      return error4( 0, e4parm_null, E90159 ) ;

   return packet->errCode2 ;
}

static int packet4setErrCode2( PACKET4 *packet, const long code2 )
{
   if ( packet == 0 )
      return error4( 0, e4parm_null, E90159 ) ;

   packet->errCode2 = code2 ;
   return 0 ;
}

static int packet4type( const PACKET4 *packet )
{
   if ( packet == 0 )
      return error4( 0, e4parm_null, E90159 ) ;

   return packet->type ;
}

static long packet4serverId( const PACKET4 *packet )
{
   if ( packet == 0 )
   {
      error4( 0, e4parm_null, E90159 ) ;
      return 0 ;
   }
   return packet->serverDataId ;
}

static long packet4clientId( const PACKET4 *packet )
{
   if ( packet == 0 )
   {
      error4( 0, e4parm_null, E90159 ) ;
      return 0 ;
   }

   return packet->clientDataId ;
}

static int packet4didUnlock( const PACKET4 *packet )
{
   if ( packet == 0 )
      return error4( 0, e4parm_null, E90159 ) ;

   return packet->didUnlock ;
}

static int packet4readLock( const PACKET4 *packet )
{
   if ( packet == 0 )
      return error4( 0, e4parm_null, E90159 ) ;

   return packet->readLock ;
}

static int packet4unlockAuto( const PACKET4 *packet )
{
   if ( packet == 0 )
      return error4( 0, e4parm_null, E90159 ) ;

   return packet->unlockAuto ;
}

static void packet4setClientId( PACKET4 *packet, const long id )
{
   if ( packet == 0 )
   {
      error4( 0, e4parm_null, E90159 ) ;
      return ;
   }

   packet->clientDataId = id ;
}

static void packet4setServerId( PACKET4 *packet, const long id )
{
   if ( packet == 0 )
   {
      error4( 0, e4parm_null, E90159 ) ;
      return ;
   }

   packet->serverDataId = id ;
}

static void packet4setReadLock( PACKET4 *packet, const int readLock )
{
   if ( packet == 0 )
   {
      error4( 0, e4parm_null, E90159 ) ;
      return ;
   }

   packet->readLock = readLock ;
}

static void packet4setRequestLockedInfo( PACKET4 *packet, const int val )
{
   if ( packet == 0 )
   {
      error4( 0, e4parm_null, E90159 ) ;
      return ;
   }

   packet->requestLockedInfo = val ;
}

int packet4requestLockedInfo( const PACKET4 *packet )
{
   if ( packet == 0 )
      return error4( 0, e4parm_null, E90159 ) ;

   return packet->requestLockedInfo ;
}

void packet4setDidUnlock( PACKET4 *packet, const int didUnlock )
{
   if ( packet == 0 )
   {
      error4( 0, e4parm_null, E90159 ) ;
      return ;
   }

   packet->didUnlock = didUnlock ;
}

static void packet4setUnlockAuto( PACKET4 *packet, const int unlockAuto )
{
   if ( packet == 0 )
   {
      error4( 0, e4parm_null, E90159 ) ;
      return ;
   }

   packet->unlockAuto = unlockAuto ;
}

static int packet4partNo( const PACKET4 *packet )
{
   if ( packet == 0 )
      return error4( 0, e4parm_null, E90159 ) ;

   return packet->partNo ;
}

static int packet4numParts( const PACKET4 *packet )
{
   if ( packet == 0 )
      return error4( 0, e4parm_null, E90159 ) ;

   return packet->numParts ;
}

long connection4clientId( const CONNECTION4 *connection )
{
   #ifdef E4PARM_LOW
      if ( connection == 0 )
      {
         error4( 0, e4parm_null, E90160 ) ;
         return 0 ;
      }
   #endif

   if ( connection->message == 0 )
      return 0 ;

   return packet4clientId( connection->message->packet ) ;
}

S4CONST char * connection4data( const CONNECTION4 *connection )
{
   return connection4dataOffset( connection, 0 ) ;
}
#endif

/* if a single linked message only (case of a receive), then can return parts */
#ifdef S4UTILS
   S4CONST char * S4FUNCTION connection4dataOffset( const CONNECTION4 *connection, const unsigned int offset )
#else
   S4CONST char *connection4dataOffset( const CONNECTION4 *connection, const unsigned int offset )
#endif
{
   MESSAGE4DATA *data ;

   #ifdef E4PARM_LOW
      if ( connection == 0 )
      {
         error4( 0, e4parm_null, E90160 ) ;
         return 0 ;
      }
   #endif

   if ( connection->messageData.nLink != 1 )
   {
      #ifdef E4ANALYZE
         error4( connection->cb, e4info, E90160 ) ;
      #endif
      return 0 ;
   }
   data = (MESSAGE4DATA *)l4first( &connection->messageData ) ;
   if ( data == 0 )
      return 0 ;
   return data->data + offset + connection->adjustForLocked ;
}

int connection4disconnect( CONNECTION4 *connection )
{
   int rc ;

   #ifdef E4PARM_LOW
      if ( connection == 0 )
         return error4( 0, e4parm_null, E90160 ) ;
   #endif

   if ( connection->connected != 0 )
   {
      #ifdef E4ANALYZE
         if ( connection->net == 0 )
            return error4( connection->cb, e4struct, E90160 ) ;
      #endif
      rc = connection4netDisconnect( connection->net ) ;
      if ( rc < 0 )
         return error4stack( connection->cb, (short)rc, E90160 ) ;
      connection->connected = 0 ;
      rc = connection4netFree( connection->net ) ;
      connection->net = 0 ;
      #ifdef S4SERVER
         l4remove( &connection->socket->connections, connection ) ;
      #endif
      return rc ;
   }
   else
      return 0 ;
}

int connection4init( CONNECTION4 *connection, SOCKET4 *socket )
{
   #ifdef E4PARM_LOW
      if ( socket == 0 || connection == 0 )
         return error4( 0, e4parm_null, E90160 ) ;
   #endif

   memset( connection, 0, sizeof( CONNECTION4 ) ) ;
   connection->cb = socket->cb ;
   connection->socket = socket ;

   return 0 ;
}

int connection4initUndo( CONNECTION4 *connection )
{
   int rc ;

   #ifdef E4PARM_LOW
      if ( connection == 0 )
         return error4( 0, e4parm_null, E90160 ) ;
   #endif

   #ifdef E4ANALYZE
      connection->initUndone = 1 ;
   #endif

   rc = connection4disconnect( connection ) ;

   connection4clear( connection ) ;

   if ( connection->net != 0 )
   {
      connection4netInitUndo( connection->net ) ;
      connection4netFree( connection->net ) ;
      connection->net = 0 ;
   }

   if ( connection->message != 0 )
   {
      message4free( connection->cb, connection->message ) ;
      connection->message = 0 ;
   }

   return rc ;
}

#ifdef E4PARM_LOW
int connection4free( CONNECTION4 *connection )
{
   if ( connection == 0 )
      return 0 ;

   #ifdef E4ANALYZE
      if ( connection->initUndone != 1 )
         return error4( connection->cb, e4info, E90160 ) ;
   #endif

   mem4free( connection->cb->connectionMemory, connection ) ;
   return 0 ;
}

/* if connection is null, it means dealing with a system file, so return 0L */
long connection4id( const CONNECTION4 *connection )
{
   if ( connection == 0 )
      return 0L ;
   return connection4netId( connection->net ) ;
}

int connection4didUnlock( const CONNECTION4 *connection )
{
   if ( connection == 0 )
      return error4( 0, e4parm_null, E90160 ) ;
   if ( connection->message == 0 )
      return error4( 0, e4struct, E90160 ) ;

   return packet4didUnlock( connection->message->packet ) ;
}

int connection4readLock( const CONNECTION4 *connection )
{
   if ( connection == 0 )
      return error4( 0, e4parm_null, E90160 ) ;
   if ( connection->message == 0 )
      return error4( 0, e4struct, E90160 ) ;

   return packet4readLock( connection->message->packet ) ;
}

int connection4unlockAuto( const CONNECTION4 *connection )
{
   if ( connection == 0 )
      return error4( 0, e4parm_null, E90160 ) ;
   if ( connection->message == 0 )
      return error4( 0, e4struct, E90160 ) ;

   return packet4unlockAuto( connection->message->packet ) ;
}
#endif /* E4PARM_LOW -- i.e. inline */

static int connection4messageAlloc( CONNECTION4 *connection )
{
   if ( connection->message == 0 )
   {
      connection->message = message4alloc( connection->cb, (unsigned int)connection4netMessageLen( connection->net ) ) ;
      if ( connection->message == 0 )
         return error4( connection->cb, e4memory, E90160 ) ;
   }
   return 0 ;
}

static MESSAGE4DATA *connection4allocateData( CONNECTION4 *connection, const unsigned allocLen, const int doAlloc, const int insert )
{
   CODE4 *c4 ;
   int rc ;
   MESSAGE4DATA *messageLink ;

   c4 = connection->cb ;

   rc = connection4messageAlloc( connection ) ;
   if ( rc != 0 )
   {
      #ifdef E4STACK
         error4stack( c4, (short)rc, E90160 ) ;
      #endif
      return 0 ;
   }

   if ( c4->messageLinkMemory == 0 )
   {
      c4->messageLinkMemory = mem4create( c4, 20, sizeof( MESSAGE4DATA ), 10, 0 ) ;
      if ( c4->messageLinkMemory == 0 )
      {
         #ifdef E4STACK
            error4stack( c4, e4memory, E90160 ) ;
         #endif
         return 0 ;
      }
   }

   messageLink = 0 ;

   if ( doAlloc == 1 )  /* check with local list first */
      if ( allocLen <= COM4DEFAULT_MESSAGE_LEN )   /* if <= 512, try to get from local list to avoid allocate/deallocate sequence */
         messageLink = (MESSAGE4DATA *)l4pop( &c4->availDataMessages ) ;

   if ( messageLink == 0 )
   {
      messageLink = (MESSAGE4DATA *)mem4alloc( c4->messageLinkMemory ) ;
      if ( messageLink == 0 )
      {
         #ifdef E4STACK
            error4stack( c4, e4memory, E90160 ) ;
         #endif
         return 0 ;
      }
   }

   if ( doAlloc == 1 )
   {
      if ( messageLink->allocLen == 0 )
      {
         if ( allocLen <= COM4DEFAULT_MESSAGE_LEN )
         {
            messageLink->allocatedData = (char *)u4allocEr( c4, (long)COM4DEFAULT_MESSAGE_LEN ) ;
            if ( messageLink->allocatedData != 0 )
               messageLink->allocLen = COM4DEFAULT_MESSAGE_LEN ;
         }
         else
         {
            messageLink->allocatedData = (char *)u4allocEr( c4, (long)allocLen ) ;
         }
         if ( messageLink->allocatedData == 0 )
         {
            #ifdef E4STACK
               error4stack( c4, e4memory, E90160 ) ;
            #endif
            return 0 ;
         }
         messageLink->data = messageLink->allocatedData ;
         messageLink->didAlloc = 1 ;
      }
   }
   else
      messageLink->didAlloc = 0 ;

   messageLink->len = allocLen ;
   if ( insert == 1 )
      l4addBefore( &connection->messageData, l4first( &connection->messageData ), messageLink ) ;
   else
      l4add( &connection->messageData, messageLink ) ;
   connection->totalDataLen += allocLen ;

   return messageLink ;
}

/* a null data paramater means that the space should be allocated but none inserted */
/* a return code of '1' means that the data was repositioned */
int connection4insertData( CONNECTION4 *connection, const void *data, const unsigned dataLen, const int doAlloc, const int insert )
{
   MESSAGE4DATA *messageLink ;

   #ifdef E4PARM_LOW
      if ( connection == 0 )
         return error4( 0, e4parm_null, E90160 ) ;
   #endif

   if ( connection->connectionFailure < 0 )
      return connection->connectionFailure ;

   if ( dataLen == 0 )
      return 0 ;

   messageLink = connection4allocateData( connection, dataLen, doAlloc, insert ) ;
   if ( messageLink == 0 )
      return error4stack( connection->c4, e4memory, E90160 ) ;

   if ( doAlloc == 1 )
      memcpy( messageLink->allocatedData, data, dataLen ) ;
   else
      messageLink->data = (char *)data ;

   return 0 ;
}

CONNECTION4 *connection4alloc( SOCKET4 *socket )
{
   int rc ;
   CONNECTION4 *connection ;
   CODE4 *c4 ;

   #ifdef E4PARM_LOW
      if ( socket == 0 )
      {
         error4( 0, e4parm_null, E90160 ) ;
         return 0 ;
      }
   #endif

   c4 = socket->cb ;

   if ( c4->connectionMemory == 0 )
   {
      #ifdef S4SERVER
         c4->connectionMemory = mem4create( c4, 10, sizeof( CONNECTION4 ), 5, 0 ) ;
      #else
         c4->connectionMemory = mem4create( c4, 2, sizeof( CONNECTION4 ), 1, 0 ) ;
      #endif
      if ( c4->connectionMemory == 0 )
         return 0 ;
   }
   connection = (CONNECTION4 *)mem4alloc( c4->connectionMemory ) ;
   if ( connection == 0 )
   {
      #ifdef E4STACK
         error4stack( c4, e4memory, E90160 ) ;
      #endif
      return 0 ;
   }

   connection->cb = c4 ;
   rc = connection4init( connection, socket ) ;
   if ( rc < 0 )
   {
      #ifdef E4STACK
         error4stack( c4, (short)rc, E90160 ) ;
      #endif
      return 0 ;
   }

   return connection ;
}

/* sets up basic required info, and clears the data connectionmunication set */
#ifdef S4UTILS
   int S4FUNCTION connection4assign( CONNECTION4 *connection, const int type, const long clientId, const long serverId )
#else
   int connection4assign( CONNECTION4 *connection, const int type, const long clientId, const long serverId )
#endif
{
   int rc ;

   #ifdef E4PARM_LOW
      if ( connection == 0 )
         return error4( 0, e4parm_null, E90160 ) ;
   #endif

   if ( connection->connectionFailure < 0 )
      return connection->connectionFailure ;

   if ( connection->net != 0 )  /* the default server will have a null net */
   {
      rc = connection4messageAlloc( connection ) ;
      if ( rc < 0 )
      {
         connection->connectionFailure = rc ;
         return error4stack( connection->cb, (short)rc, E90160 ) ;
      }
   }

   #ifdef E4ANALYZE
      if ( connection->message == 0 )
      {
         connection->connectionFailure = e4struct ;
         return error4( connection->cb, e4struct, E90160 ) ;
      }
   #endif

   #ifndef S4SERVER
      connection->cb->lockedLockItem = -2L ;
      connection->cb->lockedFileName = 0 ;
      connection->cb->lockedUserId = 0 ;
      connection->cb->lockedNetId = 0 ;
      connection->adjustForLocked = 0 ;

      if ( connection->message != 0 )
         connection4setRequestLockedInfo( connection, 0 ) ;
   #endif

   packet4setType( connection->message->packet, type ) ;
   packet4setStatus( connection->message->packet, 0 ) ;
   packet4setClientId( connection->message->packet, clientId ) ;
   packet4setServerId( connection->message->packet, serverId ) ;
   #ifndef S4SERVER
      packet4setReadLock( connection->message->packet, connection->cb->readLock ) ;
      packet4setUnlockAuto( connection->message->packet, code4unlockAuto( connection->cb ) ) ;
   #endif

   return connection4clear( connection ) ;
}

int connection4clear( CONNECTION4 *connection )
{
   MESSAGE4DATA *link ;

   #ifdef E4PARM_LOW
      if ( connection == 0 )
         return error4( 0, e4parm_null, E90160 ) ;
      if ( connection->cb == 0 )
      {
         #ifdef S4SERVER
            if ( connection->messageData.nLink != 0 )
         #endif
         return error4( 0, e4parm, E90160 ) ;
      }
   #endif

   /* WARNING:  do not modify this function without making appropriate changes
      to connection4repeat() which emulates portions of this function */
   connection->totalDataLen = 0L ;

   for ( ;; )
   {
      link = (MESSAGE4DATA *)l4first( &connection->messageData ) ;
      if ( link == 0 )
         break ;
      l4remove( &connection->messageData, link ) ;
      if ( link->didAlloc == 1 )
      {
         if ( link->allocLen == COM4DEFAULT_MESSAGE_LEN )
            l4add( &connection->cb->availDataMessages, link ) ;
         else
         {
            u4free( link->allocatedData ) ;
            link->didAlloc = 0 ;
            mem4free( connection->cb->messageLinkMemory, link ) ;
         }
      }
      else
         mem4free( connection->cb->messageLinkMemory, link ) ;
   }

   return 0 ;
}

#define packet4connectionId( p ) ( (p)->connectionId )

static MESSAGE4 *connection4returnMessage( CONNECTION4 *connection, const CONNECTION4ID id, const int partNo )
{
   MESSAGE4 *message ;

   for ( message = 0 ;; )
   {
      message = (MESSAGE4 *)l4next( &connection->receivedMessages, message ) ;
      if ( message == 0 )
         return 0 ;
      if ( packet4connectionId( message->packet ) == id && packet4partNo( message->packet ) == partNo )
      {
         l4remove( &connection->receivedMessages, message ) ;
         return message ;
      }
   }
}

#ifndef S4SERVER
static int code4unlockSet( CODE4 *c4 )
{
   DATA4FILE *data ;
   LOCK4LINK *lock ;

   #ifdef E4PARM_LOW
      if ( c4 == 0 )
         return error4( 0, e4parm_null, E90155 ) ;
   #endif

   for( data = 0 ;; )
   {
      data = (DATA4FILE *)l4next( &c4->dataFileList, data ) ;
      if ( data == 0 )
         return 0 ;
      data->numRecs = -1 ;
      #ifdef S4CLIENT
         data->appendLock = 0 ;
         data->fileLock = 0 ;
      #endif
      for( ;; )
      {
         lock = (LOCK4LINK *)l4first( &data->lockedRecords ) ;
         if ( lock == 0 )
            break ;
         l4remove( &data->lockedRecords, lock ) ;
         mem4free( c4->lockLinkMemory, lock ) ;
      }
   }
}
#endif

#ifndef S4SERVER
#ifdef S4UTILS
   int S4FUNCTION connection4receive( CONNECTION4 *connection )
#else
   int connection4receive( CONNECTION4 *connection )
#endif
{
   int rc, len, totLen, conLen; /*, hundredths ;*/
   long totalHundredths, countHundredths ;
   const char *ptr ;
   CODE4 *c4 ;
   CONNECTION4 *messageConnection ;
   #ifdef S4NO_FTIME
      struct timeval oldTime, newTime ;
   #else
      struct timeb oldTime, newTime ;
   #endif
   #ifdef S4TIMEOUT_HOOK
      int count, elapsedHundredths ;
      struct timeb origTime ;
   #endif

   #ifdef E4PARM_LOW
      if ( connection == 0 )
         return error4( 0, e4parm_null, E90160 ) ;
   #endif

   c4 = connection->cb ;

   #ifdef E4ANALYZE
      if ( c4 == 0 )
         return error4( 0, e4struct, E90160 ) ;
   #endif

   if ( connection->connectionFailure < 0 )
      return connection->connectionFailure ;

   if ( connection->message != 0 )
   {
      connection4clear( connection ) ;
      message4free( c4, connection->message ) ;
      connection->message = 0 ;
   }

   connection->totalDataLen = 0 ;
   #ifndef S4NO_FTIME
      ftime( &oldTime) ;
   #else
      gettimeofday(&oldTime, NULL ) ;
   #endif
   totalHundredths = code4timeout( c4 ) ;
   if ( totalHundredths > 0 )
      totalHundredths *= 100 ;
   #ifdef S4TIMEOUT_HOOK
      ftime( &origTime) ;
      for( count = 0 ;; )
   #else
      for( ;; )
   #endif
   {
      messageConnection = socket4receiveMessage( connection->socket ) ;
      if ( messageConnection != 0 )
      {
         if ( connection4didUnlock( connection ) == 1 )
            rc = code4unlockSet( c4 ) ;
         else
         {
            rc = 0 ;
            if ( connection4type( connection ) == CON4DISCONNECT )
            {
               connection->connectionFailure = e4connection ;
               return error4( c4, e4connection, E80220 ) ;
            }
         }

         connection->adjustForLocked = 0 ;
         if ( connection4requestLockedInfo( connection ) == 1 )
            if ( connection4status( connection ) == r4locked )
            {
               conLen = (int)connection4len( connection ) ;
               totLen = sizeof( c4->lockedLockItem ) ;
               if ( conLen < totLen + 3 )
                  return error4( c4, e4packetLen, E90160 ) ;
               ptr = connection4data( connection ) ;
               c4->lockedLockItem = *((long *)ptr ) ;
               ptr += sizeof( c4->lockedLockItem ) ;
               len = strlen( ptr ) ;
               totLen += len + 1 ;
               if ( conLen < totLen )
                  return error4( c4, e4packetLen, E90160 ) ;
               c4->lockedFileName = ptr ;
               ptr += (len + 1) ;
               len = strlen( ptr ) ;
               totLen += len + 1 ;
               if ( conLen < totLen )
                  return error4( c4, e4packetLen, E90160 ) ;
               c4->lockedUserId = ptr ;
               ptr += (len + 1) ;
               len = strlen( ptr ) ;
               totLen += len + 1 ;
               if ( conLen < totLen )
                  return error4( c4, e4packetLen, E90160 ) ;
               c4->lockedNetId = ptr ;
               connection->adjustForLocked = totLen ;
            }

         return rc ;
      }

      if ( error4code( c4 ) < 0 )
         return error4stack( c4, error4code( c4 ), E90160 ) ;

      if ( totalHundredths != -1 )
      {
      #ifndef S4NO_FTIME
         ftime( &newTime ) ;
         countHundredths = ((newTime.time - oldTime.time)*100 ) +  (( (int)newTime.millitm - (int)oldTime.millitm ) / 10 ) ;
      #else
         gettimeofday(&newTime, NULL ) ;
         countHundredths = ((newTime.tv_sec-oldTime.tv_sec)*100) + (( (int)newTime.tv_usec - (int)oldTime.tv_usec ) / 10000 ) ;
      #endif
         if ( countHundredths > totalHundredths )
         {
            #ifdef S4TIMEOUT_HOOK
               elapsedHundredths = (int)( ((newTime.time - origTime.time)*100 ) +  (( (int)newTime.millitm - (int)origTime.millitm ) / 10 ) ) ;
               rc = code4timeoutHook( c4, count, elapsedHundredths ) ;
               if ( rc != 0 )
                  return rc ;
               count++ ;
               ftime( &oldTime ) ;
            #else
               return error4( c4, e4timeOut, E81006 ) ;
            #endif
         }
      }

      connection4yield( connection ) ;
   }
}
#endif /* S4SERVER */

CONNECTION4 * socket4receiveMessage( SOCKET4 *socket )
{
   MESSAGE4 *message, *partialMessage, *returnMessage ;
   MESSAGE4DATA *messageData = 0 ;
   CONNECTION4ID connectionId ;
   int partNo ;
   int offset = 0 ;
   unsigned int numParts, i ;
   CONNECTION4 *connection ;
   CODE4 *c4 ;
   #ifdef S4SERVER
      SERVER4CLIENT *client ;
   #endif

   #ifdef E4PARM_LOW
      if ( socket == 0 )
      {
         error4( 0, e4parm_null, E90161 ) ;
         return 0 ;
      }
   #endif

   #ifdef E4ANALYZE
      if ( socket->cb == 0 )
      {
         error4( 0, e4struct, E90161 ) ;
         return 0 ;
      }
   #endif

   c4 = socket->cb ;

   #ifndef S4SERVER
      connection = socket->connect ;
   #endif

   for( ;; )
   {
      message = socket4receive( socket ) ;
      if ( message == 0 )
         return 0 ;

      #ifdef S4SERVER
         client = server4client( c4->server, message->packet->connectionId ) ;
         if ( client == 0 )   /* could be a crashed client, so just delete message */
         {
            message4free( c4, message ) ;
            return 0 ;
         }

         connection = client->connection ;
      #endif

      if ( message != 0 )  /* is this the complete message? */
      {
         /* if a disconnect message received, any pending messages for the client are deleted
            and the disconnect message is forwarded */

         if ( packet4type( message->packet ) == CON4DISCONNECT )
         {
            for ( ;; )
            {
               partialMessage = (MESSAGE4 *)l4first( &connection->receivedMessages ) ;
               if ( partialMessage == 0 )
                  break ;
               l4remove( &connection->receivedMessages, partialMessage ) ;
               message4free( c4, partialMessage ) ;
            }
            if ( connection->message != 0 )
            {
               message4free( c4, connection->message ) ;
               connection->message = 0 ;
            }
            #ifdef E4ANALYZE
               if ( packet4numParts( message->packet ) != 1 )   /* disconnect cannot span parts */
               {
                  error4( c4, e4info, E90160 ) ;
                  return 0 ;
               }
            #endif
         }

         #ifdef E4ANALYZE
            if ( connection->message != 0 )
            {
               error4( c4, e4info, E90160 ) ;
               return 0 ;
            }
         #endif

         #ifdef S4DISTRIBUTED
            if ( packet4connectionId( message->packet ) != connection4netSourceId( connection->net ) )
            {
               /* message from another server, maybe a disconnect.  */
               /* error for now */
               error4( c4, e4connection, "connection4receive() - invalid message received" ) ;
               return 0 ;
            }
         #endif

         numParts = (unsigned int)packet4numParts( message->packet ) ;
         if ( numParts == 1 )
         {
            connection->message = message ;
            connection4addData( connection, (char *)(((char *)message->packet) + message->packet->packetDataOffset), message->packet->packetDataLen, 0 ) ;
            return connection ;
         }

         connectionId = packet4connectionId( message->packet ) ;
         numParts-- ;

         /* check if we can complete a message with available parts */
         if ( connection->receivedMessages.nLink < numParts )  /* insufficient supply of parts */
         {
            l4add( &connection->receivedMessages, message ) ;
            partialMessage = 0 ;
         }
         else
            for ( partialMessage = 0 ;; )
            {
               partialMessage = (MESSAGE4 *)l4next( &connection->receivedMessages, partialMessage ) ;
               if ( partialMessage == 0 )   /* can't complete... */
               {
                  l4add( &connection->receivedMessages, message ) ;
                  break ;
               }
               if ( packet4connectionId( partialMessage->packet ) == connectionId )
               {
                  numParts-- ;
                  if ( numParts == 0 )   /* should be able to create a complete message */
                     break ;
               }
            }

         if ( partialMessage == 0 )   /* get an additional message */
            continue ;

         numParts = packet4numParts( message->packet ) ;
         partNo = packet4partNo( message->packet ) ;
         returnMessage = 0 ;

         for ( i = 1 ; i <= numParts ; i++ )
         {
            if ( (unsigned int)partNo == i )
               partialMessage = message ;
            else   /* need to get the message */
               partialMessage = connection4returnMessage( connection, connectionId, i ) ;

            if ( partialMessage == 0 )
            {
               if ( returnMessage != 0 && message != returnMessage )
                  message4free( c4, returnMessage ) ;
               if ( message != 0 )
                  message4free( c4, message ) ;
               connection->message = 0 ;
               error4( c4, e4message, E90160 ) ;
               return 0 ;
            }

            if ( i == 1 )
            {
               connection->message = partialMessage ;
               returnMessage = partialMessage ;
               #ifdef E4ANALYZE
                  if ( connection4len( connection ) > UINT_MAX )
                  {
                     error4( c4, e4info, E90160 ) ;
                     return 0 ;
                  }
               #endif
               messageData = connection4allocateData( connection, (unsigned int)connection4len( connection ), 1, 0 ) ;
               if ( messageData == 0 )
               {
                  #ifdef E4STACK
                     error4stack( c4, e4memory, E90160 ) ;
                  #endif
                  return 0 ;
               }
               #ifdef E4ANALYZE
                  if ( messageData->len < partialMessage->packet->packetDataLen )
                  {
                     error4( c4, e4info, E90160 ) ;
                     return 0 ;
                  }
               #endif
               memcpy( messageData->allocatedData, (char *)(((char *)partialMessage->packet) + partialMessage->packet->packetDataOffset), partialMessage->packet->packetDataLen ) ;
               offset = partialMessage->packet->packetDataLen ;
            }
            else
            {
               #ifdef E4ANALYZE
                  if ( messageData->len < partialMessage->packet->packetDataLen + offset )
                  {
                     error4( c4, e4info, E90160 ) ;
                     return 0 ;
                  }
               #endif
               memcpy( messageData->allocatedData + offset, (char *)(((char *)partialMessage->packet) + partialMessage->packet->packetDataOffset), partialMessage->packet->packetDataLen ) ;
               offset += partialMessage->packet->packetDataLen ;
            }

            if ( i != 1 )
            {
               if ( message == partialMessage )
                  message = 0 ;
               message4free( c4, partialMessage ) ;
            }
         }

         #ifdef S4COM_PRINT
            if ( partNo == 1 && returnMessage != 0 )
            {
               printf( "Received Message:  " ) ;
               s4connectionPrint( packet4type( returnMessage->packet ) ) ;
            }
         #endif

         return connection ;
      }

      return 0 ;
   }
}

/* return code of 1 means done.  input len is amount available for writing, startPos is where to start writing */
static int connection4placeData( CONNECTION4 *connection, unsigned short int *dataPos, unsigned short int len, unsigned short int startLen, MESSAGE4DATA **dataIn )
{
   MESSAGE4DATA *data ;

   data = *dataIn ;

   if ( data == 0 )
      data = (MESSAGE4DATA *)l4first( &connection->messageData ) ;
   if ( data == 0 )
      return 1 ;

   while ( ( data->len - *dataPos ) <= len )
   {
      memcpy( ((char *)connection->message->packet) + startLen, data->data + *dataPos, data->len - (long)*dataPos ) ;
      len -= (unsigned short)( data->len - *dataPos ) ;
      startLen += (unsigned short)( data->len - *dataPos ) ;
      data = (MESSAGE4DATA *)l4next( &connection->messageData, data ) ;
      *dataPos = 0 ;
      if ( data == 0 )
      {
         *dataIn = data ;   /* changed */
         return 1 ;
      }
      if ( len == 0 )
      {
         *dataIn = data ;
         return 0 ;
      }
   }

   memcpy( ((char *)connection->message->packet) + startLen, data->data + *dataPos, len ) ;
   *dataPos += len ;

   *dataIn = data ;
   return 0 ;
}

#ifndef S4SERVER
/* performs a send/receive loop as long as return code is loopCode numRepeat times (or
   forever if numRepeat == - 1 )
   assigning -2 to numRepeat, or -1 to delayHundredths or loopCode results in their taking
   default appropriate CODE4 values */
int connection4repeat( CONNECTION4 *connection, const int numRepeat, const int loopCodeIn, const int delayHundredthsIn, DATA4 *d4 )
{
   int rc, loop, delayHundredths, loopCode, saveAdjustedLen ;
   MESSAGE4 *saveMessage, *saveMessage2 ;
   PACKET4 savePacketInfo ;
   LIST4 saveData, saveData2 ;
   unsigned long int saveTotal, saveTotal2 ;
   CODE4 *c4 ;
   #ifdef S4LOCK_HOOK
      int count ;
   #endif

   #ifdef E4PARM_LOW
      if ( connection == 0 || numRepeat < -2 )
         return error4( 0, e4parm, E90160 ) ;
   #endif

   c4 = connection->cb ;

   loop = numRepeat ;
   if ( loop == 0 )
      loop = 1 ;
   if ( loop == -2 )
      loop = c4->lockAttempts ;
   if ( loopCodeIn == -1 )
      loopCode = r4locked ;
   else
      loopCode = loopCodeIn ;
   if ( delayHundredthsIn == -1 )
      delayHundredths = c4->lockDelay ;
   else
      delayHundredths = delayHundredthsIn ;

   saveMessage = connection->message ;
   memcpy( &saveData, &connection->messageData, sizeof( LIST4 ) ) ;
   memcpy( &savePacketInfo, connection->message->packet, sizeof( PACKET4 ) ) ;
   saveTotal = connection->totalDataLen ;
   #ifdef S4LOCK_HOOK
      for ( count = 0 ;; count++ )
   #else
      for ( ;; )
   #endif
   {
      if ( loop != numRepeat )
      {
         connection->message = saveMessage ;
         memcpy( &connection->messageData, &saveData, sizeof( LIST4 ) ) ;
         memcpy( connection->message->packet, &savePacketInfo, sizeof( PACKET4 ) );
      }
      if ( loop > 0 )
         loop-- ;
      c4->lockedLockItem = -2L ;
      c4->lockedFileName = 0 ;
      c4->lockedUserId = 0 ;
      c4->lockedNetId = 0 ;
      connection->adjustForLocked = 0 ;
      connection->totalDataLen = saveTotal ;
      #ifndef S4LOCK_HOOK
         /* in the case of lock hook, always set, otherwise only set just before return */
         if ( loop == 0 )   /* will return next call, so get lock info if r4locked */
      #endif
            connection4setRequestLockedInfo( connection, 1 ) ;
      connection4send( connection ) ;
      connection->message = 0 ;
      memset( &connection->messageData, 0, sizeof( LIST4 ) ) ;
      connection->totalDataLen = 0 ;
      rc = connection4receive( connection ) ;
      if ( rc < 0 )
         break ;
      #ifndef S4STAND_ALONE
         if ( d4 != 0 )
            if ( packet4didUnlockData( connection->message->packet ) == 1 )
               d4unlockClientData( d4 ) ;
      #endif
      rc = connection4status( connection ) ;
      if ( loop == 0 )   /* get receive return if loop == 0 */
         break ;
      if ( rc != loopCode )
         break ;
      #ifdef S4LOCK_HOOK
         rc = code4lockHook( c4, c4->lockedFileName, c4->lockedUserId, c4->lockedNetId, c4->lockedLockItem, count ) ;
         if ( rc != 0 )
            break ;
      #endif
      if ( connection->message != 0 )
      {
         connection4clear( connection ) ;
         message4free( c4, connection->message ) ;
         connection->message = 0 ;
      }
      u4delayHundredth( delayHundredths ) ;
   }

   saveMessage2 = connection->message ;
   saveAdjustedLen = connection->adjustForLocked ;
   if ( saveMessage2 != 0 )
   {
      memcpy( &saveData2, &connection->messageData, sizeof( LIST4 ) ) ;
      saveTotal2 = connection->totalDataLen ;
   }

   if ( saveMessage != 0 )
   {
      connection->message = saveMessage ;
      memcpy( &connection->messageData, &saveData, sizeof( LIST4 ) ) ;
      memcpy( connection->message->packet, &savePacketInfo, sizeof( PACKET4 ) );
      connection->totalDataLen = saveTotal ;
      connection4clear( connection ) ;
      message4free( c4, connection->message ) ;
      connection->message = 0 ;
   }

   if ( saveMessage2 != 0 )
   {
      connection->message = saveMessage2 ;
      memcpy( &connection->messageData, &saveData2, sizeof( LIST4 ) ) ;
      connection->totalDataLen = saveTotal2 ;
      connection->adjustForLocked = saveAdjustedLen ;
   }

   return rc ;
}
#endif  /* S4SERVER */

#ifdef S4UTILS
   int S4FUNCTION connection4send( CONNECTION4 *connection )
#else
   int connection4send( CONNECTION4 *connection )
#endif
{
   int rc, numParts, partNo ;
   unsigned short int messageSendLen, incLen, dataPos, startLen ;
   long int connectionLen ;
   PACKET4HEADER *header ;
   CONNECTION4ID id ;
   MESSAGE4DATA *data ;

   #ifdef E4PARM_LOW
      if ( connection == 0 )
         return error4( 0, e4parm_null, E90160 ) ;
   #endif

   #ifdef E4ANALYZE
      if ( connection->cb == 0 )
         return error4( 0, e4struct, E90160 ) ;
      if ( connection->message == 0 )
      {
         connection->connectionFailure = e4struct ;
         return error4( 0, e4struct, E90160 ) ;
      }
   #endif

   if ( connection->connectionFailure < 0 )
      return connection->connectionFailure ;

   rc = connection4setLen( connection, connection->totalDataLen ) ;
   if ( rc < 0 )
   {
      connection->connectionFailure = rc ;
      return error4stack( connection->cb, (short)rc, E90160 ) ;
   }

   #ifdef S4COM_PRINT
      printf( "Sending Message:  " ) ;
         s4connectionPrint( connection4type( connection ) ) ;
   #endif

   messageSendLen = connection4netMessageLen( connection->net ) ;

   #ifdef E4ANALYZE
      if ( messageSendLen < sizeof( PACKET4 ) )
      {
         connection->connectionFailure = e4info ;
         return error4( connection->cb, e4info, E90160 ) ;
      }
   #endif

   connectionLen = connection4len( connection ) ;
   if ( connectionLen < 0 )
   {
      connection->connectionFailure = (short)connectionLen ;
      return error4stack( connection->cb, (short)connectionLen, E90160 ) ;
   }

   startLen = messageSendLen - sizeof( PACKET4 ) ;
   if ( connectionLen < (long)startLen )
      startLen = (unsigned short int)connectionLen ;

   incLen = messageSendLen - sizeof( PACKET4HEADER ) ;
   numParts = (int) ( ( ( connectionLen - startLen ) + ( incLen - 1 ) ) / incLen + 1 ) ;

   id = connection4netDestId( connection->net ) ;
   memcpy( &connection->message->packet->connectionId, &id, sizeof( CONNECTION4ID ) ) ;
   header = (PACKET4HEADER *)(connection->message->packet) ;
   header->numParts = numParts ;

   #ifndef S4SERVER
      packet4setDidUnlock( connection->message->packet, 0 ) ;
      packet4setDidUnlockData( connection->message->packet, 0 ) ;
   #endif

   dataPos = 0 ;
   for( partNo = 1 ; partNo <= numParts ; partNo++ )
   {
      if ( partNo == 1 )
      {
         data = 0 ;
         connection4placeData( connection, &dataPos, startLen, sizeof( PACKET4 ), &data ) ;
         header->packetDataLen = startLen ;
         header->packetDataOffset = sizeof( PACKET4 ) ;
      }
      else
      {
         if ( partNo == numParts )
            incLen = (unsigned short int)(connectionLen - startLen - ( ( numParts - 2 ) * incLen ) ) ;
         connection4placeData( connection, &dataPos, incLen, sizeof( PACKET4HEADER ), &data ) ;
         header->packetDataLen = incLen ;
         header->packetDataOffset = sizeof( PACKET4HEADER ) ;
      }

      header->partNo = partNo ;
      rc = connection4netSend( connection->net, connection->message ) ;
      if ( rc < 0 )
      {
         connection->connectionFailure = rc ;
         return error4stack( connection->cb, (short)rc, E90160 ) ;
      }
   }

   return 0 ;
}

int connection4setStatus( CONNECTION4 *connection, const int status )
{
   int rc ;

   #ifdef E4PARM_LOW
      if ( connection == 0 )
         return error4( 0, e4parm_null, E90160 ) ;
   #endif

   rc = connection4messageAlloc( connection ) ;
   if ( rc < 0 )
      return error4stack( connection->cb, (short)rc, E90160 ) ;

   #ifdef E4ANALYZE
      if ( connection->message == 0 )
         return error4( connection->cb, e4parm_null, E90160 ) ;
   #endif

   packet4setStatus( connection->message->packet, status ) ;
   #ifdef E4OFF_STRING
      return packet4setErrCode2( connection->message->packet, connection->cb->errorCode2 ) ;
   #else
      return packet4setErrCode2( connection->message->packet, error4number2( connection->cb->errorCode2 ) ) ;
   #endif
}

int connection4errorDescribeExecute( const CONNECTION4 *connection, CODE4 *c4, int c1, long c2, const char *s1, const char *s2, const char *s3 )
{
   long cErr ;

   if ( connection->message == 0 )
      cErr = c2 ;
   else
   {
      cErr = connection4errCode2( connection ) ;
      if ( c2 != 0 && ( cErr > 90000 || cErr == 0 ) )
         cErr = c2 ;
      #ifndef E4OFF_STRING
         else
            cErr = error4seek( cErr ) ;    /* convert the long value to the appropriate positional value */
      #endif
   }
   return error4describeExecute( c4, c1, cErr, s1, s2, s3 ) ;
}

#ifdef E4PARM_LOW
long connection4serverId( const CONNECTION4 *connection )
{
   if ( connection == 0 )
   {
      error4( 0, e4parm_null, E90160 ) ;
      return 0 ;
   }
   if ( connection->message == 0 )
   {
      error4( 0, e4struct, E90160 ) ;
      return 0 ;
   }

   return packet4serverId( connection->message->packet ) ;
}

long int connection4len( const CONNECTION4 *connection )
{
   if ( connection == 0 )
   {
      error4( 0, e4parm_null, E90160 ) ;
      return 0 ;
   }
   if ( connection->message == 0 )
   {
      error4( 0, e4struct, E90160 ) ;
      return 0 ;
   }

   return ( packet4len( connection->message->packet ) - connection->adjustForLocked ) ;
}

int connection4setLen( CONNECTION4 *connection, const long int dataLen )
{
   if ( connection == 0 )
      return error4( 0, e4parm_null, E90160 ) ;
   if ( connection->message == 0 )
      return error4( 0, e4struct, E90160 ) ;

   return packet4setLen( connection->message->packet, dataLen ) ;
}


void connection4setRequestLockedInfo( CONNECTION4 *connection, int val )
{
   if ( connection == 0 )
   {
      error4( 0, e4parm_null, E90160 ) ;
      return ;
   }
   if ( connection->message == 0 )
   {
      error4( 0, e4struct, E90160 ) ;
      return ;
   }

   packet4setRequestLockedInfo( connection->message->packet, val ) ;
}

int connection4status( const CONNECTION4 *connection )
{
   if ( connection == 0 )
      return error4( 0, e4parm_null, E90160 ) ;
   if ( connection->message == 0 )
      return error4( 0, e4struct, E90160 ) ;

   return packet4status( connection->message->packet ) ;
}

long connection4errCode2( const CONNECTION4 *connection )
{
   if ( connection == 0 )
      return error4( 0, e4parm_null, E90160 ) ;
   if ( connection->message == 0 )
      return error4( 0, e4struct, E90160 ) ;

   return packet4errCode2( connection->message->packet ) ;
}

int connection4type( const CONNECTION4 *connection )
{
   if ( connection == 0 )
      return error4( 0, e4parm_null, E90160 ) ;
   if ( connection->message == 0 )
      return error4( 0, e4struct, E90160 ) ;

   return packet4type( connection->message->packet ) ;
}
#endif /* E4PARM_LOW -- i.e. inline */

SOCKET4 *socket4alloc( CODE4 *c4 )
{
   SOCKET4 *socket ;

   #ifdef E4PARM_LOW
      if ( c4 == 0 )
      {
         error4( 0, e4parm_null, E90161 ) ;
         return 0 ;
      }
   #endif

   socket = (SOCKET4 *)u4allocFree( c4, (long)sizeof( SOCKET4 ) ) ;
   if ( socket == 0 )
   {
      #ifdef E4STACK
         error4stack( c4, e4memory, E90161 ) ;
      #endif
      return 0 ;
   }

   socket->cb = c4 ;

   socket->net = socket4netAlloc( socket ) ;
   if ( socket->net == 0 )
   {
      #ifdef E4STACK
         error4stack( c4, e4memory, E90161 ) ;
      #endif
      u4free( socket ) ;
      return 0 ;
   }

   return socket ;
}

int socket4connect( SOCKET4 *socket, const char *serverName, const char *processId )
{
   int rc ;
   CODE4 *c4 ;
   #ifndef S4SERVER
      int len ;
   #endif

   #ifdef E4PARM_LOW
      if ( socket == 0 )
         return error4( 0, e4parm_null, E90161 ) ;
      #ifndef S4SERVER
         if ( serverName == 0 )
            return error4( socket->cb, e4parm_null, E90161 ) ;
      #endif
   #endif

   c4 = socket->cb ;

   #ifdef E4ANALYZE
      if ( socket->connect != 0 )
         return error4( c4, e4connection, E80101 ) ;
   #endif

   for ( rc = 0 ;; )
   {
      socket->connect = connection4alloc( socket ) ;
      if ( socket->connect == 0 )
      {
         rc = e4connection ;
         break ;
      }
      #ifndef S4SERVER
         len = strlen( serverName ) ;
         len = len < sizeof( socket->serverName ) ? len : sizeof( socket->serverName ) - 1 ;
         memcpy( socket->serverName, serverName, len ) ;
         c4upper( socket->serverName ) ;
      #endif

      if ( socket->connect->net != 0 )
      {
         rc = e4socket ;
         break ;
      }
      socket->connect->net = connection4netAlloc( socket->net ) ;
      if ( socket->connect->net == 0 )
      {
         rc = error4code( c4 ) ;
         if ( rc == 0 )
            rc = e4memory ;
      }
      else
         rc = connection4netInit( socket->connect->net, socket->net ) ;

      if ( rc < 0 )
         break ;

      #ifdef S4SERVER
         rc = socket4netConnectClient( socket->net, socket->connect, socket->connect->net ) ;
      #else
         rc = socket4netConnectServer( socket->net, socket->connect->net, socket->serverName, processId ) ;
         if ( rc < 0 )
            break ;
         rc = connection4receive( socket->connect ) ;
         if ( rc < 0 )
            break ;
         if ( connection4type( socket->connect ) != CON4ACK )
            rc = error4( c4, e4connection, E80229 ) ;
      #endif
      break ;
   }

   if ( rc < 0 )
   {
      if ( error4code( c4 ) < 0 )
         return error4code( c4 ) ;
      return error4( c4, (short int)rc, E90161 ) ;
   }

   #ifndef S4SERVER
      socket->connect->connected = 1 ;
      #ifdef S4DISTRIBUTED
         l4add( &c4->servers, socket ) ;
      #endif
   #endif

   return 0 ;
}

CODE4 * S4FUNCTION socket4codeBase( SOCKET4 *socket )
{
   return socket->cb ;
}

#ifdef S4SERVER
int socket4verifyConnections( SOCKET4 *socket )
{
   SERVER4CLIENT *client, *nextClient ;

   for ( nextClient = (SERVER4CLIENT *)l4first( &socket->cb->server->clients ) ;; )
   {
      client = nextClient ;
      nextClient = (SERVER4CLIENT *)l4next( &socket->cb->server->clients, client ) ;
      if ( client == 0 )
         break ;
      if ( client->connection != 0 )
         if ( client->connection->connected )
            if ( connection4netDisconnected( client->connection->net ) == 1 )
               server4disconnect( client->server, client ) ;
   }

   return 0 ;
}

int socket4connected( SOCKET4 *socket )
{
   int rc ;
   CONNECTION4 *connection ;

   #ifdef E4PARM_LOW
      if ( socket == 0 )
         return error4( 0, e4parm_null, E90161 ) ;
   #endif

   #ifdef E4ANALYZE
      if ( socket->connect == 0 || socket->net == 0 )
         return error4( socket->cb, e4struct, E90161 ) ;
   #endif

   rc = socket4netConnected( socket->net ) ;
   if ( rc == 1 )
   {
      if ( socket->connect->net == 0 )
         return error4( 0, e4info, E90161 ) ;
      socket4verifyConnections( socket ) ;   /* make sure existing connections are all valid */
      socket->connect->connected = 1 ;
      l4add( &socket->connections, socket->connect ) ;
      connection = socket->connect ;
      socket->connect = 0 ;
      /* and send the acknowledgement message */
      connection4assign( connection, CON4ACK, 0L, 0L ) ;
      rc = connection4send( connection ) ;
      if ( connection->message != 0 )
      {
         connection4clear( connection ) ;
         message4free( socket->cb, connection->message ) ;
         connection->message = 0 ;
      }
      if ( rc < 0 )
         return error4stack( socket->cb, rc, E90161 ) ;
      return 1 ;
   }

   return rc ;
}
#endif

#ifdef E4PARM_LOW
int socket4free( SOCKET4 *socket )
{
   if ( socket == 0 )
      return 0 ;

   #ifdef E4ANALYZE
      if ( socket->initUndone != 1 )
         return error4( 0, e4info, E90161 ) ;
   #endif
   u4free( socket ) ;

   return 0 ;
}

int socket4init( SOCKET4 *socket, CODE4 *c4, const char *socketName, const char *processId )
{
   #ifdef E4PARM_LOW
      if ( socket == 0 || c4 == 0 )
         return error4( c4, e4parm_null, E90161 ) ;
      #ifdef S4SERVER
         if ( socketName == 0 )
            return error4( c4, e4parm_null, E90161 ) ;
      #endif
   #endif

   socket->cb = c4 ;

   if ( processId == 0 )
      processId = DEF4PROCESS_ID ;

   #ifdef S4SERVER
      return socket4netInitServer( socket->net, socketName, processId ) ;
   #else
      return 0 ;
   #endif
}

#ifdef S4SERVER
int socket4shutdownAdvertising( SOCKET4 *socket )
{
   return socket4netShutdownAdvertising( socket->net ) ;
}
#endif
#endif /* E4PARM_LOW -- i.e. inline */

int socket4initUndo( SOCKET4 *socket )
{
   int rc, save_rc ;
   #ifdef S4SERVER
      CONNECTION4 *connection ;
   #endif

   #ifdef E4PARM_LOW
      if ( socket == 0 )
         return error4( 0, e4parm_null, E90161 ) ;
   #endif

   #ifdef E4ANALYZE
      socket->initUndone = 1 ;
   #endif

   save_rc = 0 ;
   if ( socket->connect != 0 )
   {
      rc = connection4initUndo( socket->connect ) ;
      connection4free( socket->connect ) ;
      socket->connect = 0 ;
      if ( rc != 0 )
         save_rc = rc ;
   }

   #ifdef S4SERVER
      for( ;; )
      {
         connection = (CONNECTION4 *)l4first( &socket->connections ) ;
         if ( connection == 0 )
            break ;
         rc = connection4initUndo( connection ) ;
         if ( rc != 0 )
            save_rc = rc ;
         rc = connection4free( connection ) ;
         if ( rc != 0 )
            save_rc = rc ;
      }
   #endif

   if ( socket->net != 0 )
   {
      rc = socket4netInitUndo( socket->net ) ;
      socket4netFree( socket->net ) ;
      socket->net = 0 ;
      if ( rc != 0 )
         save_rc = rc ;
   }

   return save_rc ;
}

MESSAGE4 *socket4receive( SOCKET4 *socket )
{
   MESSAGE4 *message ;

   #ifdef E4PARM_LOW
      if ( socket == 0 )
      {
         error4( 0, e4parm_null, E90161 ) ;
         return 0 ;
      }
   #endif

   message = socket4netReceive( socket->net ) ;
   #ifdef S4COM_PRINT
      if ( message != 0 )
      {
         printf( "Received Message:  " ) ;
         s4connectionPrint( packet4type( message->packet ) ) ;
      }
   #endif
   return message ;
}

#ifdef S4COM_PRINT
static void s4connectionPrint( const int type )
{
   char *out ;

   switch( type )
   {
      #ifndef S4SINGLE
         case CON4LOCK:
            out = "CON4LOCK"  ;
            break ;
         case CON4UNLOCK:
            out = "CON4UNLOCK"  ;
            break ;
      #endif
      case CON4WRITE:
         out = "CON4WRITE"  ;
         break ;
      case CON4GO:
         out = "CON4GO"  ;
         break ;
      case CON4SKIP:
         out = "CON4SKIP"  ;
         break ;
      case CON4SEEK:
         out = "CON4SEEK"  ;
         break ;
      case CON4SEEK_DBL:
         out = "CON4SEEK_DBL"  ;
         break ;
      case CON4START:
         out = "CON4START"  ;
         break ;
      case CON4COMMIT:
         out = "CON4COMMIT"  ;
         break ;
      case CON4COMPLETE:
         out = "CON4COMPLETE"  ;
         break ;
      case CON4ROLLBACK:
         out = "CON4ROLLBACK"  ;
         break ;
      case CON4OPEN:
         out = "CON4OPEN"  ;
         break ;
      case CON4CLOSE:
         out = "CON4CLOSE"  ;
         break ;
      case CON4RECCOUNT:
         out = "CON4RECCOUNT"  ;
         break ;
      #ifndef S4SINGLE
         case CON4LOCK_CONFIRM:
            out = "CON4LOCK_CONFIRM"  ;
            break ;
         case CON4LOCK_GROUP:
            out = "CON4LOCK_GROUP"  ;
            break ;
      #endif
      case CON4CONNECT:
         out = "CON4CONNECT"  ;
         break ;
      case CON4DISCONNECT:
         out = "CON4DISCONNECT"  ;
         break ;
      case CON4PACK:
         out = "CON4PACK"  ;
         break ;
      case CON4ZAP:
         out = "CON4ZAP"  ;
         break ;
      case CON4CREATE:
         out = "CON4CREATE"  ;
         break ;
      case CON4CANCEL:
         out = "CON4CANCEL"  ;
         break ;
      case CON4RELATE_INIT:
         out = "CON4RELATE_INIT"  ;
         break ;
      case CON4RELATE_TOP:
         out = "CON4RELATE_TOP"  ;
         break ;
      case CON4RELATE_BOTTOM:
         out = "CON4RELATE_BOTTOM"  ;
         break ;
      case CON4RELATE_DO:
         out = "CON4RELATE_DO"  ;
         break ;
      case CON4RELATE_DO_ONE:
         out = "CON4RELATE_DO_ONE"  ;
         break ;
      case CON4RELATE_FREE:
         out = "CON4RELATE_FREE"  ;
         break ;
      #ifndef S4SINGLE
         case CON4RELATE_LOCK:
            out = "CON4RELATE_LOCK"  ;
            break ;
         case CON4RELATE_UNLOCK:
            out = "CON4RELATE_UNLOCK"  ;
            break ;
      #endif
      case CON4RELATE_SKIP:
         out = "CON4RELATE_SKIP"  ;
         break ;
      case CON4INDEX_CREATE:
         out = "CON4INDEX_CREATE"  ;
         break ;
      case CON4INDEX_OPEN:
         out = "CON4INDEX_OPEN"  ;
         break ;
      case CON4INDEX_CLOSE:
         out = "CON4INDEX_CLOSE"  ;
         break ;
      case CON4POSITION:
         out = "CON4POSITION"  ;
         break ;
      case CON4POSITION_SET:
         out = "CON4POSITION_SET"  ;
         break ;
      case CON4REINDEX:
         out = "CON4REINDEX"  ;
         break ;
      case CON4CHECK:
         out = "CON4CHECK"  ;
         break ;
      case CON4TOP:
         out = "CON4TOP"  ;
         break ;
      case CON4BOTTOM:
         out = "CON4BOTTOM"  ;
         break ;
      case CON4APPEND:
         out = "CON4APPEND"  ;
         break ;
      case CON4MEMO_COMPRESS:
         out = "CON4MEMO_COMPRESS"  ;
         break ;
      case CON4MEMO:
         out = "CON4MEMO"  ;
         break ;
      case CON4INFO:
         out = "CON4INFO"  ;
         break ;
      case CON4UNIQUE_SET:
         out = "CON4UNIQUE_SET"  ;
         break ;
      case CON4PASSWORD:
         out = "CON4PASSWORD"  ;
         break ;
      case CON4TRANS_INIT:
         out = "CON4TRANS_INIT"  ;
         break ;
      case CON4RELATE_OPT:
         out = "CON4RELATE_OPT"  ;
         break ;
      case CON4SYSTEM:
         out = "CON4SYSTEM"  ;
         break ;
      case CON4TAG_SYNCH:
         out = "CON4TAG_SYNCH"  ;
         break ;
      case CON4DATE_FORMAT:
         out = "CON4DATE_FORMAT"  ;
         break ;
      case CON4TRAN_EOF:
         out = "CON4TRAN_EOF"  ;
         break ;
      case CON4TRAN_EOF_HALT:
         out = "CON4TRAN_EOF_HALT"  ;
         break ;
      case CON4TRAN_RESTART:
         out = "CON4TRAN_RESTART"  ;
         break ;
      case CON4INDEX_FORMAT:
         out = "CON4INDEX_FORMAT"  ;
         break ;
      default:
         out = "INVALID MESSAGE"  ;
         break ;
   }
   printf( "%s\n", out ) ;
}
#endif

#endif /* S4OFF_COMMUNICATIONS */