/*****************************************************************************\
* Copyright (c) 2002-2004 Pelle Johansson.                                    *
* All rights reserved.                                                        *
*                                                                             *
* This file is part of the moftpd package. Use and distribution of            *
* this software is governed by the terms in the file LICENCE, which           *
* should have come with this package.                                         *
\*****************************************************************************/

/* $moftpd: events_select.c 1251 2005-03-06 22:24:29Z morth $ */

#include "system.h"

#include "events.h"

static fd_set readSet, writeSet;
static int maxFd = 0;

extern int urgData;

void events_einit (void)
{
  FD_ZERO(&readSet);
  FD_ZERO(&writeSet);
  maxFd = 0;
}

int events_earf (int fd, void *data)
{
  if (fd >= FD_SETSIZE)
  {
    // We're restricted by FD_SETSIZE in select().
    errno = EINVAL;
    return -1;
  }
  
  if (!FD_ISSET (fd, &readSet))
  {
    /* Update maxFd if appropriate. */
    if(fd >= maxFd)
      maxFd = fd + 1;
    
    FD_SET(fd, &readSet);
  }
  
  return 0;
}

int events_eawf (int fd, void *data)
{
  if (fd >= FD_SETSIZE)
  {
    // We're restricted by FD_SETSIZE in select().
    errno = EINVAL;
    return -1;
  }
  
  if (!FD_ISSET (fd, &writeSet))
  {
    /* Update maxFd if appropriate. */
    if(fd >= maxFd)
      maxFd = fd + 1;
    
    FD_SET(fd, &writeSet);
  }
  
  return 0;
}

void events_errf (int fd)
{
  if(fd < 0 || fd >= FD_SETSIZE)
    return;
  
  if (FD_ISSET (fd, &readSet))
  {
    FD_CLR(fd, &readSet);
    
    /* We might be able to lower maxFd. Check! */
    if(fd == maxFd - 1)
    {
      int fdIter, newMax = -1;
      
      for(fdIter = maxFd - 2; fdIter >= 0; fdIter--)
      {
	if(FD_ISSET(fdIter, &readSet) || FD_ISSET(fdIter, &writeSet))
	{
	  newMax = fdIter;
	  break;
	}
      }
      maxFd = newMax + 1;
    }
  }
}

void events_erwf (int fd)
{
  if(fd < 0 || fd >= FD_SETSIZE)
    return;
  
  if (FD_ISSET (fd, &writeSet))
  {
    FD_CLR(fd, &writeSet);
    
    /* We might be able to lower maxFd. Check! */
    if(fd == maxFd - 1)
    {
      int fdIter, newMax = -1;
      
      for(fdIter = maxFd - 2; fdIter >= 0; fdIter--)
      {
	if(FD_ISSET(fdIter, &readSet) || FD_ISSET(fdIter, &writeSet))
	{
	  newMax = fdIter;
	  break;
	}
      }
      maxFd = newMax + 1;
    }
  }
}

int run_events (void)
{
  fd_set rSet, wSet, eSet;
  int fdIter;
  struct timeval tv;
  
  rSet = readSet;
  wSet = writeSet;
  eSet = readSet;
  
  tv.tv_sec = 10;
  tv.tv_usec = 0;
  
  if(select(maxFd, &rSet, &wSet, &eSet, &tv) < 0)
  {
    if(errno == EINTR)
      return 0;
    syslog (LOG_ERR, "select: %m");
    return -1;
  }
  urgData = 0;
  
  for(fdIter = 0; fdIter < maxFd; fdIter++)
  {
    /*
     * Cheack readSet and writeSet as well as they might have been removed
     * by an earlier handler.
     */
    if(FD_ISSET(fdIter, &eSet) && FD_ISSET(fdIter, &readSet))
    {
      /*
       * "Exceptional state" means urgent data available on sockets. 
       * Hopefully that's the only thing that activates it, not completely
       * sure.
       */
      if (events_run_handler (fdIter, 0, 1))
	break;
    }
    if(FD_ISSET(fdIter, &rSet) && FD_ISSET(fdIter, &readSet))
    {
      if (events_run_handler (fdIter, 0, 0))
	break;
    }
    if(FD_ISSET(fdIter, &wSet) && FD_ISSET(fdIter, &writeSet))
    {
      if (events_run_handler (fdIter, 1, 0))
	break;
    }
  }
  return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1