/*
		                  LCRZO
                             Network library
             Copyright(c) 1999,2000,2001,2002 Laurent Constantin
                                  -----

  Main server    : http://www.laurentconstantin.com/
  Backup servers : http://go.to/laurentconstantin/
                   http://laurentconstantin.est-la.com/
                   http://laurentconstantin.free.fr/
                   http://lauconstantin.multimania.com/
  [my current email address is on the web servers]

                                  -----
  This file is part of Lcrzo.

  Lcrzo is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  Lcrzo 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
  Lesser General Public License for more details (http://www.gnu.org).

------------------------------------------------------------------------
*/


#include "lcrzo_incl.h"

/*-------------------------------------------------------------*/
/*-------------------------------------------------------------*/
/*-------------------------------------------------------------*/
/*-------------------------------------------------------------*/

#define LCRZO_WAIT_WAIT_DEBUG 0

/*-------------------------------------------------------------*/
typedef struct
{ lcrzo_int32 index;
  lcrzo_wait item;
  lcrzo_bool event;
} lcrzo_priv_waitlistitem;

/*-------------------------------------------------------------*/
int lcrzo_priv_wait_independent_wait_read(lcrzo_wait *pwait1, 
                                  lcrzo_time *pmaxwaitingtime, 
                                  lcrzo_bool *pevent1);
