/************************************************************************
 *   IRC - Internet Relay Chat, src/socketengine_select.c
 *   Copyright (C) 2003 Lucas Madar
 *
 * engine functions for the select() socket engine
 *
 */

/* $Id: socketengine_select.c,v 1.1.1.1 2005/06/27 03:02:35 sheik Exp $ */

#include "struct.h"
#include "common.h"
#include "sys.h"
#include "h.h"
#include "fds.h"

static fd_set g_read_set, g_write_set;

void engine_init()
{
   FD_ZERO(&g_read_set);
   FD_ZERO(&g_write_set);
}

void engine_add_fd(int fd)
{
   set_fd_internal(fd, (void *) 0);
}

void engine_del_fd(int fd)
{
   FD_CLR(fd, &g_read_set);
   FD_CLR(fd, &g_write_set);
}

void engine_change_fd_state(int fd, unsigned int stateplus)
{
   int prevstate = (int) get_fd_internal(fd);

   if((stateplus & FDF_WANTREAD) && !(prevstate & FDF_WANTREAD))
   { 
      FD_SET(fd, &g_read_set);
      prevstate |= FDF_WANTREAD;
   }
   else if(!(stateplus & FDF_WANTREAD) && (prevstate & FDF_WANTREAD))
   {
      FD_CLR(fd, &g_read_set);
      prevstate &= ~(FDF_WANTREAD);
   }

   if((stateplus & FDF_WANTWRITE) && !(prevstate & FDF_WANTWRITE))
   {
      FD_SET(fd, &g_write_set);
      prevstate |= FDF_WANTWRITE;
   }
   else if(!(stateplus & FDF_WANTWRITE) && (prevstate & FDF_WANTWRITE))
   {
      FD_CLR(fd, &g_write_set);
      prevstate &= ~(FDF_WANTWRITE);
   }

   set_fd_internal(fd, (void *) prevstate);
}

static void engine_get_fdsets(fd_set *r, fd_set *w)
{
   memcpy(r, &g_read_set, sizeof(fd_set));
   memcpy(w, &g_write_set, sizeof(fd_set));
}

int engine_read_message(time_t delay)
{
   fd_set read_set, write_set;
   struct timeval wt;   
   int nfds, length, i;
   unsigned int fdflags;
   int fdtype;
   void *fdvalue;
   aClient *cptr;
   aListener *lptr;

   engine_get_fdsets(&read_set, &write_set);

   wt.tv_sec = delay;
   wt.tv_usec = 0;

   nfds = select(MAXCONNECTIONS, &read_set, &write_set, NULL, &wt);
   if (nfds == -1)
   {
      if(((errno == EINTR) || (errno == EAGAIN)))
         return -1;
      report_error("select %s:%s", &me);
      sleep(5);
      return -1;
   }
   else if (nfds == 0)
      return 0;

   if(delay)
      NOW = timeofday = time(NULL);

   for (i = 0; i < MAXCONNECTIONS; i++) 
   {
      get_fd_info(i, &fdtype, &fdflags, &fdvalue);

      cptr = NULL;
      length = -1;

      if (nfds)
      {
         int rr = FD_ISSET(i, &read_set);
         int rw = FD_ISSET(i, &write_set);

         if(rr || rw)
            nfds--;
         else
            continue;

         fdfprintf(stderr, "fd %d: %s%s\n", i, rr ? "read " : "", rw ? "write" : "");

         switch(fdtype)
         {
            case FDT_NONE:
               break;

            case FDT_AUTH:
               cptr = (aClient *) fdvalue;
               if (rr)
                  read_authports(cptr);
               if (rw && cptr->authfd >= 0)
                  send_authports(cptr);
               check_client_fd(cptr);
               break;

            case FDT_LISTENER:
               lptr = (aListener *) fdvalue;
               if(rr)
                  accept_connection(lptr);
               break;

            case FDT_RESOLVER:
               do_dns_async();
               break;

            case FDT_CLIENT:
               cptr = (aClient *) fdvalue;
               readwrite_client(cptr, rr, rw);
               break;

            case FDT_CALLBACKP:
               {
                  struct fd_callbackp *fdcb = (struct fd_callbackp *) fdvalue;

                  fdcb->rdf = rr;
                  fdcb->wrf = rw;
                  (*fdcb->callback)(fdcb);
               }
               break;

            default:
               abort(); /* unknown client type? bail! */
         }
      }
      else
         break; /* no more fds? break out of the loop */
   } /* end of for() loop for testing selected sockets */

   return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1