/***************************************************************************
 *                                                                         *
 *   SOCKLST.C                                                             *
 *                                                                         *
 *   Copyright (c) 1994-1998 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   This is the implementation file for the dynamic socket list API       *
 *   used by the modified Windows 95/NT TCP/IP Kernel.                     *
 *                                                                         *
 *   Initial implementation.            - W. Muharsky                      *
 *                                      - J. Delekto       1/7/1997        *
 ***************************************************************************/

#include "gcomm.h"
#include "socklst.h"
#ifdef GCWINNT
#include "winsock.h"
#endif

#define LIST_GRANULARITY      10   /* Increments in which to allocate      */

typedef struct _SOCKETINFO {       /*  Information on socket events        */
     INT  iSocket;                 /*  Socket number to track              */
     LONG lEvents;                 /*  Events to check for                 */
} SOCKETINFO,*LPSOCKETINFO;

typedef struct _RECALLEVENTS {     /*  For backward compatibilty           */
     INT *lpiSockets;              /*  Sockets keeping track of            */
     INT iNumber;                  /*  Number of sockets in array          */
     INT iEvent;                   /*  Event to generate                   */
} RECALLEVENTS, *LPRECALLEVENTS;

static RECALLEVENTS Connects={NULL,0,FD_CONNECT};  /*  Connecting sockets  */
static RECALLEVENTS Writes={NULL,0,FD_WRITE};      /*  Writing sockets     */

static LPSOCKETINFO lpSockList=NULL;    /*  Dynamic socket event list      */
static INT iNumSockets=0;          /*  Number of sockets in list           */
static INT iNumSlots=0;            /*  Number of slots available           */

/***************************************************************************
 * Function prototypes                                                     *
 ***************************************************************************/
static
LPSOCKETINFO                       /*   Pointer to SOCKETINFO structure    */
slFind(                            /* Find a socket in the track list      */
INT iSocket);                      /*   Socket number to find              */

static VOID                        /*  For backward compatibilty           */
Recall(                            /* Recall past event                    */
LPRECALLEVENTS rc);                /*  Pointer to event list               */

static VOID
RecallTrack(                       /*  Track a recall event                */
LPRECALLEVENTS rc,                 /*  Pointer to RECALL structure         */
INT iSocket);                      /*  Socket to add to socket list        */

/***************************************************************************
 * Source code                                                             *
 ***************************************************************************/
MARKSOURCE(socklst);

VOID
slShutdown(VOID)                   /* Cleanup socket list                  */
{
     if (lpSockList != NULL) {
          free(lpSockList);
     }
}


VOID
slTrack(                           /* Add or update socket events          */
INT iSocket,                       /*   Socket number to track             */
LONG lEvent)                       /*   Event flags we are interested in   */
{
     LPSOCKETINFO slptr;

     if ((slptr=slFind(iSocket)) == NULL) {
          if (iNumSlots == iNumSockets) {
               lpSockList=(LPSOCKETINFO)alcrsz(lpSockList,
                 sizeof(SOCKETINFO)*iNumSlots,
                 sizeof(SOCKETINFO)*(iNumSlots+LIST_GRANULARITY));
               setmem(&lpSockList[iNumSlots],sizeof(SOCKETINFO)*LIST_GRANULARITY,0);
               iNumSlots+=LIST_GRANULARITY;
          }
          slptr=&lpSockList[iNumSockets];
          iNumSockets++;
     }
     slptr->iSocket=iSocket;
     slptr->lEvents|=lEvent;
}

LONG                               /*   Returned flags for the socket      */
slEvents(                          /* Retrieve the flags for a socket      */
INT iSocket)                       /*   Socket we want the flags for       */
{
     LPSOCKETINFO slptr;

     if ((slptr=slFind(iSocket)) != NULL) {
          return(slptr->lEvents);
     }
     return(0);
}

VOID
slUntrack(                         /* Clear certain socket events          */
INT iSocket,                       /*   Socket number to untrack           */
LONG lEvent)                       /*   Event flags to clear               */
{
     LPSOCKETINFO slptr;

     if ((slptr=slFind(iSocket)) != NULL) {
          slptr->lEvents&=~lEvent;
     }
}

VOID
slRemove(                          /* Remove a socket from the track list  */
INT iSocket)                       /*   Socket number to remove            */
{
     LPSOCKETINFO slptr;

     if ((slptr=slFind(iSocket)) != NULL) {
          if (slptr != &lpSockList[iNumSockets-1]) {
               *slptr=lpSockList[iNumSockets-1];
               setmem(&lpSockList[iNumSockets-1],sizeof(SOCKETINFO),0);
          }
          iNumSockets--;
     }

}

static
LPSOCKETINFO                       /*   Pointer to SOCKETINFO structure    */
slFind(                            /* Find a socket in the track list      */
INT iSocket)                       /*   Socket number to find              */
{
     INT iCount;

     for (iCount=0; iCount < iNumSockets; iCount++) {
          if (lpSockList[iCount].iSocket == iSocket) {
               return(&lpSockList[iCount]);
          }
     }
     return(NULL);
}

VOID
UpdateRecalls(VOID)                /*  Update 2 recall events being tracked */
{
     Recall(&Connects);
     Recall(&Writes);
}

VOID
RecallAdd(                         /*  Add a Recall Event                  */
INT iEvent,                        /*  Event to add for                    */
INT iSocket)                       /*  Socket number to add to list        */
{
     switch (iEvent) {
     case FD_CONNECT:
          RecallTrack(&Connects,iSocket);
          break;

     case FD_WRITE:
          RecallTrack(&Writes,iSocket);
          break;
     }
}

static VOID
RecallTrack(                       /*  Track a recall event                */
LPRECALLEVENTS rc,                 /*  Pointer to RECALL structure         */
INT iSocket)                       /*  Socket to add to socket list        */
{
     INT count;

     for (count=0; count < rc->iNumber; count++) {
          if (rc->lpiSockets[count]==iSocket) {
               return;
          }
     }

     rc->lpiSockets=(INT*)alcrsz(rc->lpiSockets,
      sizeof(INT)*rc->iNumber,sizeof(INT)*(rc->iNumber+1));
     rc->lpiSockets[rc->iNumber]=iSocket;
     rc->iNumber++;
}

static VOID                        /*  For backward compatibilty           */
Recall(                            /* Recall past event                    */
LPRECALLEVENTS rc)                 /*  Pointer to event list               */
{
     INT iNumber;
     INT *lpiSockets;
     INT iCount;

     if ((rc->lpiSockets != NULL)) {
          lpiSockets=rc->lpiSockets;
          iNumber=rc->iNumber;

          rc->lpiSockets=NULL;
          rc->iNumber=0;
          for (iCount=0; iCount < iNumber; iCount++) {
               HandleSocket(rc->iEvent,lpiSockets[iCount]);
          }
          free(lpiSockets);
     }
}