int lcrzo_priv_wait_independent_wait_read(lcrzo_wait *pwait1, 
                                  lcrzo_time *pmaxwaitingtime, 
                                  lcrzo_bool *pevent1)
{ lcrzo_bool event1;
  int ret;

  /* first, only wait */
  switch(pwait1->source)
  { case LCRZO_PRIV_WAIT_SOURCE_FILE:
      lcrzo_er(lcrzo_priv_wait_file_wait(pwait1->psource,
					 pwait1->action,
					 pmaxwaitingtime, &event1));
      break;
    case LCRZO_PRIV_WAIT_SOURCE_IPC:
      lcrzo_er(lcrzo_priv_wait_ipc_wait(pwait1->psource, 
					pmaxwaitingtime, &event1));
      break;
    case LCRZO_PRIV_WAIT_SOURCE_SNIFF:
      lcrzo_er(lcrzo_priv_wait_sniff_wait(pwait1->psource,
					  pmaxwaitingtime, &event1));
      break;
    case LCRZO_PRIV_WAIT_SOURCE_SOCK:
      lcrzo_er(lcrzo_priv_wait_sock_wait(pwait1->psource,
					 pmaxwaitingtime, &event1));
      break;
    case LCRZO_PRIV_WAIT_SOURCE_PROCESS:
      lcrzo_er(lcrzo_priv_wait_process_wait(pwait1->psource,
					    pmaxwaitingtime, &event1));
      break;
    default:
      return(lcrzo_ieuv("lcrzo_priv_wait_independent_wait_read", "pwait1->source"));
  }

#if LCRZO_WAIT_WAIT_DEBUG==1
  printf("Event1=%d\n", event1);
#endif

  /* nothing occured */
  if (!event1)
  { if (pevent1!=NULL) *pevent1=LCRZO_FALSE;
    return(LCRZO_ERR_OK);
  }

  /* read to obtain the promised data */
  switch(pwait1->source)
  { case LCRZO_PRIV_WAIT_SOURCE_FILE:
      ret=lcrzo_priv_wait_file_read(pwait1->psource, pwait1->action);
      if (ret==LCRZO_ERR_OKDATAEND)
      { ((lcrzo_file*)(pwait1->psource))->dataend=LCRZO_TRUE;
        ret=LCRZO_ERR_OK;
      }
      break;
    case LCRZO_PRIV_WAIT_SOURCE_IPC:
      ret=lcrzo_priv_wait_ipc_read(pwait1->psource);
      if (ret==LCRZO_ERR_OKDATAEND)
      { ((lcrzo_ipc*)(pwait1->psource))->dataend=LCRZO_TRUE;
        ret=LCRZO_ERR_OK;
      }
      break;
    case LCRZO_PRIV_WAIT_SOURCE_SNIFF:
      ret=lcrzo_priv_wait_sniff_read(pwait1->psource);
      if (ret==LCRZO_ERR_OKDATAEND)
      { ((lcrzo_sniff*)(pwait1->psource))->dataend=LCRZO_TRUE;
        ret=LCRZO_ERR_OK;
      }
      break;
    case LCRZO_PRIV_WAIT_SOURCE_SOCK:
      ret=lcrzo_priv_wait_sock_read(pwait1->psource);
      if (ret==LCRZO_ERR_OKDATAEND)
      { ((lcrzo_sock*)(pwait1->psource))->dataend=LCRZO_TRUE;
        ret=LCRZO_ERR_OK;
      }
      break;
    case LCRZO_PRIV_WAIT_SOURCE_PROCESS:
      /* nothing to read for a process */
      ret=LCRZO_ERR_OK;
      break;
    default:
      return(lcrzo_ieuv("lcrzo_priv_wait_independent_wait_read", "pwait1->source"));
  }

#if LCRZO_WAIT_WAIT_DEBUG==1
  printf("End read (%d)\n", ret);
#endif

  if ( ret==LCRZO_ERR_OK )
  { /* data was read */
    if (pevent1!=NULL) *pevent1=LCRZO_TRUE;
    return(LCRZO_ERR_OK);
  }
  else if ( ret==LCRZO_ERR_OKTEMPDATAEND )
  { /* no read data */
    if (pevent1!=NULL) *pevent1=LCRZO_FALSE;
    return(LCRZO_ERR_OK);
  }
  else
  { return(ret);
  }

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
lcrzo_bool lcrzo_priv_wait_check_read(lcrzo_wait *pwait);
lcrzo_bool lcrzo_priv_wait_check_read(lcrzo_wait *pwait)
{
  if (pwait->action!=LCRZO_PRIV_WAIT_ACTION_READ)
    return(LCRZO_FALSE);

  switch(pwait->source)
  { case LCRZO_PRIV_WAIT_SOURCE_FILE:
      return(lcrzo_priv_storeddata_contain(((lcrzo_file*)(pwait->psource))->storeddata,
                                           ((lcrzo_file*)(pwait->psource))->storeddatasize));
      break;
    case LCRZO_PRIV_WAIT_SOURCE_IPC:
      return(lcrzo_priv_storedtlvdata_contain(((lcrzo_ipc*)(pwait->psource))->storeddata,
                                              ((lcrzo_ipc*)(pwait->psource))->storeddatasize));
      break;
    case LCRZO_PRIV_WAIT_SOURCE_SNIFF:
      return(lcrzo_priv_storeddata_contain(((lcrzo_sniff*)(pwait->psource))->storeddata,
                                           ((lcrzo_sniff*)(pwait->psource))->storeddatasize));
      break;
    case LCRZO_PRIV_WAIT_SOURCE_SOCK:
      return(lcrzo_priv_storedtlvdata_contain(((lcrzo_sock*)(pwait->psource))->storeddata,
                                              ((lcrzo_sock*)(pwait->psource))->storeddatasize));
      break;
    default:
      lcrzo_err_print(lcrzo_ieuv("lcrzo_priv_wait_check_read", "pwait->source"));
  }

  return(LCRZO_FALSE);
}

/*-------------------------------------------------------------*/
lcrzo_bool lcrzo_priv_wait_check_read_fixed(lcrzo_wait *pwait);
lcrzo_bool lcrzo_priv_wait_check_read_fixed(lcrzo_wait *pwait)
{
  if (pwait->action!=LCRZO_PRIV_WAIT_ACTION_READ_FIXED)
    return(LCRZO_FALSE);

  switch(pwait->source)
  { case LCRZO_PRIV_WAIT_SOURCE_FILE:
      return(lcrzo_priv_storeddata_contain_fixed(((lcrzo_file*)(pwait->psource))->storeddata,
                                                 ((lcrzo_file*)(pwait->psource))->storeddatasize,
                                                 pwait->wantedfixedsize));
      break;
    case LCRZO_PRIV_WAIT_SOURCE_IPC:
      return(lcrzo_priv_storedtlvdata_contain_fixed(((lcrzo_ipc*)(pwait->psource))->storeddata,
                                                    ((lcrzo_ipc*)(pwait->psource))->storeddatasize,
                                                    pwait->wantedfixedsize));
      break;
    case LCRZO_PRIV_WAIT_SOURCE_SNIFF:
      lcrzo_err_print(lcrzo_ie("lcrzo_priv_wait_fixedok", 
                      "should not wait for fixed in sniff",
                      "on ne devrait pas attendre pour du fixed lors du sniff"));
      break;
    case LCRZO_PRIV_WAIT_SOURCE_SOCK:
      return(lcrzo_priv_storedtlvdata_contain_fixed(((lcrzo_sock*)(pwait->psource))->storeddata,
                                                    ((lcrzo_sock*)(pwait->psource))->storeddatasize,
                                                    pwait->wantedfixedsize));
      break;
    default:
      lcrzo_err_print(lcrzo_ieuv("lcrzo_priv_wait_check_read_fixed", "pwait->source"));
  }

  return(LCRZO_FALSE);
}

/*-------------------------------------------------------------*/
lcrzo_bool lcrzo_priv_wait_check_read_line(lcrzo_wait *pwait);
lcrzo_bool lcrzo_priv_wait_check_read_line(lcrzo_wait *pwait)
{ 
  if (pwait->action!=LCRZO_PRIV_WAIT_ACTION_READ_LINE)
    return(LCRZO_FALSE);

  switch(pwait->source)
  { case LCRZO_PRIV_WAIT_SOURCE_FILE:
      return(lcrzo_priv_storeddata_contain_line(((lcrzo_file*)(pwait->psource))->storeddata,
                                                ((lcrzo_file*)(pwait->psource))->storeddatasize));
      break;
    case LCRZO_PRIV_WAIT_SOURCE_IPC:
      return(lcrzo_priv_storedtlvdata_contain_line(((lcrzo_ipc*)(pwait->psource))->storeddata,
                                                    ((lcrzo_ipc*)(pwait->psource))->storeddatasize));
      break;
    case LCRZO_PRIV_WAIT_SOURCE_SNIFF:
      lcrzo_err_print(lcrzo_ie("lcrzo_priv_wait_lineok", 
                      "should not wait for line in sniff",
                      "on ne devrait pas attendre pour une ligne lors du sniff"));
      break;
    case LCRZO_PRIV_WAIT_SOURCE_SOCK:
      return(lcrzo_priv_storedtlvdata_contain_line(((lcrzo_sock*)(pwait->psource))->storeddata,
                                                    ((lcrzo_sock*)(pwait->psource))->storeddatasize));
      break;
    default:
      lcrzo_err_print(lcrzo_ieuv("lcrzo_priv_wait_check_read_line", "pwait->source"));
  }

  return(LCRZO_FALSE);
}

/*-------------------------------------------------------------*/
lcrzo_bool lcrzo_priv_wait_check_end(lcrzo_wait *pwait);
lcrzo_bool lcrzo_priv_wait_check_end(lcrzo_wait *pwait)
{
  switch(pwait->source)
  { case LCRZO_PRIV_WAIT_SOURCE_FILE:
      if ( ((lcrzo_file*)(pwait->psource))->dataend &&
           ((lcrzo_file*)(pwait->psource))->storeddatasize==0 )
        return(LCRZO_TRUE);
      if ( ((lcrzo_file*)(pwait->psource))->dataend &&
           pwait->action==LCRZO_PRIV_WAIT_ACTION_READ_LINE )
        return(LCRZO_TRUE);
      if ( ((lcrzo_file*)(pwait->psource))->dataend &&
           pwait->action==LCRZO_PRIV_WAIT_ACTION_READ_FIXED )
        return(LCRZO_TRUE);
      break;
    case LCRZO_PRIV_WAIT_SOURCE_IPC:
      if ( ((lcrzo_ipc*)(pwait->psource))->dataend &&
           ! lcrzo_priv_storedtlvdata_contain(((lcrzo_ipc*)(pwait->psource))->storeddata,
                                              ((lcrzo_ipc*)(pwait->psource))->storeddatasize) )
        return(LCRZO_TRUE);
      if ( lcrzo_priv_storedtlvdata_isatend(((lcrzo_ipc*)(pwait->psource))->storeddata,
					    ((lcrzo_ipc*)(pwait->psource))->storeddatasize) )
        return(LCRZO_TRUE);
      break;
    case LCRZO_PRIV_WAIT_SOURCE_SNIFF:
      if ( ((lcrzo_sniff*)(pwait->psource))->dataend &&
           ((lcrzo_file*)(pwait->psource))->storeddatasize==0 )
        return(LCRZO_TRUE);
      break;
    case LCRZO_PRIV_WAIT_SOURCE_SOCK:
      if ( ((lcrzo_sock*)(pwait->psource))->dataend &&
           ! lcrzo_priv_storedtlvdata_contain(((lcrzo_sock*)(pwait->psource))->storeddata,
                                              ((lcrzo_sock*)(pwait->psource))->storeddatasize) )
	return(LCRZO_TRUE);
      if ( lcrzo_priv_storedtlvdata_isatend(((lcrzo_sock*)(pwait->psource))->storeddata,
					    ((lcrzo_sock*)(pwait->psource))->storeddatasize) )
        return(LCRZO_TRUE);
      break;
    case LCRZO_PRIV_WAIT_SOURCE_PROCESS:
      if ( ((lcrzo_process*)(pwait->psource))->hasended )
        return(LCRZO_TRUE);
      break;
    default:
      lcrzo_err_print(lcrzo_ieuv("lcrzo_priv_wait_check_end", "pwait->source"));
  }

  return(LCRZO_FALSE);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_wait_wait(lcrzo_wait *pwait, 
                         lcrzo_time *pmaxwaitingtime, 
                         lcrzo_bool *pevent);
int lcrzo_priv_wait_wait(lcrzo_wait *pwait, 
                         lcrzo_time *pmaxwaitingtime, 
                         lcrzo_bool *pevent)
{ lcrzo_bool event, continuetowait;
  lcrzo_time starttime, timediff, waitingtime;
  
  /* parameters verification */
  if (pwait==NULL) return(LCRZO_ERR_PANULLPTR);
  if (pmaxwaitingtime==NULL) return(LCRZO_ERR_PANULLPTR);
  switch(pwait->action)
  { case LCRZO_PRIV_WAIT_ACTION_READ:
    case LCRZO_PRIV_WAIT_ACTION_READ_FIXED:
    case LCRZO_PRIV_WAIT_ACTION_READ_LINE:
    case LCRZO_PRIV_WAIT_ACTION_END:
      break;
    default:
      return(lcrzo_ieuv("lcrzo_priv_wait_wait", "pwait->action"));
  }

  /* initialize time */
  lcrzo_er(lcrzo_time_init_currenttime(&starttime));
  lcrzo_er(lcrzo_time_init_time(pmaxwaitingtime, &waitingtime));

  /* first, check if it's not already ok */
  if ( lcrzo_priv_wait_check_end(pwait) ||
       lcrzo_priv_wait_check_read(pwait) ||
       lcrzo_priv_wait_check_read_fixed(pwait) ||
       lcrzo_priv_wait_check_read_line(pwait)
     )
  { /* sufficient data was read */
    if (pevent!=NULL) *pevent=LCRZO_TRUE;
#if LCRZO_WAIT_WAIT_DEBUG==1
    printf("Already OK %d %d %d %d\n", lcrzo_priv_wait_check_end(pwait) ,
       lcrzo_priv_wait_check_read(pwait) ,
       lcrzo_priv_wait_check_read_fixed(pwait) ,
       lcrzo_priv_wait_check_read_line(pwait));
#endif
    return(LCRZO_ERR_OK);
  }

  /* then, loop */
  continuetowait=LCRZO_TRUE;
  do
  { /* try to wait and read data on specific devices */
    lcrzo_er(lcrzo_priv_wait_independent_wait_read(pwait, &waitingtime, &event));
    /* data was read */
    if ( event )
    { if ( lcrzo_priv_wait_check_end(pwait) ||
           lcrzo_priv_wait_check_read(pwait) ||
           lcrzo_priv_wait_check_read_fixed(pwait) ||
           lcrzo_priv_wait_check_read_line(pwait)
         )
      { /* sufficient data was read */
        if (pevent!=NULL) *pevent=LCRZO_TRUE;
#if LCRZO_WAIT_WAIT_DEBUG==1
    printf("Finally OK %d %d %d %d\n", lcrzo_priv_wait_check_end(pwait) ,
       lcrzo_priv_wait_check_read(pwait) ,
       lcrzo_priv_wait_check_read_fixed(pwait) ,
       lcrzo_priv_wait_check_read_line(pwait));
#endif
        return(LCRZO_ERR_OK);
      }
    }
    /* check if we can exit */
    if ( pmaxwaitingtime->sec==LCRZO_WAIT_NODELAY_VAL &&
	 pmaxwaitingtime->usec==LCRZO_WAIT_NODELAY_VAL )
    { /* just one test */
      continuetowait=LCRZO_FALSE;
    }
    else if ( ! ( pmaxwaitingtime->sec==LCRZO_WAIT_INFINITE_VAL &&
		  pmaxwaitingtime->usec==LCRZO_WAIT_INFINITE_VAL ) )
    { /* it's time to exit */
      lcrzo_er(lcrzo_time_diffcurrent_time(&starttime, &timediff));
      if ( timediff.sec > pmaxwaitingtime->sec ) 
        continuetowait=LCRZO_FALSE;
      if ( timediff.sec == pmaxwaitingtime->sec &&
	   timediff.usec >= pmaxwaitingtime->usec)
        continuetowait=LCRZO_FALSE;
      /* set new waiting time */
      if (continuetowait)
      { lcrzo_er(lcrzo_time_minus_time(pmaxwaitingtime, &timediff,
				       &waitingtime));
      }
    }
    if (continuetowait)
    { /* really, it can sleep more than one millisecond */
   	  lcrzo_er(lcrzo_time_sleep_msec(1)); 
    }
  }
  while (continuetowait);

  /* if we are here, no sufficient data was read */
  if (pevent!=NULL) *pevent=LCRZO_FALSE;

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_wait_list_wait(lcrzo_list *pwaitlist,
                         lcrzo_time *pmaxwaitingtime, 
                         lcrzo_bool waitforall,
                         lcrzo_bool *pevent, 
                         lcrzo_int32 *pfirsteventindex,
                         lcrzo_list *plistofalleventindexes);
int lcrzo_priv_wait_list_wait(lcrzo_list *pwaitlist,
                         lcrzo_time *pmaxwaitingtime, 
                         lcrzo_bool waitforall,
                         lcrzo_bool *pevent, 
                         lcrzo_int32 *pfirsteventindex,
                         lcrzo_list *plistofalleventindexes)
{ lcrzo_time starttime, timediff;
  lcrzo_priv_waitlistitem wli;
  lcrzo_bool event, continuetocheck, eventonall, atleastoneevent;
  lcrzo_int32 i, waitlistcount;

  /* parameters verification */
  if (pmaxwaitingtime==NULL) return(LCRZO_ERR_PANULLPTR);

  /*Developper note : this algorithm it not as good as using 
    WaitForMultipleObjects.
    However, at the time of this writing, I don't know how to mix handles
    from lcrzo_sniff, or lcrzo_sock, because these modules are not yet 
    ported to Windows. 
    So this is an intermediate solution which should be sufficient for the 
    earliest versions of the Windows port.
  */

  /* initialize the number of data sources */
  lcrzo_er(lcrzo_list_count(pwaitlist, &waitlistcount));
  if (waitlistcount==0) return(LCRZO_ERR_LOWAITLISTEMPTY);

  /* initialize time */
  lcrzo_er(lcrzo_time_init_currenttime(&starttime));

  /* check */
  atleastoneevent=LCRZO_FALSE;
  continuetocheck=LCRZO_TRUE;
  eventonall=LCRZO_FALSE;
  while(continuetocheck)
  { 
    /* loop through each data source */
    eventonall=LCRZO_TRUE;
    for ( i=1 ; i<=waitlistcount ; i++ )
    { /* select on which event to work*/
      lcrzo_er(lcrzo_list_value_pos(pwaitlist, i, &wli));
      /* verify if it contains something to read */
      if (waitlistcount==1)
      { lcrzo_er(lcrzo_priv_wait_wait(&wli.item, pmaxwaitingtime, &event));
      }
      else
      { lcrzo_er(lcrzo_priv_wait_wait(&wli.item, LCRZO_WAIT_NODELAY, &event));
      }
#if LCRZO_WAIT_WAIT_DEBUG==1
      printf("Item %ld : event=%d\n", i, event);
#endif
      if (event) atleastoneevent=LCRZO_TRUE;
      if (waitforall && !event) eventonall=LCRZO_FALSE;
      /* replace list item (even if event is false, because it might previously have been true) */
      wli.event=event;
      lcrzo_er(lcrzo_list_replace_pos(pwaitlist, i, &wli));
    }

    /* treat special exit cases */
    if ( !waitforall && atleastoneevent )
    { /* wait for one device succeded */
      continuetocheck=LCRZO_FALSE;
    }
    else if ( waitforall && eventonall )
    { /* wait for all devices succeded */
      continuetocheck=LCRZO_FALSE;
    }
    else if ( pmaxwaitingtime->sec==LCRZO_WAIT_NODELAY_VAL && pmaxwaitingtime->usec==LCRZO_WAIT_NODELAY_VAL )
    { /* just one test */
      continuetocheck=LCRZO_FALSE;
    }
    else if ( ! (pmaxwaitingtime->sec==LCRZO_WAIT_INFINITE_VAL && pmaxwaitingtime->usec==LCRZO_WAIT_INFINITE_VAL ) )
    { /* it's time to exit */
      lcrzo_er(lcrzo_time_diffcurrent_time(&starttime, &timediff));
      if ( timediff.sec > pmaxwaitingtime->sec ) 
        continuetocheck=LCRZO_FALSE;
      if ( timediff.sec == pmaxwaitingtime->sec && timediff.usec >= pmaxwaitingtime->usec)
        continuetocheck=LCRZO_FALSE;
    }
    else
    { /* infinite */
    }
    /* just a little sleep (otherwise, this loop may consume a lot of ressources) */
    if (continuetocheck)
    { /* really, it can sleep more than one millisecond */
   	  lcrzo_er(lcrzo_time_sleep_msec(1)); 
    }
  }

  /* set output values */
  if ( !waitforall && atleastoneevent )
  { if (pevent!=NULL) *pevent=LCRZO_TRUE;
    /* loop to set the eventindexes list */
    if (plistofalleventindexes!=NULL)
    { lcrzo_er(lcrzo_list_init(plistofalleventindexes, sizeof(lcrzo_int32), NULL));
    }
    for ( i=waitlistcount ; i>=1 ; i-- )
    { /* select on which event to work*/
      lcrzo_er(lcrzo_list_value_pos(pwaitlist, i, &wli));
      if (wli.event)
      { if (pfirsteventindex!=NULL) *pfirsteventindex=wli.index;
        if (plistofalleventindexes!=NULL)
        { lcrzo_er(lcrzo_list_add_first(plistofalleventindexes, &wli.index));
        }
      }
    }
  }
  else if ( waitforall && eventonall )
  { if (pevent!=NULL) *pevent=LCRZO_TRUE;
  }
  else
  { if (pevent!=NULL) *pevent=LCRZO_FALSE;
    /* do not initialize pfirsteventindex or plistofalleventindexes */
  }

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
/*-------------------------------------------------------------*/
/*-------------------------------------------------------------*/
/*-------------------------------------------------------------*/
/*-------------------------------------------------------------*/

/*-------------------------------------------------------------*/
int lcrzo_wait_init_process_end(lcrzo_process *pprocess, lcrzo_wait *pwait)
{
  if (pprocess==NULL) return(LCRZO_ERR_PANULLPTR);

  if (pwait!=NULL)
  { pwait->source=LCRZO_PRIV_WAIT_SOURCE_PROCESS;
    pwait->action=LCRZO_PRIV_WAIT_ACTION_END;
    pwait->psource=pprocess;
    lcrzo_priv_notset_int32(pwait->wantedfixedsize);
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_wait_init_file_read(lcrzo_file *pfile, lcrzo_wait *pwait)
{
  if (pfile==NULL) return(LCRZO_ERR_PANULLPTR);

  /* check if file is open for reading */
  if (!pfile->openforreading)
  { return(LCRZO_ERR_LONOTOPENFORREADING);
  }

  if (pwait!=NULL)
  { pwait->source=LCRZO_PRIV_WAIT_SOURCE_FILE;
    pwait->action=LCRZO_PRIV_WAIT_ACTION_READ;
    pwait->psource=pfile;
    lcrzo_priv_notset_int32(pwait->wantedfixedsize);
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_wait_init_file_read_fixed(lcrzo_file *pfile, lcrzo_int32 wantedfixedsize, lcrzo_wait *pwait)
{
  if (pfile==NULL) return(LCRZO_ERR_PANULLPTR);

  /* check if file is open for reading */
  if (!pfile->openforreading)
  { return(LCRZO_ERR_LONOTOPENFORREADING);
  }

  if (pwait!=NULL)
  { pwait->source=LCRZO_PRIV_WAIT_SOURCE_FILE;
    pwait->action=LCRZO_PRIV_WAIT_ACTION_READ_FIXED;
    pwait->wantedfixedsize=wantedfixedsize;
    pwait->psource=pfile;
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_wait_init_file_read_line(lcrzo_file *pfile, lcrzo_wait *pwait)
{
  if (pfile==NULL) return(LCRZO_ERR_PANULLPTR);

  /* check if file is open for reading */
  if (!pfile->openforreading)
  { return(LCRZO_ERR_LONOTOPENFORREADING);
  }

  if (pwait!=NULL)
  { pwait->source=LCRZO_PRIV_WAIT_SOURCE_FILE;
    pwait->action=LCRZO_PRIV_WAIT_ACTION_READ_LINE;
    pwait->psource=pfile;
    lcrzo_priv_notset_int32(pwait->wantedfixedsize);
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_wait_init_file_end(lcrzo_file *pfile, lcrzo_wait *pwait)
{
  if (pfile==NULL) return(LCRZO_ERR_PANULLPTR);

  if (pwait!=NULL)
  { pwait->source=LCRZO_PRIV_WAIT_SOURCE_FILE;
    pwait->action=LCRZO_PRIV_WAIT_ACTION_END;
    pwait->psource=pfile;
    lcrzo_priv_notset_int32(pwait->wantedfixedsize);
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_wait_init_ipc_read(lcrzo_ipc *pipc, lcrzo_wait *pwaitread)
{
  if (pipc==NULL) return(LCRZO_ERR_PANULLPTR);

  /* check if ipc is open for reading */
  if (!pipc->openforreading)
  { return(LCRZO_ERR_LONOTOPENFORREADING);
  }

  if (pwaitread!=NULL)
  { pwaitread->source=LCRZO_PRIV_WAIT_SOURCE_IPC;
    pwaitread->action=LCRZO_PRIV_WAIT_ACTION_READ;
    pwaitread->psource=pipc;
    lcrzo_priv_notset_int32(pwaitread->wantedfixedsize);
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_wait_init_ipc_read_fixed(lcrzo_ipc *pipc, lcrzo_int32 wantedfixedsize, lcrzo_wait *pwaitread)
{
  if (pipc==NULL) return(LCRZO_ERR_PANULLPTR);

  /* check if ipc is open for reading */
  if (!pipc->openforreading)
  { return(LCRZO_ERR_LONOTOPENFORREADING);
  }

  if (pwaitread!=NULL)
  { pwaitread->source=LCRZO_PRIV_WAIT_SOURCE_IPC;
    pwaitread->action=LCRZO_PRIV_WAIT_ACTION_READ_FIXED;
    pwaitread->wantedfixedsize=wantedfixedsize;
    pwaitread->psource=pipc;
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_wait_init_ipc_read_line(lcrzo_ipc *pipc, lcrzo_wait *pwaitread)
{
  if (pipc==NULL) return(LCRZO_ERR_PANULLPTR);

  /* check if ipc is open for reading */
  if (!pipc->openforreading)
  { return(LCRZO_ERR_LONOTOPENFORREADING);
  }

  if (pwaitread!=NULL)
  { pwaitread->source=LCRZO_PRIV_WAIT_SOURCE_IPC;
    pwaitread->action=LCRZO_PRIV_WAIT_ACTION_READ_LINE;
    pwaitread->psource=pipc;
    lcrzo_priv_notset_int32(pwaitread->wantedfixedsize);
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_wait_init_ipc_end(lcrzo_ipc *pipc, lcrzo_wait *pwaitread)
{
  if (pipc==NULL) return(LCRZO_ERR_PANULLPTR);

  if (pwaitread!=NULL)
  { pwaitread->source=LCRZO_PRIV_WAIT_SOURCE_IPC;
    pwaitread->action=LCRZO_PRIV_WAIT_ACTION_END;
    pwaitread->psource=pipc;
    lcrzo_priv_notset_int32(pwaitread->wantedfixedsize);
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_wait_init_sniff_read(lcrzo_sniff *psniff, 
                               lcrzo_wait *pwait)
{
  if (psniff==NULL) return(LCRZO_ERR_PANULLPTR);

  if (pwait!=NULL)
  { pwait->source=LCRZO_PRIV_WAIT_SOURCE_SNIFF;
    pwait->action=LCRZO_PRIV_WAIT_ACTION_READ;
    pwait->psource=psniff;
    lcrzo_priv_notset_int32(pwait->wantedfixedsize);
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_wait_init_sock_read(lcrzo_sock *psock, lcrzo_wait *pwaitread)
{
  if (psock==NULL) return(LCRZO_ERR_PANULLPTR);

  if (pwaitread!=NULL)
  { pwaitread->source=LCRZO_PRIV_WAIT_SOURCE_SOCK;
    pwaitread->action=LCRZO_PRIV_WAIT_ACTION_READ;
    pwaitread->psource=psock;
    lcrzo_priv_notset_int32(pwaitread->wantedfixedsize);
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_wait_init_sock_read_fixed(lcrzo_sock *psock, lcrzo_int32 wantedfixedsize, lcrzo_wait *pwaitread)
{
  if (psock==NULL) return(LCRZO_ERR_PANULLPTR);

  if (pwaitread!=NULL)
  { pwaitread->source=LCRZO_PRIV_WAIT_SOURCE_SOCK;
    pwaitread->action=LCRZO_PRIV_WAIT_ACTION_READ_FIXED;
    pwaitread->wantedfixedsize=wantedfixedsize;
    pwaitread->psource=psock;
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_wait_init_sock_read_line(lcrzo_sock *psock, lcrzo_wait *pwaitread)
{
  if (psock==NULL) return(LCRZO_ERR_PANULLPTR);

  if (pwaitread!=NULL)
  { pwaitread->source=LCRZO_PRIV_WAIT_SOURCE_SOCK;
    pwaitread->action=LCRZO_PRIV_WAIT_ACTION_READ_LINE;
    pwaitread->psource=psock;
    lcrzo_priv_notset_int32(pwaitread->wantedfixedsize);
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_wait_init_sock_end(lcrzo_sock *psock, lcrzo_wait *pwaitread)
{
  if (psock==NULL) return(LCRZO_ERR_PANULLPTR);

  if (pwaitread!=NULL)
  { pwaitread->source=LCRZO_PRIV_WAIT_SOURCE_SOCK;
    pwaitread->action=LCRZO_PRIV_WAIT_ACTION_END;
    pwaitread->psource=psock;
    lcrzo_priv_notset_int32(pwaitread->wantedfixedsize);
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_wait_close(lcrzo_wait *pwait)
{ 
  if (pwait==NULL) return(LCRZO_ERR_PANULLPTR);

  pwait->source=0;
  pwait->action=0;
  pwait->psource=NULL;
  lcrzo_priv_notset_int32(pwait->wantedfixedsize);

  return(LCRZO_ERR_OK);
}


/*-------------------------------------------------------------*/
lcrzo_time * lcrzo__time_init_wait_nodelay(void)
{ static lcrzo_time none;
  /* Can use static even in multithread because store always the same value */
  none.sec=LCRZO_WAIT_NODELAY_VAL;
  none.usec=LCRZO_WAIT_NODELAY_VAL;
  return(&none);
}

/*-------------------------------------------------------------*/
lcrzo_time * lcrzo__time_init_wait_infinite(void)
{ static lcrzo_time infinite;
  /* Can use static even in multithread because store always the same value */
  infinite.sec=LCRZO_WAIT_INFINITE_VAL;
  infinite.usec=LCRZO_WAIT_INFINITE_VAL;
  return(&infinite);
}


/*-------------------------------------------------------------*/
int lcrzo_priv_wait_list_init(void *pelem);
int lcrzo_priv_wait_list_init(void *pelem)
{ lcrzo_priv_waitlistitem *pwli=(lcrzo_priv_waitlistitem *)pelem;
  return(lcrzo_wait_close(&pwli->item));
}
int lcrzo_wait_list_init(lcrzo_list *pwaitlist)
{ lcrzo_er(lcrzo_list_init(pwaitlist, sizeof(lcrzo_priv_waitlistitem), 
                           &lcrzo_priv_wait_list_init));
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_wait_list_add(lcrzo_list *pwaitlist,
                        lcrzo_int32 itemindex,
                        lcrzo_wait *pwait)
{ lcrzo_priv_waitlistitem wli;

  if (pwaitlist==NULL) return(LCRZO_ERR_PANULLPTR);
  if (pwait==NULL) return(LCRZO_ERR_PANULLPTR);

  wli.index=itemindex;
  wli.item=*pwait;
  wli.event=LCRZO_FALSE;

  lcrzo_er(lcrzo_list_add_last(pwaitlist, &wli));

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
lcrzo_bool lcrzo_priv_wait_list_del(const void *pelem, void *pinfos);
lcrzo_bool lcrzo_priv_wait_list_del(const void *pelem, void *pinfos)
{ const lcrzo_priv_waitlistitem *pwli=(const lcrzo_priv_waitlistitem *)pelem;
  lcrzo_int32 *pi=(lcrzo_int32 *)pinfos;

  if (pwli->index==*pi) return(LCRZO_TRUE);
  return(LCRZO_FALSE);
}
int lcrzo_wait_list_del(lcrzo_list *pwaitlist,
                        lcrzo_int32 itemindex)
{ lcrzo_er(lcrzo_list_remove_criteria_all(pwaitlist,
					  &lcrzo_priv_wait_list_del,
					  &itemindex));
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_wait_list_wait(lcrzo_list *pwaitlist,
                         lcrzo_time *pmaxwaitingtime, 
                         lcrzo_bool *pevent, 
                         lcrzo_int32 *pfirsteventindex,
                         lcrzo_list *plistofalleventindexes)
{ return(lcrzo_priv_wait_list_wait(pwaitlist, pmaxwaitingtime, LCRZO_FALSE,
                                   pevent, pfirsteventindex,
				   plistofalleventindexes));
}

/*-------------------------------------------------------------*/
int lcrzo_wait_list_wait_all(lcrzo_list *pwaitlist,
                             lcrzo_time *pmaxwaitingtime, 
                             lcrzo_bool *pevent)
{ return(lcrzo_priv_wait_list_wait(pwaitlist, pmaxwaitingtime, LCRZO_TRUE,
                                   pevent, NULL, NULL));
}


/*-------------------------------------------------------------*/
int lcrzo_wait_wait1(lcrzo_wait *pwait1, 
                     lcrzo_time *pmaxwaitingtime, 
                     lcrzo_bool *pevent1)
{ return(lcrzo_wait_wait5(pwait1, NULL, NULL, NULL, NULL,
         pmaxwaitingtime, pevent1, NULL, NULL, NULL, NULL));
}

/*-------------------------------------------------------------*/
int lcrzo_wait_wait2(lcrzo_wait *pwait1, 
                     lcrzo_wait *pwait2, 
                     lcrzo_time *pmaxwaitingtime, 
                     lcrzo_bool *pevent1, 
                     lcrzo_bool *pevent2)
{ return(lcrzo_wait_wait5(pwait1, pwait2, NULL, NULL, NULL,
         pmaxwaitingtime, pevent1, pevent2, NULL, NULL, NULL));
}
/*-------------------------------------------------------------*/
int lcrzo_wait_wait2_all(lcrzo_wait *pwait1, 
                         lcrzo_wait *pwait2, 
                         lcrzo_time *pmaxwaitingtime, 
                         lcrzo_bool *pevent)
{ return(lcrzo_wait_wait5_all(pwait1, pwait2, NULL, NULL, NULL,
         pmaxwaitingtime, pevent));
}

/*-------------------------------------------------------------*/
int lcrzo_wait_wait3(lcrzo_wait *pwait1, 
                     lcrzo_wait *pwait2, 
                     lcrzo_wait *pwait3, 
                     lcrzo_time *pmaxwaitingtime, 
                     lcrzo_bool *pevent1, 
                     lcrzo_bool *pevent2, 
                     lcrzo_bool *pevent3)
{ return(lcrzo_wait_wait5(pwait1, pwait2, pwait3, NULL, NULL,
         pmaxwaitingtime, pevent1, pevent2, pevent3, NULL, NULL));
}
/*-------------------------------------------------------------*/
int lcrzo_wait_wait3_all(lcrzo_wait *pwait1, 
                     lcrzo_wait *pwait2, 
                     lcrzo_wait *pwait3, 
                     lcrzo_time *pmaxwaitingtime, 
                     lcrzo_bool *pevent)
{ return(lcrzo_wait_wait5_all(pwait1, pwait2, pwait3, NULL, NULL,
         pmaxwaitingtime, pevent));
}

/*-------------------------------------------------------------*/
int lcrzo_wait_wait4(lcrzo_wait *pwait1, 
                     lcrzo_wait *pwait2, 
                     lcrzo_wait *pwait3, 
                     lcrzo_wait *pwait4, 
                     lcrzo_time *pmaxwaitingtime, 
                     lcrzo_bool *pevent1, 
                     lcrzo_bool *pevent2, 
                     lcrzo_bool *pevent3, 
                     lcrzo_bool *pevent4)
{ return(lcrzo_wait_wait5(pwait1, pwait2, pwait3, pwait4, NULL,
         pmaxwaitingtime, pevent1, pevent2, pevent3, pevent4, NULL));
}
/*-------------------------------------------------------------*/
int lcrzo_wait_wait4_all(lcrzo_wait *pwait1, 
                     lcrzo_wait *pwait2, 
                     lcrzo_wait *pwait3, 
                     lcrzo_wait *pwait4, 
                     lcrzo_time *pmaxwaitingtime, 
                     lcrzo_bool *pevent)
{ return(lcrzo_wait_wait5_all(pwait1, pwait2, pwait3, pwait4, NULL,
         pmaxwaitingtime, pevent));
}

/*-------------------------------------------------------------*/
int lcrzo_wait_wait5(lcrzo_wait *pwait1, 
                     lcrzo_wait *pwait2, 
                     lcrzo_wait *pwait3, 
                     lcrzo_wait *pwait4, 
                     lcrzo_wait *pwait5, 
                     lcrzo_time *pmaxwaitingtime, 
                     lcrzo_bool *pevent1, 
                     lcrzo_bool *pevent2, 
                     lcrzo_bool *pevent3, 
                     lcrzo_bool *pevent4, 
                     lcrzo_bool *pevent5)
{ lcrzo_list list, eventlist;
  lcrzo_bool event;
  lcrzo_int32 count, i, itemindex;

  lcrzo_er(lcrzo_wait_list_init(&list));
  if (pwait1!=NULL) { lcrzo_er(lcrzo_wait_list_add(&list, 1, pwait1)); }
  if (pwait2!=NULL) { lcrzo_er(lcrzo_wait_list_add(&list, 2, pwait2)); }
  if (pwait3!=NULL) { lcrzo_er(lcrzo_wait_list_add(&list, 3, pwait3)); }
  if (pwait4!=NULL) { lcrzo_er(lcrzo_wait_list_add(&list, 4, pwait4)); }
  if (pwait5!=NULL) { lcrzo_er(lcrzo_wait_list_add(&list, 5, pwait5)); }
  lcrzo_er(lcrzo_priv_wait_list_wait(&list, pmaxwaitingtime, LCRZO_FALSE,
                                     &event, NULL, &eventlist));
  /* set default output values */
  if (pevent1!=NULL) *pevent1=LCRZO_FALSE;
  if (pevent2!=NULL) *pevent2=LCRZO_FALSE;
  if (pevent3!=NULL) *pevent3=LCRZO_FALSE;
  if (pevent4!=NULL) *pevent4=LCRZO_FALSE;
  if (pevent5!=NULL) *pevent5=LCRZO_FALSE;
  /* set true events */
  if (event)
  { /* loop through eventlist */
    lcrzo_er(lcrzo_list_count(&eventlist, &count));
    for ( i=1 ; i<=count ; i++ )
    { lcrzo_er(lcrzo_list_value_pos(&eventlist, i, &itemindex));
      switch((int)itemindex) {
        case 1 : if (pevent1!=NULL) *pevent1=LCRZO_TRUE; break;
        case 2 : if (pevent2!=NULL) *pevent2=LCRZO_TRUE; break;
        case 3 : if (pevent3!=NULL) *pevent3=LCRZO_TRUE; break;
        case 4 : if (pevent4!=NULL) *pevent4=LCRZO_TRUE; break;
        case 5 : if (pevent5!=NULL) *pevent5=LCRZO_TRUE; break;
        default : return(lcrzo_ieuv("lcrzo_wait_wait5", "itemindex"));
      }
    }
    lcrzo_er(lcrzo_list_close(&eventlist));  
  }

  lcrzo_er(lcrzo_list_close(&list));  
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_wait_wait5_all(lcrzo_wait *pwait1, 
                     lcrzo_wait *pwait2, 
                     lcrzo_wait *pwait3, 
                     lcrzo_wait *pwait4, 
                     lcrzo_wait *pwait5, 
                     lcrzo_time *pmaxwaitingtime, 
                     lcrzo_bool *pevent)
{ lcrzo_list list;

  lcrzo_er(lcrzo_wait_list_init(&list));
  if (pwait1!=NULL) { lcrzo_er(lcrzo_wait_list_add(&list, 1, pwait1)); }
  if (pwait2!=NULL) { lcrzo_er(lcrzo_wait_list_add(&list, 2, pwait2)); }
  if (pwait3!=NULL) { lcrzo_er(lcrzo_wait_list_add(&list, 3, pwait3)); }
  if (pwait4!=NULL) { lcrzo_er(lcrzo_wait_list_add(&list, 4, pwait4)); }
  if (pwait5!=NULL) { lcrzo_er(lcrzo_wait_list_add(&list, 5, pwait5)); }
  lcrzo_er(lcrzo_priv_wait_list_wait(&list, pmaxwaitingtime, LCRZO_TRUE,
                                     pevent, NULL, NULL));
  lcrzo_er(lcrzo_list_close(&list));  
  return(LCRZO_ERR_OK);
}
