/*
		                  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).

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

/* -- Note about comments and variable names --
   In versions 1.00 up to 2.08, lcrzo was only written in French (my
   mother tongue). Since version 3.00, lcrzo is written in English, but
   most of comments and variable names are still in French.
   In future versions, I'll try to translate everything into English,
   but as you may guess, that's a quite big work...
   So, for the moment, here are some word translation, if you want to
   be able to understand what's going on in the functions :
     afficher - print     ;  ajouter - add         ;  copie - copy
     dernier - last       ;  donnee - data         ;  ecrire - write
     entete - header      ;  envoye - sent         ;  ferme - close
     fichier - file       ;  lire - read           ;  masque - mask 
     nboct - byte number  ;  ouvre, ouvrir - open  ;  paq, paquet - packet
     plage - range        ;  premier - first       ;  recu - received
     reseau - network     ;  retour - return       ;  taille - size
     tunnel - pipe

*/

#include "lcrzo_incl.h"


/*-------------------------------------------------------------*/
#if LCRZODEF_LIBPCAPINSTALLED==1
 #include <pcap.h>
#endif

/*-------------------------------------------------------------*/
int lcrzo_priv_etha_and(const lcrzo_etha a, const lcrzo_etha b, lcrzo_etha resu)
{ int i;

  /*parameters verification*/
  if (a==NULL) return(LCRZO_ERR_PANULLPTR);
  if (b==NULL) return(LCRZO_ERR_PANULLPTR);
  
  if (resu!=NULL)
  { for ( i=0 ; i<LCRZO_ETHA_MAXBYTES ; i++ )
    { resu[i]=(lcrzo_uint8)(a[i]&b[i]);
    }
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_etha_or(const lcrzo_etha a, const lcrzo_etha b, lcrzo_etha resu)
{ int i;
  
  /*parameters verification*/
  if (a==NULL) return(LCRZO_ERR_PANULLPTR);
  if (b==NULL) return(LCRZO_ERR_PANULLPTR);
  
  if (resu!=NULL)
  { for ( i=0 ; i<LCRZO_ETHA_MAXBYTES ; i++ )
    { resu[i]=(lcrzo_uint8)(a[i]|b[i]);
    }
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_etha_xor(const lcrzo_etha a, const lcrzo_etha b, lcrzo_etha resu)
{ int i;

  /*parameters verification*/
  if (a==NULL) return(LCRZO_ERR_PANULLPTR);
  if (b==NULL) return(LCRZO_ERR_PANULLPTR);
  
  if (resu!=NULL)
  { for ( i=0 ; i<LCRZO_ETHA_MAXBYTES ; i++ )
    { resu[i]=(lcrzo_uint8)(a[i]^b[i]);
    }
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_etha_not(const lcrzo_etha a, lcrzo_etha resu)
{ int i;

  /*parameters verification*/
  if (a==NULL) return(LCRZO_ERR_PANULLPTR);
  
  if (resu!=NULL)
  { for ( i=0 ; i<LCRZO_ETHA_MAXBYTES ; i++ )
    { resu[i]=(lcrzo_uint8)(~a[i]);
    }
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_etha_inc(const lcrzo_etha a, lcrzo_etha resu)
{ int i;
  lcrzo_uint8 retenue;

  /*parameters verification*/
  if (a==NULL) return(LCRZO_ERR_PANULLPTR);
  
  if (resu!=NULL)
  { retenue=1;
    for ( i=LCRZO_ETHA_MAXBYTES-1 ; i>=0 ; i-- )
    { if ( a[i]==0xFF && retenue ) /*il va y avoir debordement*/
      { resu[i]=0;
      }
      else
      { resu[i]=(lcrzo_uint8)(a[i]+retenue);
        retenue=0; /*plus de retenue a propager*/
      }
    }
  }
  /*on ignore le debordement de ff:ff:ff:ff:ff:ff*/
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_etha_dec(const lcrzo_etha a, lcrzo_etha resu)
{ int i;
  lcrzo_uint8 retenue;

  /*parameters verification*/
  if (a==NULL) return(LCRZO_ERR_PANULLPTR);
  
  if (resu!=NULL)
  { retenue=1;
    for ( i=LCRZO_ETHA_MAXBYTES-1 ; i>=0 ; i-- )
    { if ( a[i]==0 && retenue ) /*il va y avoir debordement*/
      { resu[i]=0xFF;
      }
      else
      { resu[i]=(lcrzo_uint8)(a[i]-retenue);
        retenue=0; /*plus de retenue a propager*/
      }
    }
  }
  /*on ignore le debordement de 0:0:0:0:0:0*/
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_etha_shl(const lcrzo_etha a, lcrzo_etha resu)
{ int i;
  lcrzo_uint8 retenue, r;

  /*parameters verification*/
  if (a==NULL) return(LCRZO_ERR_PANULLPTR);
  
  if (resu!=NULL)
  { retenue=0;
    for ( i=LCRZO_ETHA_MAXBYTES-1 ; i>=0 ; i-- )
    { r=(lcrzo_uint8)(a[i]&0x80);
      resu[i]=(lcrzo_uint8)((a[i]<<1)|retenue);
      retenue=(lcrzo_uint8)(r>>7);
    }
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_etha_shr(const lcrzo_etha a, lcrzo_etha resu)
{ int i;
  lcrzo_uint8 retenue, r;

  /*parameters verification*/
  if (a==NULL) return(LCRZO_ERR_PANULLPTR);
  
  if (resu!=NULL)
  { retenue=0;
    for ( i=0 ; i<LCRZO_ETHA_MAXBYTES ; i++ )
    { r=(lcrzo_uint8)(a[i]&1);
      resu[i]=(lcrzo_uint8)((a[i]>>1)|(retenue<<7));
      retenue=r;
    }
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_etha_lt(const lcrzo_etha a, const lcrzo_etha b)
{
  if ( a[0]<b[0] ) return(LCRZO_TRUE);
  else if ( a[0]>b[0] ) return(LCRZO_FALSE);
  else
  { if ( a[1]<b[1] ) return(LCRZO_TRUE);
    else if ( a[1]>b[1] ) return(LCRZO_FALSE);
    else
    { if ( a[2]<b[2] ) return(LCRZO_TRUE);
      else if ( a[2]>b[2] ) return(LCRZO_FALSE);
      else
      { if ( a[3]<b[3] ) return(LCRZO_TRUE);
        else if ( a[3]>b[3] ) return(LCRZO_FALSE);
        else
        { if ( a[4]<b[4] ) return(LCRZO_TRUE);
          else if ( a[4]>b[4] ) return(LCRZO_FALSE);
          else
          { if ( a[5]<b[5] ) return(LCRZO_TRUE);
            else if ( a[5]>b[5] ) return(LCRZO_FALSE);
            else return(LCRZO_FALSE);
          }
        }
      }
    }
  }
}

/*-------------------------------------------------------------*/
int lcrzo_priv_etha_le(const lcrzo_etha a, const lcrzo_etha b)
{
  if ( a[0]<b[0] ) return(LCRZO_TRUE);
  else if ( a[0]>b[0] ) return(LCRZO_FALSE);
  else
  { if ( a[1]<b[1] ) return(LCRZO_TRUE);
    else if ( a[1]>b[1] ) return(LCRZO_FALSE);
    else
    { if ( a[2]<b[2] ) return(LCRZO_TRUE);
      else if ( a[2]>b[2] ) return(LCRZO_FALSE);
      else
      { if ( a[3]<b[3] ) return(LCRZO_TRUE);
        else if ( a[3]>b[3] ) return(LCRZO_FALSE);
        else
        { if ( a[4]<b[4] ) return(LCRZO_TRUE);
          else if ( a[4]>b[4] ) return(LCRZO_FALSE);
          else
          { if ( a[5]<b[5] ) return(LCRZO_TRUE);
            else if ( a[5]>b[5] ) return(LCRZO_FALSE);
            else return(LCRZO_TRUE);
          }
        }
      }
    }
  }
}

/*-------------------------------------------------------------*/
int lcrzo_priv_etha_gt(const lcrzo_etha a, const lcrzo_etha b)
{
  if ( a[0]<b[0] ) return(LCRZO_FALSE);
  else if ( a[0]>b[0] ) return(LCRZO_TRUE);
  else
  { if ( a[1]<b[1] ) return(LCRZO_FALSE);
    else if ( a[1]>b[1] ) return(LCRZO_TRUE);
    else
    { if ( a[2]<b[2] ) return(LCRZO_FALSE);
      else if ( a[2]>b[2] ) return(LCRZO_TRUE);
      else
      { if ( a[3]<b[3] ) return(LCRZO_FALSE);
        else if ( a[3]>b[3] ) return(LCRZO_TRUE);
        else
        { if ( a[4]<b[4] ) return(LCRZO_FALSE);
          else if ( a[4]>b[4] ) return(LCRZO_TRUE);
          else
          { if ( a[5]<b[5] ) return(LCRZO_FALSE);
            else if ( a[5]>b[5] ) return(LCRZO_TRUE);
            else return(LCRZO_FALSE);
          }
        }
      }
    }
  }
}

/*-------------------------------------------------------------*/
int lcrzo_priv_etha_ge(const lcrzo_etha a, const lcrzo_etha b)
{
  if ( a[0]<b[0] ) return(LCRZO_FALSE);
  else if ( a[0]>b[0] ) return(LCRZO_TRUE);
  else
  { if ( a[1]<b[1] ) return(LCRZO_FALSE);
    else if ( a[1]>b[1] ) return(LCRZO_TRUE);
    else
    { if ( a[2]<b[2] ) return(LCRZO_FALSE);
      else if ( a[2]>b[2] ) return(LCRZO_TRUE);
      else
      { if ( a[3]<b[3] ) return(LCRZO_FALSE);
        else if ( a[3]>b[3] ) return(LCRZO_TRUE);
        else
        { if ( a[4]<b[4] ) return(LCRZO_FALSE);
          else if ( a[4]>b[4] ) return(LCRZO_TRUE);
          else
          { if ( a[5]<b[5] ) return(LCRZO_FALSE);
            else if ( a[5]>b[5] ) return(LCRZO_TRUE);
            else return(LCRZO_TRUE);
          }
        }
      }
    }
  }
}


/*-------------------------------------------------------------*/
int lcrzo_priv_hdrleth_equal_addr(lcrzo_hdrleth hdrleth,
				  const lcrzo_etha ethasrc,
				  const lcrzo_etha ethadst)
{ return(lcrzo_etha_equal(hdrleth.src, ethasrc) &&
	 lcrzo_etha_equal(hdrleth.dst, ethadst));
}

/*-------------------------------------------------------------*/
int lcrzo_priv_hdrlip_equal_addr(lcrzo_hdrlip hdrlip,
				 lcrzo_ipl iplsrc,
				 lcrzo_ipl ipldst)
{ return(lcrzo_ipl_equal(hdrlip.saddr, iplsrc) &&
	 lcrzo_ipl_equal(hdrlip.daddr, ipldst));
}

/*-------------------------------------------------------------*/
int lcrzo_priv_hdrludp_equal_port(lcrzo_hdrludp hdrludp,
				  lcrzo_uint16 portsrc,
				  lcrzo_uint16 portdst)
{ return( (hdrludp.sport==portsrc) && (hdrludp.dport==portdst) );
}

/*-------------------------------------------------------------*/
int lcrzo_priv_hdrltcp_equal_port(lcrzo_hdrltcp hdrltcp,
				  lcrzo_uint16 portsrc,
				  lcrzo_uint16 portdst)
{ return( (hdrltcp.sport==portsrc) && (hdrltcp.dport==portdst) );
}

/*-------------------------------------------------------------*/
int lcrzo_ie(lcrzo_conststring functionname,
	     lcrzo_conststring frexplain,
	     lcrzo_conststring enexplain)
{ lcrzo_string string;
  lcrzo_int16 language;

  lcrzo_string_initm_text("", &string);
  if ( functionname!=NULL )
  { lcrzo_string_appendm_text(functionname, &string);
    if ( frexplain!=NULL || enexplain!=NULL )
      lcrzo_string_appendm_text(" : " , &string);
  }
  lcrzo_global_get_language(&language);
  if (language==LCRZO_GLOBAL_LANG_FR)
  { if ( frexplain!=NULL ) lcrzo_string_appendm_text(frexplain, &string);
    else if ( enexplain!=NULL ) lcrzo_string_appendm_text(enexplain, &string);
  }
  else
  { if ( enexplain!=NULL ) lcrzo_string_appendm_text(enexplain, &string);
    else if ( frexplain!=NULL ) lcrzo_string_appendm_text(frexplain, &string);
  }

  if ( functionname!=NULL || frexplain!=NULL || enexplain!=NULL )
  { lcrzo_string_appendm_text("\n" , &string);
  }

  lcrzo_priv_global_set_errmsglcrzo(string);

  if (language==LCRZO_GLOBAL_LANG_FR)
  { puts("Erreur interne : contactez Laurent");
  }
  else
  { puts("Internal error : contact Laurent");
  }
  puts(string);
  lcrzo_string_free(string);

  /* this function always return ie */
  return(LCRZO_ERR_IEINTERNALERROR);
}

/*-------------------------------------------------------------*/
int lcrzo_ieuv(lcrzo_conststring functionname,
	       lcrzo_conststring varname)
{ lcrzo_string frstr, enstr;

  lcrzo_string_initm_text("valeur inconnue", &frstr);
  lcrzo_string_initm_text("unknown value", &enstr);
  if (varname!=NULL)
  { lcrzo_string_appendm_text(" pour ", &frstr);
    lcrzo_string_appendm_text(varname, &frstr);
    lcrzo_string_appendm_text(" for ", &enstr);
    lcrzo_string_appendm_text(varname, &enstr);
  }

  lcrzo_ie(functionname, frstr, enstr);
  
  lcrzo_string_free(frstr);
  lcrzo_string_free(enstr);

  /* this function always return ie */
  return(LCRZO_ERR_IEINTERNALERROR);
}

/*-------------------------------------------------------------*/
int lcrzo_ierp(lcrzo_conststring functionname)
{ return(lcrzo_ie(functionname, 
		  "ce point n'aurait pas du etre atteint",
		  "we should not have reached this point"));
}


/*-------------------------------------------------------------*/
int lcrzo_priv_manageelempos(lcrzo_int32 elemcount,
			     lcrzo_int32 position,
			     lcrzo_bool wanttoinsertelem,
			     lcrzo_int32 *pposnegative,
			     lcrzo_int32 *ppospositive,
			     int *pdirection, 
			     lcrzo_int32 *pdistance)
{ int sens;
  lcrzo_int32 distance, pospositif, posnegatif;

  /*verification des parametres*/
  lcrzo_bool_verifbof(wanttoinsertelem);

  /*verification et calcul de la position positive*/
  if (position==0)
  { return(LCRZO_ERR_PONOZERO);
  }
  else if (position>0)
  { if ( position > elemcount+(wanttoinsertelem?1:0) )
    { return(LCRZO_ERR_POTOOBIG);
    }
    posnegatif=position-elemcount-(wanttoinsertelem?2:1);
    pospositif=position;
  }
  else
  { if ( position < -(elemcount+(wanttoinsertelem?1:0)) )
    { return(LCRZO_ERR_POTOOLOW);
    }
    posnegatif=position;
    pospositif=elemcount+(wanttoinsertelem?2:1)+position;
  }

  /*regarde quel sens prendre*/
  if ( pospositif <= -posnegatif )
  { sens=LCRZO_PRIV_INCREASE;
    distance=pospositif;
  }
  else
  { sens=LCRZO_PRIV_DECREASE;
    distance=-posnegatif;
  }

  if (pdirection!=NULL) *pdirection=sens;
  if (pdistance!=NULL) *pdistance=distance;
  if (pposnegative!=NULL) *pposnegative=posnegatif;
  if (ppospositive!=NULL) *ppospositive=pospositif;

  /*  printf("cnt=%ld pos=%ld ins=%d ==> dir %d, distance %ld [%ld,%ld]\n",
	 elemcount, position, wanttoinsertelem,
	 sens, distance, posnegatif, pospositif);*/
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_manageelemrange(lcrzo_int32 elemcount,
			       lcrzo_int32 infposition,
			       lcrzo_int32 supposition,
			       lcrzo_int32 *pinfposnegative,
			       lcrzo_int32 *pinfpospositive,
			       lcrzo_int32 *psupposnegative,
			       lcrzo_int32 *psuppospositive,
			       int *pinfdirection, 
			       lcrzo_int32 *pinfdistance,
			       int *pinfsupdirection, 
			       lcrzo_int32 *pinfsupdistance)
{ lcrzo_int32 infpospositive, suppospositive, infsupdistance;
  int infsupdirection;

  lcrzo_er(lcrzo_priv_manageelempos(elemcount, infposition, 0,
				    pinfposnegative, &infpospositive,
				    pinfdirection, pinfdistance));
  lcrzo_er(lcrzo_priv_manageelempos(elemcount, supposition, 0,
				    psupposnegative, &suppospositive,
				    NULL, NULL));

  /*on calcule le nombre d'elements a voir et le sens*/
  if (suppospositive>=infpospositive)
  { infsupdirection=LCRZO_PRIV_INCREASE;
    infsupdistance=suppospositive-infpospositive;
  }
  else
  { infsupdirection=LCRZO_PRIV_DECREASE;
    infsupdistance=infpospositive-suppospositive;
  }

  if (pinfpospositive!=NULL) *pinfpospositive=infpospositive;
  if (psuppospositive!=NULL) *psuppospositive=suppospositive;
  if (pinfsupdirection!=NULL) *pinfsupdirection=infsupdirection;
  if (pinfsupdistance!=NULL) *pinfsupdistance=infsupdistance;

  /*  printf("pos=%ld*%ld[%ld*%ld] ==> dir %d, distance %ld\n",
	 infposition, supposition, infpospositive, suppospositive, 
	 infsupdirection, infsupdistance);
  */
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_manageelemrangefast(lcrzo_int32 elemcount,
				   lcrzo_int32 infposition,
				   lcrzo_int32 supposition,
				   lcrzo_int32 *pinfposnegative,
				   lcrzo_int32 *pinfpospositive,
				   lcrzo_int32 *psupposnegative,
				   lcrzo_int32 *psuppospositive,
				   int *pinfdirection, 
				   lcrzo_int32 *pinfdistance,
				   int *pinfsupdirection, 
				   lcrzo_int32 *pinfsupdistance)
{ lcrzo_int32 infdist, supdist;

  lcrzo_er(lcrzo_priv_manageelempos(elemcount, infposition, 0, NULL, NULL,
				    NULL, &infdist));
  lcrzo_er(lcrzo_priv_manageelempos(elemcount, supposition, 0, NULL, NULL,
				    NULL, &supdist));

  if (infdist<=supdist)
  { lcrzo_er(lcrzo_priv_manageelemrange(elemcount,
					infposition, supposition,
					pinfposnegative, pinfpospositive,
					psupposnegative, psuppospositive,
					pinfdirection, pinfdistance,
					pinfsupdirection, pinfsupdistance));
  }
  else
  { lcrzo_er(lcrzo_priv_manageelemrange(elemcount,
					supposition, infposition,
					pinfposnegative, pinfpospositive,
					psupposnegative, psuppospositive,
					pinfdirection, pinfdistance,
					pinfsupdirection, pinfsupdistance));
  }

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_managedataposneg(lcrzo_int32 datasize,
				lcrzo_int32 negative,
				lcrzo_int32 *pnegative,
				lcrzo_int32 *ppositive)
{
  if (negative==0) return(LCRZO_ERR_PONOZERO);
  if (negative<datasize-1)return(LCRZO_ERR_POTOOBIG);
  
  if (pnegative!=NULL) *pnegative=negative;
  if (ppositive!=NULL) *ppositive=negative+datasize+1;
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_managedatapospos(lcrzo_int32 datasize,
				lcrzo_int32 positive,
				lcrzo_int32 *pnegative,
				lcrzo_int32 *ppositive)
{
  if (positive==0) return(LCRZO_ERR_PONOZERO);
  if (positive>datasize+1)return(LCRZO_ERR_POTOOBIG);
  
  if (pnegative!=NULL) *pnegative=positive-datasize-1;
  if (ppositive!=NULL) *ppositive=positive;
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_managedatarange(lcrzo_int32 datasize,
			       lcrzo_int32 infposition,
			       lcrzo_int32 supposition,
			       lcrzo_int32 *pinfposnegative,
			       lcrzo_int32 *pinfpospositive,
			       lcrzo_int32 *psupposnegative,
			       lcrzo_int32 *psuppospositive,
			       int *pinfdirection, 
			       lcrzo_int32 *pinfdistance,
			       int *pinfsupdirection, 
			       lcrzo_int32 *pinfsupdistance)
{ lcrzo_int32 infposnegative, infpospositive, supposnegative;
  lcrzo_int32 suppospositive, infdistance, infsupdistance;
  int infdirection, infsupdirection;

  if (infposition==0) return(LCRZO_ERR_PONOZERO);
  if (supposition==0) return(LCRZO_ERR_PONOZERO);

  /*check infposition*/
  if (infposition>0)
  { if (infposition>datasize+1)return(LCRZO_ERR_POTOOBIG);
    infposnegative=infposition-datasize-2;
    infpospositive=infposition;
  }
  else
  { if (-infposition>datasize+1)return(LCRZO_ERR_POTOOLOW);
    infposnegative=infposition;
    infpospositive=datasize+2+infposition;
  }

  /*check supposition*/
  if (supposition>0)
  { if (supposition>datasize+1)return(LCRZO_ERR_POTOOBIG);
    supposnegative=supposition-datasize-2;
    suppospositive=supposition;
  }
  else
  { if (-supposition>datasize+1)return(LCRZO_ERR_POTOOLOW);
    supposnegative=supposition;
    suppospositive=datasize+2+supposition;
  }

  /*calculate infdirection and distance*/
  if (infpospositive>(datasize+1)/2)
  { infdirection=LCRZO_PRIV_DECREASE;
    infdistance=-infposnegative-1;
  }
  else
  { infdirection=LCRZO_PRIV_INCREASE;
    infdistance=infpospositive-1;
  }

  /*on calcule le nombre d'elements a voir et le sens*/
  if (suppospositive>=infpospositive)
  { infsupdirection=LCRZO_PRIV_INCREASE;
    infsupdistance=suppospositive-infpospositive;
  }
  else
  { infsupdirection=LCRZO_PRIV_DECREASE;
    infsupdistance=infpospositive-suppospositive;
  }

  if (pinfposnegative!=NULL) *pinfposnegative=infposnegative;
  if (psupposnegative!=NULL) *psupposnegative=supposnegative;
  if (pinfpospositive!=NULL) *pinfpospositive=infpospositive;
  if (psuppospositive!=NULL) *psuppospositive=suppospositive;
  if (pinfdirection!=NULL) *pinfdirection=infdirection;
  if (pinfdistance!=NULL) *pinfdistance=infdistance;
  if (pinfsupdirection!=NULL) *pinfsupdirection=infsupdirection;
  if (pinfsupdistance!=NULL) *pinfsupdistance=infsupdistance;

  /*
  printf("%ld pos=%ld*%ld[%ld*%ld][%ld*%ld] ==> %d_%ld %d_%ld\n", datasize,
	 infposition, supposition, 
	 infposnegative, supposnegative, infpospositive, suppospositive, 
	 infdirection, infdistance, infsupdirection, infsupdistance);
  */
  return(LCRZO_ERR_OK);
}


/*-------------------------------------------------------------*/
int lcrzo_priv_data_u16_initm_data(lcrzo_constdata datain,
				   lcrzo_uint16 datainsize,
				   lcrzo_data *pdataout,
				   lcrzo_uint16 *pdataoutsize)
{ lcrzo_data ptr;

  ptr=NULL;
  lcrzo_er(lcrzo_priv_data_u16_appendm_data(datain, datainsize, 0,
					    &ptr, pdataoutsize));
  if (pdataout!=NULL) *pdataout=ptr;
  else lcrzo_data_free(ptr);
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_data_u16_appendm_data(lcrzo_constdata datain,
				     lcrzo_uint16 datainsize,
				     lcrzo_uint16 dataoutcurrentsize,
				     lcrzo_data *pdataout,
				     lcrzo_uint16 *pdataoutsize)
{ lcrzo_uint16 dataoutsize;

  /*parameters verification*/
  if (datain==NULL && datainsize) return(LCRZO_ERR_PANULLPTRSIZE);

  /*compute the output size*/
  dataoutsize=(lcrzo_uint16)(dataoutcurrentsize+datainsize);
  if (pdataoutsize!=NULL) 
  { *pdataoutsize=(lcrzo_uint16)dataoutsize;
  }

  /*init pdataout*/
  if (pdataout!=NULL)
  { lcrzo_er(lcrzo_data_realloc(dataoutsize, pdataout));
    memcpy(*pdataout+dataoutcurrentsize, datain, datainsize);
  }

  return(LCRZO_ERR_OK);
}


/*-------------------------------------------------------------*/
int lcrzo_priv_storeddata_append(lcrzo_data data, 
                                 lcrzo_int32 datasize,
                                 lcrzo_data *pstoreddata,
                                 lcrzo_int32 *pstoreaddatasize)
{
  /* check consistency of stored data */
  if (*pstoreddata==NULL && *pstoreaddatasize) return(LCRZO_ERR_PANULLPTRSIZE);

  if (*pstoreaddatasize==0)
  { /* stored data do not yet contain data */
    lcrzo_er(lcrzo_data_initm_data(data, datasize,
				   pstoreddata, pstoreaddatasize));
  }
  else
  { /* append to storeddata */
    lcrzo_er(lcrzo_data_appendm_data(data, datasize,
				     *pstoreaddatasize, 
				     pstoreddata, pstoreaddatasize));
  }

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_storeddata_insert(lcrzo_data data, 
                                 lcrzo_int32 datasize,
                                 lcrzo_data *pstoreddata,
                                 lcrzo_int32 *pstoreddatasize)
{ lcrzo_data storeddata;
  lcrzo_int32 storeddatasize;
 
  /* check consistency of stored data */
  if (*pstoreddata==NULL && *pstoreddatasize) return(LCRZO_ERR_PANULLPTRSIZE);

  if (*pstoreddatasize==0)
  { /* stored data do not yet contain data */
    lcrzo_er(lcrzo_data_initm_data(data, datasize, pstoreddata, pstoreddatasize));
  }
  else
  { /* insert before storeddata */
    lcrzo_er(lcrzo_data_initm_data(data, datasize, &storeddata, &storeddatasize));
    lcrzo_er(lcrzo_data_appendm_data(*pstoreddata, *pstoreddatasize, storeddatasize, 
                                     &storeddata, &storeddatasize));
    lcrzo_data_free(*pstoreddata);
    *pstoreddata=storeddata;
    *pstoreddatasize=storeddatasize;
  }

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
lcrzo_bool lcrzo_priv_storeddata_contain(lcrzo_data storeddata,
                                         lcrzo_int32 storeddatasize)
{ if ( storeddatasize >= 1 ) return(LCRZO_TRUE);
  return(LCRZO_FALSE);
  storeddata=storeddata;
}

/*-------------------------------------------------------------*/
lcrzo_bool lcrzo_priv_storeddata_contain_fixed(lcrzo_data storeddata,
                                               lcrzo_int32 storeddatasize,
                                               lcrzo_int32 wantedsize)
{ if ( storeddatasize >= wantedsize ) return(LCRZO_TRUE);
  return(LCRZO_FALSE);
  storeddata=storeddata;
}

/*-------------------------------------------------------------*/
lcrzo_bool lcrzo_priv_storeddata_contain_line(lcrzo_data storeddata,
                                              lcrzo_int32 storeddatasize)
{ char *pc;

  if (storeddatasize==0)
    return(LCRZO_FALSE);

  /* search the '\n' */
  pc=memchr(storeddata, 0x0A, storeddatasize);
  if (pc!=NULL)
    return(LCRZO_TRUE);

  return(LCRZO_FALSE);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_storeddata_extract(lcrzo_data *pstoreddata,
				  lcrzo_int32 *pstoreddatasize,
				  lcrzo_bool dataend,
				  lcrzo_data *pdataout,
				  lcrzo_int32 *pdataoutsize)
{
  /* check consistency of stored data */
  if (*pstoreddata==NULL && *pstoreddatasize) return(LCRZO_ERR_PANULLPTRSIZE);

  if (*pstoreddatasize==0)
  { if (dataend) return(LCRZO_ERR_OKDATAEND);
    else return(LCRZO_ERR_OKTEMPDATAEND);
  }

  if (pdataoutsize!=NULL) *pdataoutsize=*pstoreddatasize;
  *pstoreddatasize=0;

  if (pdataout==NULL)
  { lcrzo_data_free(*pstoreddata);
  }
  else
  { *pdataout=*pstoreddata;
  }
  *pstoreddata=NULL;

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_storeddata_extract_fixed(lcrzo_data *pstoreddata,
					lcrzo_int32 *pstoreddatasize,
					lcrzo_bool dataend,
					lcrzo_int32 wantedsize,
					lcrzo_data *pdataout)
{ lcrzo_data ptr;

  /* check consistency of stored data */
  if (*pstoreddata==NULL && *pstoreddatasize) return(LCRZO_ERR_PANULLPTRSIZE);
  if (*pstoreddatasize<wantedsize)
  { if ( dataend )
    { if (*pstoreddatasize!=0) return(LCRZO_ERR_OKAVAILDATAOTHTYPE);
      else return(LCRZO_ERR_OKDATAEND);
    }
    else return(LCRZO_ERR_OKTEMPDATAEND);
  }

  if ( *pstoreddatasize == wantedsize )
  { if (pdataout==NULL) lcrzo_data_free(*pstoreddata);
    else *pdataout=*pstoreddata;
    *pstoreddata=NULL;
    *pstoreddatasize=0;
  }
  else
  { lcrzo_er(lcrzo_data_initm_range(*pstoreddata, *pstoreddatasize,
				    +1, 1+wantedsize,
				    pdataout, NULL));
    lcrzo_er(lcrzo_data_initm_range(*pstoreddata, *pstoreddatasize,
				    1+wantedsize, -1,
				    &ptr, pstoreddatasize));
    lcrzo_data_free(*pstoreddata);
    *pstoreddata=ptr;
  }

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_storeddata_extract_line(lcrzo_data *pstoreddata,
				       lcrzo_int32 *pstoreddatasize,
				       lcrzo_bool dataend,
				       lcrzo_data *pdataout,
				       lcrzo_int32 *pdataoutsize)
{ lcrzo_int32 dataoutsize;
  lcrzo_uint8 *pc;
  lcrzo_data ptr;

  /* check consistency of stored data */
  if (*pstoreddata==NULL && *pstoreddatasize) return(LCRZO_ERR_PANULLPTRSIZE);

  pc=memchr(*pstoreddata, 0x0A, *pstoreddatasize);
  if (pc==NULL)
  { if ( dataend )
    { if (*pstoreddatasize!=0) return(LCRZO_ERR_OKAVAILDATAOTHTYPE);
      else return(LCRZO_ERR_OKDATAEND);
    }
    else return(LCRZO_ERR_OKTEMPDATAEND);
  }
  dataoutsize=1+pc-(*pstoreddata);

  lcrzo_er(lcrzo_priv_storeddata_extract_fixed(pstoreddata, pstoreddatasize,
					       LCRZO_FALSE,
                                               dataoutsize, &ptr));
  /* ignore the ending '\n' and '\r' */
  if (pdataoutsize!=NULL)
  { while ( dataoutsize>0 &&
	    ( ptr[dataoutsize-1]==0x0D || ptr[dataoutsize-1]==0x0A ) )
    { dataoutsize--; 
    }
    *pdataoutsize=dataoutsize;
  }
  if (pdataout!=NULL) *pdataout=ptr;
  else lcrzo_data_free(ptr);

  return(LCRZO_ERR_OK);
}


/*-------------------------------------------------------------*/
int lcrzo_priv_storedtlvdata_append(int type,
                                    lcrzo_data data, 
                                    lcrzo_int32 datasize,
                                    lcrzo_data *pstoredtlvdata,
                                    lcrzo_int32 *pstoredtlvdatasize)
{ lcrzo_uint8 tl[6];

  /* check consistency of stored data */
 if (*pstoredtlvdata==NULL && *pstoredtlvdatasize)
   return(LCRZO_ERR_PANULLPTRSIZE);

  if (*pstoredtlvdatasize==0)
  { /* stored data do not yet contain data */
    lcrzo_er(lcrzo_data_initm_text("", pstoredtlvdata, pstoredtlvdatasize));
  }

  /* append type and length */
  tl[0]=(lcrzo_uint8)(type>>8);
  tl[1]=(lcrzo_uint8)(type&0xFF);
  tl[2]=(lcrzo_uint8)(datasize>>24);
  tl[3]=(lcrzo_uint8)((datasize>>16)&0xFF);
  tl[4]=(lcrzo_uint8)((datasize>>8)&0xFF);
  tl[5]=(lcrzo_uint8)(datasize&0xFF);
  lcrzo_er(lcrzo_data_appendm_data(tl, 6, *pstoredtlvdatasize, pstoredtlvdata,
				   pstoredtlvdatasize));

  /* append data */
  lcrzo_er(lcrzo_data_appendm_data(data, datasize, *pstoredtlvdatasize,
				   pstoredtlvdata, pstoredtlvdatasize));
  
  return(LCRZO_ERR_OK);
}


/*-------------------------------------------------------------*/
int lcrzo_priv_storedtlvdata_insert(int type,
                                    lcrzo_data data, 
                                    lcrzo_int32 datasize,
                                    lcrzo_data *pstoredtlvdata,
                                    lcrzo_int32 *pstoredtlvdatasize)
{ lcrzo_uint8 tl[6];
  lcrzo_data storedtlvdata;
  lcrzo_int32 storedtlvdatasize;

  /* check consistency of stored data */
  if (*pstoredtlvdata==NULL && *pstoredtlvdatasize)
    return(LCRZO_ERR_PANULLPTRSIZE);

  if (*pstoredtlvdatasize==0)
  { /* stored data do not yet contain data */
    lcrzo_er(lcrzo_data_initm_text("", pstoredtlvdata, pstoredtlvdatasize));
  }

  /* set type and length */
  tl[0]=(lcrzo_uint8)(type>>8);
  tl[1]=(lcrzo_uint8)(type&0xFF);
  tl[2]=(lcrzo_uint8)(datasize>>24);
  tl[3]=(lcrzo_uint8)((datasize>>16)&0xFF);
  tl[4]=(lcrzo_uint8)((datasize>>8)&0xFF);
  tl[5]=(lcrzo_uint8)(datasize&0xFF);
  lcrzo_er(lcrzo_data_initm_data(tl, 6, &storedtlvdata, &storedtlvdatasize));

  /* append data */
  lcrzo_er(lcrzo_data_appendm_data(data, datasize, storedtlvdatasize,
				   &storedtlvdata, &storedtlvdatasize));
  
  /* append old storedtlvs */
  lcrzo_er(lcrzo_data_appendm_data(*pstoredtlvdata, *pstoredtlvdatasize,
				   storedtlvdatasize, &storedtlvdata,
				   &storedtlvdatasize));
  lcrzo_data_free(*pstoredtlvdata);
  
  /* set output */
  *pstoredtlvdata=storedtlvdata;
  *pstoredtlvdatasize=storedtlvdatasize;

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
lcrzo_bool lcrzo_priv_storedtlvdata_copyfirst(lcrzo_constdata storedtlvdata,
                                              lcrzo_int32 storedtlvdatasize,
                                              lcrzo_uint16 *ptype,
                                              lcrzo_data *pdata,
                                              lcrzo_int32 *pdatasize,
                                              lcrzo_int32 *preachedsize)
{ lcrzo_uint16 type;
  lcrzo_uint32 msgsize;

  /* not enough data for a message header */
  if (storedtlvdatasize<6) return(LCRZO_FALSE);

  /* set type and size */
  type = (lcrzo_uint16)( (storedtlvdata[0]<<8) | storedtlvdata[1] );
  msgsize = (storedtlvdata[2]<<24) | (storedtlvdata[3]<<16) |
    (storedtlvdata[4]<<8) | storedtlvdata[5] ;

  /* check if there is sufficient data */
  if ( (lcrzo_uint32)storedtlvdatasize-6 < msgsize ) return(LCRZO_FALSE);

  /* extract data */
  if (ptype!=NULL) *ptype=type;
  lcrzo_data_initm_range(storedtlvdata, storedtlvdatasize, +7, msgsize+7,
			 pdata, pdatasize);
  if (preachedsize!=NULL) *preachedsize=msgsize+6;
  return(LCRZO_TRUE);
}

/*-------------------------------------------------------------*/
lcrzo_bool lcrzo_priv_storedtlvdata_contain(lcrzo_data storedtlvdata,
                                            lcrzo_int32 storedtlvdatasize)
{ return(lcrzo_priv_storedtlvdata_copyfirst(storedtlvdata, storedtlvdatasize,
                                            NULL, NULL, NULL, NULL));
}

/*-------------------------------------------------------------*/
lcrzo_bool lcrzo_priv_storedtlvdata_contain_fixed(lcrzo_data storedtlvdata,
                                                 lcrzo_int32 storedtlvdatasize,
                                                  lcrzo_int32 wantedsize)
{ lcrzo_int32 reachedsize, i, datasize, totalsize;
  lcrzo_uint16 type;
  lcrzo_data data;
  
  i=0;
  totalsize=0;
  while (lcrzo_priv_storedtlvdata_copyfirst(storedtlvdata+i, 
					    storedtlvdatasize-i,
                                            &type, &data, &datasize, 
					    &reachedsize))
  { totalsize+=datasize;
    i+=reachedsize;
    lcrzo_data_free(data);
    /* the stored type is not correct */
    if (type!=LCRZO_PRIV_IPCTYPE_MSG) return(LCRZO_FALSE);
    /* stored data contains sufficient data */
    if (totalsize>=wantedsize) return(LCRZO_TRUE);
  }
  return(LCRZO_FALSE);
}

/*-------------------------------------------------------------*/
lcrzo_bool lcrzo_priv_storedtlvdata_contain_line(lcrzo_data storedtlvdata,
                                                 lcrzo_int32 storedtlvdatasize)
{ lcrzo_int32 reachedsize, i, datasize;
  lcrzo_uint16 type;
  lcrzo_data data;
  char *pc;
  
  i=0;
  while (lcrzo_priv_storedtlvdata_copyfirst(storedtlvdata+i,
					    storedtlvdatasize-i,
                                            &type, &data, &datasize,
					    &reachedsize))
  { i+=reachedsize;
    /* the stored type is not correct */
    if (type!=LCRZO_PRIV_IPCTYPE_MSG)
    { lcrzo_data_free(data);
      return(LCRZO_FALSE);
    }
    /* search the '\n' */
    pc=memchr(data, 0x0A, datasize);
    lcrzo_data_free(data);
    if (pc!=NULL) return(LCRZO_TRUE);
  }
  return(LCRZO_FALSE);
}

/*-------------------------------------------------------------*/
lcrzo_bool lcrzo_priv_storedtlvdata_isatend(lcrzo_data storedtlvdata,
					    lcrzo_int32 storedtlvdatasize)
{ lcrzo_uint16 type;
  int ret;
  
  ret=lcrzo_priv_storedtlvdata_copyfirst(storedtlvdata, storedtlvdatasize,
					 &type, NULL, NULL, NULL);
  if ( ret==LCRZO_TRUE )
  { if (type==LCRZO_PRIV_IPCTYPE_MSGEND)
    { return(LCRZO_TRUE);
    }
  }
  return(LCRZO_FALSE);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_storedtlvdata_extractifwanted(lcrzo_data *pstoredtlvdata,
                                             lcrzo_int32 *pstoredtlvdatasize,
                                             lcrzo_bool dataend,
                                             lcrzo_uint16 wantedtype,
                                             lcrzo_int32 wantedsize,
                                             lcrzo_data *pdataout,
					     lcrzo_int32 *pdataoutsize)
{ lcrzo_bool err;
  lcrzo_int32 reachedsize, datasize;
  lcrzo_uint16 type;
  lcrzo_data data;
  
  /* exit cases without data */
  err=lcrzo_priv_storedtlvdata_copyfirst(*pstoredtlvdata, *pstoredtlvdatasize,
                                         &type, &data, &datasize,
					 &reachedsize);
  if ( !err )
  { /* message not complete */
    if (dataend) return(LCRZO_ERR_OKDATAEND);
    else return(LCRZO_ERR_OKTEMPDATAEND);
  }
  if ( type!=wantedtype )
  { lcrzo_data_free(data);
    if (type==LCRZO_PRIV_IPCTYPE_MSGEND) return(LCRZO_ERR_OKDATAEND);
    return(LCRZO_ERR_OKAVAILDATAOTHTYPE);
  }
  if ( wantedsize!=0/*no check*/ && datasize!=wantedsize )
  { lcrzo_data_free(data);
    return(LCRZO_ERR_IEIPCBADELEMSIZE);
  }

  /* remove this message from queue (suppose nobody did it just before us) */
  lcrzo_efr(lcrzo_priv_storeddata_extract_fixed(pstoredtlvdata,
						pstoredtlvdatasize, 
						LCRZO_FALSE,
                                                reachedsize, NULL), 
	    lcrzo_data_free(data));
  /* set output */
  if (pdataoutsize!=NULL) *pdataoutsize=datasize;
  if (pdataout==NULL)
  { lcrzo_data_free(data);
  }
  else
  { *pdataout=data;
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_storedtlvdata_extract(lcrzo_data *pstoredtlvdata,
                                     lcrzo_int32 *pstoredtlvdatasize,
                                     lcrzo_bool dataend,
                                     lcrzo_data *pdataout,
 	                                   lcrzo_int32 *pdataoutsize)
{ return(lcrzo_priv_storedtlvdata_extractifwanted(pstoredtlvdata,
						  pstoredtlvdatasize, dataend,
                                                  LCRZO_PRIV_IPCTYPE_MSG, 0,
                                                  pdataout, pdataoutsize));
}


/*-------------------------------------------------------------*/
int lcrzo_priv_storedtlvdata_extract_fixed(lcrzo_data *pstoredtlvdata,
                                           lcrzo_int32 *pstoredtlvdatasize,
                                           lcrzo_bool dataend,
                                           lcrzo_int32 datasizetoread,
                                           lcrzo_data *pdataout)
{ lcrzo_int32 reachedsize, datasize, dataoutsize, i;
  lcrzo_uint16 type;
  lcrzo_data data, dataout;
  int ret;

  /* loop and extract data */ 
  i=0;
  lcrzo_er(lcrzo_data_initm_text("", &dataout, &dataoutsize));
  while (lcrzo_priv_storedtlvdata_copyfirst(*pstoredtlvdata+i,
					    *pstoredtlvdatasize-i,
                                            &type, &data, &datasize,
					    &reachedsize))
  { 
    /* error if the stored type is not correct */
    if (type!=LCRZO_PRIV_IPCTYPE_MSG)
    { lcrzo_data_free(data);
      lcrzo_data_free(dataout);
      return(LCRZO_ERR_OKAVAILDATAOTHTYPE);
    }
    /* append data */
    ret=lcrzo_data_appendm_data(data, datasize, dataoutsize,
				&dataout, &dataoutsize);
    lcrzo_data_free(data);
    if (ret!=LCRZO_ERR_OK)
    { lcrzo_data_free(dataout);
      return(ret);
    }
    /* dataout contains sufficient data */
    if (dataoutsize>=datasizetoread)
    { /* remove messages from queue */
      lcrzo_efr(lcrzo_priv_storeddata_extract_fixed(pstoredtlvdata,
						    pstoredtlvdatasize,
						    LCRZO_FALSE,
                                                    i+reachedsize, NULL),
		lcrzo_data_free(dataout));
      /* eventually insert a new message */
      if (dataoutsize>datasizetoread)
      { datasize=dataoutsize-datasizetoread;
        lcrzo_efr(lcrzo_priv_storedtlvdata_insert(LCRZO_PRIV_IPCTYPE_MSG,
                                                  dataout+datasizetoread,
						  datasize,
                                                  pstoredtlvdata,
						  pstoredtlvdatasize),
                                                  lcrzo_data_free(dataout));
      }
      /* set output */
      ret=lcrzo_data_initm_data(dataout, datasizetoread, pdataout, NULL);
      lcrzo_data_free(dataout);
      return(ret);
    }
    /* continue */
    i+=reachedsize;
  }

  /* arrive here on error */
  lcrzo_data_free(dataout);
  if (i==0) /* there is not even one message*/
  { if (dataend) return(LCRZO_ERR_OKDATAEND);
  }
  return(LCRZO_ERR_OKTEMPDATAEND);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_storedtlvdata_extract_line(lcrzo_data *pstoredtlvdata,
                                          lcrzo_int32 *pstoredtlvdatasize,
                                          lcrzo_bool dataend,
                                          lcrzo_data *pdataout,
					  lcrzo_int32 *pdataoutsize)
{ lcrzo_int32 reachedsize, datasize, dataoutsize, i, datasizetoread;
  lcrzo_uint16 type;
  lcrzo_uintmax datasizetoreadmax;
  lcrzo_data data, dataout;
  int ret;
  char *pc;

  /* loop and extract data */ 
  i=0;
  lcrzo_er(lcrzo_data_initm_text("", &dataout, &dataoutsize));
  while (lcrzo_priv_storedtlvdata_copyfirst(*pstoredtlvdata+i,
					    *pstoredtlvdatasize-i,
                                            &type, &data, &datasize,
					    &reachedsize))
  { 
    /* error if the stored type is not correct */
    if (type!=LCRZO_PRIV_IPCTYPE_MSG)
    { lcrzo_data_free(data);
      lcrzo_data_free(dataout);
      return(LCRZO_ERR_OKAVAILDATAOTHTYPE);
    }
    /* append data */
    ret=lcrzo_data_appendm_data(data, datasize, dataoutsize,
				&dataout, &dataoutsize);
    lcrzo_data_free(data);
    if (ret!=LCRZO_ERR_OK)
    { lcrzo_data_free(dataout);
      return(ret);
    }
    /* dataout contains sufficient data */
    pc=memchr(dataout, 0x0A, dataoutsize);
    if (pc!=NULL)
    { datasizetoreadmax=(lcrzo_uintmax)pc-(lcrzo_uintmax)dataout+1;
      if (datasizetoreadmax>0x7FFFFFFF)
      { lcrzo_data_free(dataout);
        return(LCRZO_ERR_PATOOBIG);
      }
      datasizetoread=datasizetoreadmax;
      /* remove messages from queue */
      lcrzo_efr(lcrzo_priv_storeddata_extract_fixed(pstoredtlvdata, 
						    pstoredtlvdatasize,
						    LCRZO_FALSE,
                                                    i+reachedsize, NULL),
		lcrzo_data_free(dataout));
      /* eventually insert a new message */
      if (dataoutsize>datasizetoread)
      { datasize=dataoutsize-datasizetoread;
        lcrzo_efr(lcrzo_priv_storedtlvdata_insert(LCRZO_PRIV_IPCTYPE_MSG,
                                                  dataout+datasizetoread,
						  datasize,
                                                  pstoredtlvdata,
						  pstoredtlvdatasize),
                                                  lcrzo_data_free(dataout));
      }
      /* set output */
      /* ignore the ending '\n' and '\r' */
      if (pdataoutsize!=NULL)
      { while ( datasizetoread>0 &&
		( dataout[datasizetoread-1]==0x0D ||
		  dataout[datasizetoread-1]==0x0A ) )
        { datasizetoread--; 
        }
        *pdataoutsize=datasizetoread;
      }
      if (pdataout!=NULL) *pdataout=dataout;
      else lcrzo_data_free(dataout);
      return(LCRZO_ERR_OK);
    }
    /* continue */
    i+=reachedsize;
  }

  /* arrive here on error */
  lcrzo_data_free(dataout);
  if (i==0) /* there is not even one message*/
  { if (dataend) return(LCRZO_ERR_OKDATAEND);
  }
  return(LCRZO_ERR_OKTEMPDATAEND);
}


/*-------------------------------------------------------------*/
int lcrzo_priv_global_get_intspoof_eth(const lcrzo_device device,
				       const lcrzo_etha ethaforcomp,
				       lcrzo_etha etha)
{ lcrzo_priv_staticvars *psv;
  lcrzo_uint8 intspoof_eth_type;
  int i;

  if (etha==NULL) return(LCRZO_ERR_OK);

  /* if we change the type, we have to redo the randconst */
  lcrzo_er(lcrzo_global_get_intspoof_eth_type(&intspoof_eth_type));
  lcrzo_er(lcrzo_priv_staticvars_ptr_get(&psv));
  if (intspoof_eth_type!=LCRZO_INTSPOOF_TYPE_RANDCONST)
  { psv->intspoof_eth_randconstdone=LCRZO_FALSE;
  }
  if (intspoof_eth_type!=LCRZO_INTSPOOF_TYPE_COMPCONST)
  { psv->intspoof_eth_compconstdone=LCRZO_FALSE;
  }

  switch(intspoof_eth_type)
  { case LCRZO_INTSPOOF_TYPE_RAND:
      for ( i=0 ; i<LCRZO_ETHA_MAXBYTES ; i++ )
      { lcrzo_er(lcrzo_uint8_init_rand(1, 0xFE, &(etha[i])));
      }
      break;
    case LCRZO_INTSPOOF_TYPE_RANDCONST:
      if (!psv->intspoof_eth_randconstdone)
      { for ( i=0 ; i<LCRZO_ETHA_MAXBYTES ; i++ )
        { lcrzo_er(lcrzo_uint8_init_rand(1, 0xFE,
				    &(psv->intspoof_eth_savedetha[i])));
        }
        psv->intspoof_eth_randconstdone=LCRZO_TRUE;
      }
      memcpy(etha, psv->intspoof_eth_savedetha, LCRZO_ETHA_MAXBYTES);
      break;
    case LCRZO_INTSPOOF_TYPE_USER:
      lcrzo_er(lcrzo_global_get_intspoof_eth_address(etha));
      break;
    case LCRZO_INTSPOOF_TYPE_COMP:
      if (ethaforcomp==NULL) return(LCRZO_ERR_PANULLPTR);
      etha[0]=ethaforcomp[0];
      etha[1]=ethaforcomp[1];
      for ( i=2 ; i<LCRZO_ETHA_MAXBYTES ; i++ )
      { lcrzo_er(lcrzo_uint8_init_rand(1, 0xFE, &(etha[i])));
      }
      break;
    case LCRZO_INTSPOOF_TYPE_COMPCONST:
      if (ethaforcomp==NULL) return(LCRZO_ERR_PANULLPTR);
      if (!psv->intspoof_eth_compconstdone)
      { psv->intspoof_eth_savedetha[0]=ethaforcomp[0];
        psv->intspoof_eth_savedetha[1]=ethaforcomp[1];
        for ( i=2 ; i<LCRZO_ETHA_MAXBYTES ; i++ )
        { lcrzo_er(lcrzo_uint8_init_rand(1, 0xFE, &(psv->intspoof_eth_savedetha[i])));
        }
        psv->intspoof_eth_compconstdone=LCRZO_TRUE;
      }
      memcpy(etha, psv->intspoof_eth_savedetha, LCRZO_ETHA_MAXBYTES);
      break;
    case LCRZO_INTSPOOF_TYPE_CONF:
      lcrzo_er(lcrzo_etha_init_device(device, etha));
      break;
    default:
      return(LCRZO_ERR_PAUNKNOWNTYPE);
      break;
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_global_get_intspoof_ip(const lcrzo_device device,
				      const lcrzo_ipa ipaforcomp,
                                      lcrzo_ipa ipa)
{ lcrzo_priv_staticvars *psv;
  int i;
  lcrzo_uint8 sixbits, twobits, intspoof_ip_type;

  if (ipa==NULL) return(LCRZO_ERR_OK);

  /* if we change the type, we have to redo the randconst */
  lcrzo_er(lcrzo_global_get_intspoof_ip_type(&intspoof_ip_type));
  lcrzo_er(lcrzo_priv_staticvars_ptr_get(&psv));
  if (intspoof_ip_type!=LCRZO_INTSPOOF_TYPE_RANDCONST)
  { psv->intspoof_ip_randconstdone=LCRZO_FALSE;
  }
  if (intspoof_ip_type!=LCRZO_INTSPOOF_TYPE_COMPCONST)
  { psv->intspoof_ip_compconstdone=LCRZO_FALSE;
  }

  switch(intspoof_ip_type)
  { case LCRZO_INTSPOOF_TYPE_RAND:
      for ( i=0 ; i<LCRZO_IPA_MAXBYTES ; i++ )
      { lcrzo_er(lcrzo_uint8_init_rand(1, 0xFE, &(ipa[i])));
      }
      break;
    case LCRZO_INTSPOOF_TYPE_RANDCONST:
      if (!psv->intspoof_ip_randconstdone)
      { for ( i=0 ; i<LCRZO_IPA_MAXBYTES ; i++ )
        { lcrzo_er(lcrzo_uint8_init_rand(1, 0xFE,
				    &psv->intspoof_ip_savedipa[i]));
        }
        psv->intspoof_ip_randconstdone=LCRZO_TRUE;
      }
      memcpy(ipa, psv->intspoof_ip_savedipa, LCRZO_IPA_MAXBYTES);
      break;
    case LCRZO_INTSPOOF_TYPE_USER:
      lcrzo_er(lcrzo_global_get_intspoof_ip_address(ipa));
      break;
    case LCRZO_INTSPOOF_TYPE_COMP:
      if (ipaforcomp==NULL) return(LCRZO_ERR_PANULLPTR);
      memcpy(ipa, ipaforcomp, LCRZO_IPA_MAXBYTES);
      sixbits=(lcrzo_uint8)(ipa[3]&252);
      twobits=(lcrzo_uint8)(ipa[3]&3);
      switch(twobits)
      { case 0: twobits=1; break;
        case 1: twobits=2; break;
        case 2: twobits=1; break;
        case 3: twobits=2; break;
      }
      ipa[3]= (lcrzo_uint8)(sixbits | twobits) ; 
      break;
    case LCRZO_INTSPOOF_TYPE_COMPCONST:
      if (ipaforcomp==NULL) return(LCRZO_ERR_PANULLPTR);
      if (!psv->intspoof_ip_compconstdone)
      { memcpy(psv->intspoof_ip_savedipa, ipaforcomp, LCRZO_IPA_MAXBYTES);
        sixbits=(lcrzo_uint8)(ipaforcomp[3]&252);
        twobits=(lcrzo_uint8)(ipaforcomp[3]&3);
        switch(twobits)
        { case 0: twobits=1; break;
          case 1: twobits=2; break;
          case 2: twobits=1; break;
          case 3: twobits=2; break;
	}
	psv->intspoof_ip_savedipa[3]= (lcrzo_uint8)(sixbits | twobits) ; 
        psv->intspoof_ip_compconstdone=LCRZO_TRUE;
      }
      memcpy(ipa, psv->intspoof_ip_savedipa, LCRZO_IPA_MAXBYTES);
      break;
    case LCRZO_INTSPOOF_TYPE_CONF:
      { lcrzo_int32 count;
        lcrzo_device founddevice;
	lcrzo_ipl foundipl, foundnetmask, iplforcomp;
	char *pc;
	/* convert */
	lcrzo_er(lcrzo_ipl_init_ipa(ipaforcomp, &iplforcomp));
	/* search the device */
	lcrzo_er(lcrzo_conf_devices_count(&count));
	for ( i=1 ; i<=count ; i++ )
	{ lcrzo_er(lcrzo_conf_devices_value_pos(i, founddevice, 
						NULL, &foundipl, &foundnetmask,
						NULL, NULL, NULL));
	  /* suppress ":.*$" in found device */
          pc=strchr(founddevice, ':');
	  if (pc!=NULL) *pc='\0';
 	  if ( lcrzo_device_equal(founddevice, device) )
	  { /* iplforcomp is inside range */
            if ( iplforcomp>=(foundipl&foundnetmask) &&
		 iplforcomp<=(foundipl|(~foundnetmask)) )
	    { lcrzo_er(lcrzo_ipa_init_ipl(foundipl, ipa));
	      return(LCRZO_ERR_OK);
	    }
          }
	}
	/* nothing found : use default */
	lcrzo_er(lcrzo_ipl_init_device(device, &foundipl, NULL));
	lcrzo_er(lcrzo_ipa_init_ipl(foundipl, ipa));
      }
      break;
    default:
      return(LCRZO_ERR_PAUNKNOWNTYPE);
      break;
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
#if LCRZODEF_LIBPCAPINSTALLED==1
int lcrzo_priv_datalink_verifknown(lcrzo_uint32 datalinktype)
{
  /* we cannot use a switch because the parameter is an lcrzo_uint32
     and not only an int
  */

  if ( datalinktype==DLT_NULL ||
       datalinktype==DLT_EN10MB ||
       datalinktype==DLT_PPP ||
       datalinktype==DLT_RAW
 #if defined DLT_LOOP
       || datalinktype==DLT_LOOP
 #endif
 #if defined DLT_LINUX_SLL
       || datalinktype==DLT_LINUX_SLL
 #endif
     )
  { /* ok, the values are recognized : we know how to treat */
    return(LCRZO_ERR_OK);
  }
  else
  { /* only display a warning */
    lcrzo_int16 language;
    lcrzo_er(lcrzo_global_get_language(&language));
    if (language==LCRZO_GLOBAL_LANG_FR)
    { lcrzo_uint32_print("Attention : datalinktype ",
			 datalinktype, "%u", " non reconnu.\n");
      printf("Contactez Laurent pour l'aider a traiter ce cas.\n");
      lcrzo_stdin_press("Pressez une touche pour continuer ");
    }
    else
    { lcrzo_uint32_print("Warning : datalinktype ",
			 datalinktype, "%u", " not recognized.\n");
      printf("Contact Laurent to help in removing this warning.\n");
      lcrzo_stdin_press("Press a key to continue ");
    }
    return(LCRZO_ERR_IEUNIMPLEMENTED);
  }

  return(lcrzo_ierp("lcrzo_priv_datalink_verifknown"));
}

/*-------------------------------------------------------------*/
typedef struct {
  lcrzo_uint16 pkttype;          /* packet type */
  lcrzo_uint16 hatype;           /* link-layer address type */
  lcrzo_uint16 halen;            /* link-layer address length */
  lcrzo_uint8  srcaddr[8];       /* link-layer address */
  lcrzo_uint16 protocol;         /* protocol */
} lcrzo_priv_hdrpsll;
int lcrzo_priv_datalink_modifyheader(lcrzo_uint32 datalinktype,
				     lcrzo_constdata datain,
				     lcrzo_int32 datainsize,
				     lcrzo_data *pdataout,
				     lcrzo_int32 *pdataoutsize)
{ lcrzo_int32 startip;
  lcrzo_etha ethsrc, ethdst;
  lcrzo_uint16 ethtype;
  lcrzo_bool falseeth;

#if 0
  /* debug */
  lcrzo_uint32_print("dlt=", datalinktype, "%u",
		     " [display only 32 bytes of datain]\n");
  lcrzo_data_print(datain, (datainsize<32)?datainsize:32,
		   LCRZO_PRINTTYPE_DUMP); 

#endif
 
  /* set default values */
  falseeth=LCRZO_FALSE; /* by default, do not use a false Ethernet header */
  lcrzo_er(lcrzo_etha_initdefault(ethsrc));
  lcrzo_er(lcrzo_etha_initdefault(ethdst));
  ethtype=LCRZO_HDRLETH_TYPE_IP;
  startip=0;
  
  /* select good values */
  /* we cannot use a switch because the parameter is an lcrzo_uint32
     and not only an int
  */

  /*-------------------*/
  if ( datalinktype==DLT_NULL ) {
    /* if too short, do not try to modify */
    if ( datainsize<4 ) 
    { falseeth=LCRZO_FALSE;
    }
    else
    { /* need a false Ethernet header */
      falseeth=LCRZO_TRUE;
      /* start with 4 bytes containing the ethtype in host order 
	 (but we do not know if it's the local host order) */
      if ( datain[0]==0 && datain[1]==0 && datain[2]==0 && datain[3]==0 )
      { /* should not happen, so use default */
      }
      else if ( datain[0]==0 && datain[1]==0 )
	ethtype=(lcrzo_uint16)((datain[2]<<8)|datain[3]);
      else if ( datain[2]==0 && datain[3]==0 )
	ethtype=(lcrzo_uint16)((datain[1]<<8)|datain[0]);
      else
      { /* should not happen, so use default */
      }
      /* on FreeBSD and OpenBSD, AF_xx is used */
      if (ethtype==AF_INET)
      { ethtype=LCRZO_HDRLETH_TYPE_IP;
      }
      /* offset it a 4 */
      startip=4;
    }
  }

  /*-------------------*/
  else if ( datalinktype==DLT_EN10MB ) { 
    /* do not need a false Ethernet header : data contains everything */
    falseeth=LCRZO_FALSE;
  }

  /*-------------------*/
  else if ( datalinktype==DLT_PPP ) { 
    /* XXX - I will complete this later (my firewall complicates
       the tests and I do not want to stay connected 10 hours to
       my ISP for testing purposes) */
    /* Here is how I think I will decode it :
         #include <net/if_ppp.h>
         byte1 : PPP_ADDRESS(p)
         byte2 : PPP_CONTROL(p)
         byte3,4: PPP_PROTOCOL(p) : can contain PPP_IP = 0x21
       If you think it's wrong do not hesitate to tell me. */
    falseeth=LCRZO_TRUE;
    startip=4;
  }

  /*-------------------*/
  else if ( datalinktype==DLT_RAW ) { 
    /* contain a raw IP packet */
    falseeth=LCRZO_TRUE;
  }

  /*-------------------*/
  #if defined DLT_LOOP
  else if ( datalinktype==DLT_LOOP ) { 
    /* if too short, do not try to modify */
    if ( datainsize<4 ) 
    { falseeth=LCRZO_FALSE;
    }
    else
    { /* set false Ethernet addresses */
      falseeth=LCRZO_TRUE;
      /* start with 4 bytes containing the ethtype in network order */
      if ( datain[0]==0 && datain[1]==0 && datain[2]==0 && datain[3]==0 )
      { /* should not happen, so use default */
      }
      else if ( datain[0]==0 && datain[1]==0 )
	ethtype=(datain[2]<<8)|datain[3];
      else
      { /* should not happen, so use default */
      }
      /* on FreeBSD and OpenBSD, AF_xx is used */
      if (ethtype==AF_INET)
      { ethtype=LCRZO_HDRLETH_TYPE_IP;
      }
      /* offset it a 4 */
      startip=4;
    }
  }
  #endif

  /*-------------------*/
  #if defined DLT_LINUX_SLL
  else if ( datalinktype==DLT_LINUX_SLL ) { 
    lcrzo_priv_hdrpsll hdrpsll;
    /* if too short, do not try to modify */
    if ( datainsize<16 ) 
    { falseeth=LCRZO_FALSE;
    }
    else
    { /* we remove the SLL header */
      falseeth=LCRZO_TRUE;
      memcpy(&hdrpsll, datain, 16);
      ethtype=lcrzo_ntohs(hdrpsll.protocol);
      startip=16;
     #if LCRZODEF_HAVEINCL_NETPACKET_PACKET==1
      /* try to decode Ethernet only if we have the definitions */
      /* set ethsrc */
      switch(lcrzo_ntohs(hdrpsll.hatype)) {
      case ARPHRD_ETHER :
	if ( lcrzo_ntohs(hdrpsll.halen)==LCRZO_ETHA_MAXBYTES )
	{ memcpy(ethsrc, hdrpsll.srcaddr, LCRZO_ETHA_MAXBYTES);
	}
	break;
      }  
      /* set ethdst */
      switch(lcrzo_ntohs(hdrpsll.pkttype)) {
      case PACKET_LOOPBACK :
	/* use default value : all 0's */
	break;
      case PACKET_BROADCAST :
	memset(ethdst, 0xFF, LCRZO_ETHA_MAXBYTES);
	break;
      default :
	ethdst[0]=lcrzo_ntohs(hdrpsll.pkttype);
	break;
      }
     #endif
    }
  }
  #endif

  /*-------------------*/
  else { 
    /* for the moment, do not support other types */
    falseeth=LCRZO_FALSE;
  }

  /*-------------------*/
  /*-------------------*/
  if ( falseeth && datainsize>=startip )
  { /* modify header */
    if ( pdataoutsize!=NULL )
    { *pdataoutsize=datainsize+14-startip;
    }
    if ( pdataout!=NULL )
    { lcrzo_er(lcrzo_data_alloc(datainsize+14-startip, pdataout));
      /* set Ethernet addresses of Ethernet header */
      memcpy(*pdataout, ethdst, 6);
      memcpy(*pdataout+6, ethsrc, 6);
      /* set the ethtype */
      (*pdataout)[12]=(lcrzo_uint8)(0xFF&(ethtype>>8));
      (*pdataout)[13]=(lcrzo_uint8)(0xFF&ethtype);
      /* set data */
      memcpy(*pdataout+14, datain+startip, datainsize-startip);
    }
  }
  else
  { /* do not modify header */
    lcrzo_er(lcrzo_data_initm_data(datain, datainsize,
				   pdataout, pdataoutsize));
  }

  return(LCRZO_ERR_OK);
}
#endif

/*-------------------------------------------------------------*/
int lcrzo_priv_checkpcapincluded(lcrzo_conststring functionname)
{ lcrzo_string errmsg;
  lcrzo_int16 language;

  lcrzo_er(lcrzo_global_get_language(&language));
  if (language==LCRZO_GLOBAL_LANG_FR)
  { lcrzo_string_initm_text("Fonction ", &errmsg);
    lcrzo_string_appendm_text(functionname, &errmsg);
    lcrzo_string_appendm_text(" non implementee car ", &errmsg);
    lcrzo_string_appendm_text("libpcap n'est pas installe. Vous devez ", &errmsg);
    lcrzo_string_appendm_text("installer libpcap et regenerer liblcrzo", &errmsg);
    lcrzo_string_appendm_text(" (n'oubliez pas de mettre LCRZODEF_LIBPCAPINSTALLED=1 dans config.dat).", &errmsg);
  }
  else 
  { lcrzo_string_initm_text("Function ", &errmsg);
    lcrzo_string_appendm_text(functionname, &errmsg);
    lcrzo_string_appendm_text(" unimplemented because ", &errmsg);
    lcrzo_string_appendm_text("libpcap is not installed. You should install ", &errmsg);
    lcrzo_string_appendm_text("libpcap and generate a new liblcrzo", &errmsg);
    lcrzo_string_appendm_text(" (don't forget to set LCRZODEF_LIBPCAPINSTALLED=1 in config.dat).", &errmsg);
  }
  lcrzo_er(lcrzo_priv_global_set_errmsglcrzo(errmsg));
  lcrzo_string_free(errmsg);

  return (LCRZO_ERR_IEUNIMPLEMENTED);
}
  
/*-------------------------------------------------------------*/
int lcrzo_priv_strcasecmp(lcrzo_conststring s1, lcrzo_conststring s2)
{ const char *p1 = (const char *) s1;
  const char *p2 = (const char *) s2;
  lcrzo_uint8 c1, c2;

  if (p1 == p2)
    return 0;

  do
  { c1 = (lcrzo_uint8)tolower(*p1++);
    c2 = (lcrzo_uint8)tolower(*p2++);
    if (c1 == '\0') break;
  }
  while (c1 == c2);

  return (c1 - c2);
}

/*-------------------------------------------------------------*/
/*a valid format is "%[0+. -]{0,1}[1-9]{0,2}[douxX]"*/
int lcrzo_priv_int_fmt_check(lcrzo_conststring fmt,
			     lcrzo_bool addal,
			     lcrzo_string *pfmt)
{ char *pc;
  char c;
  char formatloc[10], formatloc2[10];
  lcrzo_string outputfmt;

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

  /* fmt should not already contain a 'l' */
  pc=strchr(fmt, 'l');
  if (pc!=NULL) return(LCRZO_ERR_FMINTFMTL);

  /*le premier caractere doit etre %*/
  if ( fmt[0]!='%' )
  { return(LCRZO_ERR_FMINTFMT);
  }
  /*le dernier caractere doit etre douxX*/
  c=fmt[strlen(fmt)-1];
  if ( c!='d' && c!='o' && c!='u' && c!='x' && c!='X' )
  { return(LCRZO_ERR_FMINTFMT);
  }
  /*la taille totale du format ne doit pas depasser 1+1+2+1=5*/
  if ( strlen(fmt)>5 )
  { return(LCRZO_ERR_FMINTFMT);
  }

  /* initialize the output format */
  lcrzo_er(lcrzo_string_initm_text("%", &outputfmt));

  /*maintenant, il n'y a que les champs optionnels du milieu :
    "[0+-.]{0,1}[1-9]{0,2}"
  */
  lcrzo_er(lcrzo_string_init_text(fmt+1, 9, formatloc));
  formatloc[strlen(formatloc)-1]='\0';
  /*il y a des options : on les verifie avant de continuer*/
  if ( strlen(formatloc) )
  { /*la premiere option peut etre 0+-.*/
    c=formatloc[0];
    if ( c=='0' || c=='+' || c==' ' || c=='.' || c=='-' )
    { lcrzo_er(lcrzo_string_init_text(formatloc+1, 9, formatloc2));
      lcrzo_er(lcrzo_string_init_text(formatloc2, 9, formatloc));
      lcrzo_er(lcrzo_string_appendm_char(c, 1, &outputfmt));
    }
  }

  if ( strlen(formatloc) )
  { /*ensuite, on peut avoir 2 chiffres*/
    c=formatloc[0];
    if ( c>='0' && c<='9' )
    { lcrzo_er(lcrzo_string_init_text(formatloc+1, 9, formatloc2));
      lcrzo_er(lcrzo_string_appendm_char(c, 1, &outputfmt));
      lcrzo_er(lcrzo_string_init_text(formatloc2, 9, formatloc));
    }
    c=formatloc[0];
    if ( c>='0' && c<='9' )
    { lcrzo_er(lcrzo_string_init_text(formatloc+1, 9, formatloc2));
      lcrzo_er(lcrzo_string_init_text(formatloc2, 9, formatloc));
      lcrzo_er(lcrzo_string_appendm_char(c, 1, &outputfmt));
    }
  }

  /*il ne doit plus rien rester*/
  if ( strlen(formatloc) )
  { lcrzo_string_free(outputfmt);
    return(LCRZO_ERR_FMINTFMT);
  }

  /* eventually add the 'l' */
  if (addal)
  { lcrzo_er(lcrzo_string_appendm_text("l", &outputfmt));
  }

  /* add the last character */
  c=fmt[strlen(fmt)-1];
  lcrzo_er(lcrzo_string_appendm_char(c, 1, &outputfmt));

  /* set output */
  if (pfmt==NULL)
  { lcrzo_string_free(outputfmt);
  }
  else
  { *pfmt=outputfmt;
  }

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_write_notwroteall(lcrzo_int32 shouldwrite,
				 lcrzo_int32 reallywrote);
int lcrzo_priv_write_notwroteall(lcrzo_int32 shouldwrite,
				 lcrzo_int32 reallywrote)
{ lcrzo_int16 language;
  lcrzo_string errmsg;

  lcrzo_er(lcrzo_global_get_language(&language));
  
  if (language==LCRZO_GLOBAL_LANG_FR)
  { lcrzo_string_initm_text("la fonction write() a ecrit seulement ", &errmsg);
    lcrzo_string_appendm_int(reallywrote, "%d", &errmsg);
    lcrzo_string_appendm_text(" octets au lieu d'ecrire ", &errmsg);
    lcrzo_string_appendm_int(shouldwrite, "%d", &errmsg);
    lcrzo_string_appendm_text(" octets.", &errmsg);
  }
  else
  { lcrzo_string_initm_text("write() wrote only ", &errmsg);
    lcrzo_string_appendm_int(reallywrote, "%d", &errmsg);
    lcrzo_string_appendm_text(" bytes over ", &errmsg);
    lcrzo_string_appendm_int(shouldwrite, "%d", &errmsg);
    lcrzo_string_appendm_text(" bytes.", &errmsg);
  }

  lcrzo_er(lcrzo_priv_global_set_errmsglcrzo(errmsg));
  lcrzo_er(lcrzo_string_free(errmsg));
  return(LCRZO_ERR_FUWRITE);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_fd_write(int fd,
		        lcrzo_constdata data,
		        lcrzo_int32 datasize)
{ int retour;
  lcrzo_int32 blocksize, i;
  
  /*parameters verification*/
  if (data==NULL && datasize) return(LCRZO_ERR_PANULLPTRSIZE);
  if (datasize<0) return(LCRZO_ERR_PATOOLOW);

  /*nothing to write*/
  if (data==NULL || datasize==0) return(LCRZO_ERR_OK);

  for ( i=0 ; i<datasize ; )
  { /*determine block size*/
    blocksize=datasize-i;
    if (blocksize>LCRZO_PRIV_WRITEMAXMSGSIZE)
      blocksize=LCRZO_PRIV_WRITEMAXMSGSIZE;
    /*write a block*/
    retour=LAURENT_write(fd, data+i, blocksize);
    if (retour==-1)
    { if (errno==EBADF) 
      { errno=0;
        return(LCRZO_ERR_LONOTOPENFORWRITING);
      }
      return(LCRZO_ERR_FUWRITE);
    }
    /*update the current position*/
    i+=retour;
  }

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_fd_write_msg(int fd,
		            lcrzo_constdata data,
		            lcrzo_int32 datasize)
{ int retour;
  
  /*parameters verification*/
  if (data==NULL && datasize) return(LCRZO_ERR_PANULLPTRSIZE);
  if (datasize<0) return(LCRZO_ERR_PATOOLOW);

  /*nothing to write*/
  if (data==NULL || datasize==0) return(LCRZO_ERR_OK);

  /*write a block*/
  retour=LAURENT_write(fd, data, datasize);
  if (retour==-1)
  { if (errno==EBADF) 
    { errno=0;
      return(LCRZO_ERR_LONOTOPENFORWRITING);
    }
    return(LCRZO_ERR_FUWRITE);
  }

  if (retour!=(int)datasize)
    return(lcrzo_priv_write_notwroteall(datasize, retour));

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_fd_send(int fd,
		       lcrzo_constdata data,
		       lcrzo_int32 datasize)
{ int retour;
  lcrzo_int32 blocksize, i;
  
  /*parameters verification*/
  if (data==NULL && datasize) return(LCRZO_ERR_PANULLPTRSIZE);
  if (datasize<0) return(LCRZO_ERR_PATOOLOW);

  /*nothing to write*/
  if (data==NULL || datasize==0) return(LCRZO_ERR_OK);

  for ( i=0 ; i<datasize ; )
  { /*determine block size*/
    blocksize=datasize-i;
    if (blocksize>LCRZO_PRIV_WRITEMAXMSGSIZE)
      blocksize=LCRZO_PRIV_WRITEMAXMSGSIZE;
    /*write a block*/
    retour=LAURENT_send(fd, data+i, blocksize, 0);
    if (retour==-1)
    { if (errno==EBADF) 
      { errno=0;
        return(LCRZO_ERR_LONOTOPENFORWRITING);
      }
      return(LCRZO_ERR_FUWRITE);
    }
    /*update the current position*/
    i+=retour;
  }

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_fd_send_msg(int fd,
		           lcrzo_constdata data,
		           lcrzo_int32 datasize)
{ int retour;
  
  /*parameters verification*/
  if (data==NULL && datasize) return(LCRZO_ERR_PANULLPTRSIZE);
  if (datasize<0) return(LCRZO_ERR_PATOOLOW);

  /*nothing to write*/
  if (data==NULL || datasize==0) return(LCRZO_ERR_OK);

  /*write a block*/
  retour=LAURENT_send(fd, data, datasize, 0);
  if (retour==-1)
  { if (errno==EBADF) 
    { errno=0;
      return(LCRZO_ERR_LONOTOPENFORWRITING);
    }
    return(LCRZO_ERR_FUWRITE);
  }

  if (retour!=(int)datasize)
    return(lcrzo_priv_write_notwroteall(datasize, retour));

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_wait_fd_wait(int fd, 
                            lcrzo_time *pmaxwaitingtime, 
			    lcrzo_bool *pevent)
#if LCRZODEF_HAVEFONC_POLL==1
#include <sys/poll.h>
{ struct pollfd pollarray[1];
  lcrzo_uint32 timeoutuint;
  lcrzo_int32 timeoutint;
  int ret, mask;

  /* set wanted mask */
#if ! defined POLLRDNORM
 #define POLLRDNORM 0
#endif
#if ! defined POLLRDBAND
 #define POLLRDBAND 0
#endif
  mask = POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND | POLLHUP;

  /* set fd */
  pollarray[0].fd=fd;
  pollarray[0].events=mask;
  pollarray[0].revents=0;

  /* set timeout */
  if ( pmaxwaitingtime->sec==LCRZO_WAIT_NODELAY_VAL && 
       pmaxwaitingtime->usec==LCRZO_WAIT_NODELAY_VAL )
  { timeoutint=0;
  }
  else if ( pmaxwaitingtime->sec==LCRZO_WAIT_INFINITE_VAL && 
            pmaxwaitingtime->usec==LCRZO_WAIT_INFINITE_VAL )
  { timeoutint=-1;
  }
  else
  { ret=lcrzo_time_get_msec(pmaxwaitingtime, &timeoutuint);
    if (ret==LCRZO_ERR_OK)
    { if (timeoutuint>0x7FFFFFFF) timeoutint=-1;
      else timeoutint=timeoutuint;
    }
    else if (ret==LCRZO_ERR_SPTIMEMSECOVER)
    { timeoutint=-1;
    }
    else
      return(ret);    
  }

  /* wait */
  ret=poll(pollarray, 1, timeoutint);
  if (ret<0) return(LCRZO_ERR_FUPOLL);

  /* set error values */
  if (ret==0)
  { if (pevent!=NULL) *pevent=LCRZO_FALSE;
    return(LCRZO_ERR_OK);
  }

  /* set normal values */
  if (pevent!=NULL) 
  { if ( pollarray[0].revents&mask )
      *pevent=LCRZO_TRUE;
    else
      *pevent=LCRZO_FALSE;
  }
  return(LCRZO_ERR_OK);
}
#else
{ lcrzo_bool event;
  fd_set readfdset, exceptfdset;
  struct timeval timeouttv, *ptv;
  int ret;

  /* initialize fd */
  FD_ZERO(&readfdset);
  FD_ZERO(&exceptfdset);
  FD_SET(fd, &readfdset);
  FD_SET(fd, &exceptfdset);

  /* set timeout */
  ptv=&timeouttv;
  if ( pmaxwaitingtime->sec==LCRZO_WAIT_NODELAY_VAL && 
       pmaxwaitingtime->usec==LCRZO_WAIT_NODELAY_VAL )
  { timeouttv.tv_sec=0;
    timeouttv.tv_usec=0;
  }
  else if ( pmaxwaitingtime->sec==LCRZO_WAIT_INFINITE_VAL && 
            pmaxwaitingtime->usec==LCRZO_WAIT_INFINITE_VAL )
  { ptv=NULL;
  }
  else
  { timeouttv.tv_sec=pmaxwaitingtime->sec;
    timeouttv.tv_usec=pmaxwaitingtime->usec;
  }

  /* select */
  ret=select(fd+1, &readfdset, NULL, &exceptfdset, ptv);
  if (ret<0) return(LCRZO_ERR_FUSELECT);

  /* check output */
  event=LCRZO_FALSE;
  if ( FD_ISSET(fd, &readfdset) ||
       FD_ISSET(fd, &exceptfdset) )
  { event=LCRZO_TRUE;
  }

  if (pevent!=NULL) *pevent=event;
  return(LCRZO_ERR_OK);
}
#endif

/*-------------------------------------------------------------*/
/* Developper Note : unicode devices names
   Actually, lcrzo doesn't support unicode devices names.
   However, I do not understand why Packet.dll supports them because
   registery key names are not in unicode ???
*/
int lcrzo_priv_device_init_unicode(lcrzo_constdata deviceunicode,
                                   lcrzo_device devicestr)
{ lcrzo_uint8 c, d;
  char x, y;
  int i, j;
  
  i=j=0;
#define LCRZO_PRIV_DEVICE_UNICODE_DEBUG 0
#if LCRZO_PRIV_DEVICE_UNICODE_DEBUG==1
  devicestr[j++]='$';
#endif
  while( ! (deviceunicode[i]==0 && deviceunicode[i+1]==0) )
  { if ( i > LCRZO_DEVICE_MAXBYTES ) return(LCRZO_ERR_PAOVERFLOW);
    if ( devicestr != NULL )
    { c=deviceunicode[i];
      d=deviceunicode[i+1];
      if (d==0)
      { 
        if (c=='%')
        { devicestr[j++]='%';
          devicestr[j++]='2';
          devicestr[j++]='5';
          devicestr[j++]='0';
          devicestr[j]='0';
        }
        else
        { devicestr[j]=c;
        }
      }
      else
      { devicestr[j++]='%';
        x=(char)(c/16);
        y=(char)(c-x);
        x=(char)((x<=9)?'0'+x:'a'+x-10);
        y=(char)((y<=9)?'0'+y:'a'+y-10);
        devicestr[j++]=x;
        devicestr[j++]=y;
        x=(char)(d/16);
        y=(char)(d-x);
        x=(char)((x<=9)?'0'+x:'a'+x-10);
        y=(char)((y<=9)?'0'+y:'a'+y-10);
        devicestr[j++]=x;
        devicestr[j]=y;
      }
    }
    i+=2;
    j++;
  }
  devicestr[j]='\0';

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_device_init_ascii(lcrzo_conststring deviceascii,
                                 lcrzo_device devicestr)
{ lcrzo_uint8 c;
  int i, j;
  
  i=j=0;
#if LCRZO_PRIV_DEVICE_UNICODE_DEBUG==1
  devicestr[j++]='$';
#endif
  while( deviceascii[i]!=0 )
  { if ( i > LCRZO_DEVICE_MAXBYTES ) return(LCRZO_ERR_PAOVERFLOW);
    if ( devicestr != NULL )
    { c=deviceascii[i];
      if (c=='%')
      { devicestr[j++]='%';
        devicestr[j++]='2';
        devicestr[j++]='5';
        devicestr[j++]='0';
        devicestr[j]='0';
      }
      else
      { devicestr[j]=c;
      }
    }
    i++;
    j++;
  }
  devicestr[j]='\0';

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_unicode_initm_device(lcrzo_constdevice devicestr,
                                    lcrzo_data *pdeviceunicode)
{ lcrzo_data data;
  lcrzo_uint8 c;
  char x, y;
  int i, j;
  
  i=0;
#if LCRZO_PRIV_DEVICE_UNICODE_DEBUG==1
  if (devicestr[0]=='$')
  { i=1;
  }
#endif
  lcrzo_er(lcrzo_data_alloc(2*(strlen(devicestr)+1), &data));

  j=0;
  while ( (c=devicestr[i]) != '\0' )
  { if (c=='%')
    { i++;
      x=devicestr[i++];
      y=devicestr[i++];
      x=(char)((x>='0'&&x<='9')?x-'9':x-'a'+10);
      y=(char)((y>='0'&&y<='9')?y-'9':y-'a'+10);
      data[j++]=(char)(x|y);
      x=devicestr[i++];
      y=devicestr[i++];
      x=(char)((x>='0'&&x<='9')?x-'9':x-'a'+10);
      y=(char)((y>='0'&&y<='9')?y-'9':y-'a'+10);
      data[j++]=(char)(x|y);
    }
    else
    { data[j++]=c;
      data[j++]=0;
    }
    i++;
  }
  data[j++]=0;
  data[j]=0;

  if (pdeviceunicode==NULL) lcrzo_data_free(data);
  else *pdeviceunicode=data;

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_ascii_init_device(lcrzo_constdevice devicestr,
                                 lcrzo_device deviceascii)
{ lcrzo_uint8 c;
  char x, y;
  int i, j;
  
  i=0;
#if LCRZO_PRIV_DEVICE_UNICODE_DEBUG==1
  if (devicestr[0]=='$')
  { i=1;
  }
#endif

  j=0;
  while ( (c=devicestr[i]) != '\0' )
  { if (c=='%')
    { i++;
      x=devicestr[i++];
      y=devicestr[i++];
      x=(char)((x>='0'&&x<='9')?x-'9':x-'a'+10);
      y=(char)((y>='0'&&y<='9')?y-'9':y-'a'+10);
      if (deviceascii!=NULL) deviceascii[j++]=(char)(x|y);
      x=devicestr[i++];
      y=devicestr[i++];
      /* x and y should be 0, do not check and generate error */
    }
    else
    { if (deviceascii!=NULL) deviceascii[j++]=c;
    }
    i++;
  }
  if (deviceascii!=NULL) deviceascii[j]=0;

  return(LCRZO_ERR_OK);
}

