/*-----------------------------------------------------------------*/
/*! 
  \file scscpio.cxx
  \brief SCSCP I/O function
  \author  M. Gastineau 
           Astronomie et Systemes Dynamiques, IMCCE, CNRS, Observatoire de Paris. 

   Copyright, 2008, 2009, 2012, M. Gastineau, IMCCE-CNRS
   email of the author : gastineau@imcce.fr

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

/*-----------------------------------------------------------------*/
/* License  of this file :
 This file is "dual-licensed", you have to choose one  of the two licenses 
 below to apply on this file.
 
    CeCILL-C
    	The CeCILL-C license is close to the GNU LGPL.
    	( http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html )
 
 or CeCILL v2.0
      The CeCILL license is compatible with the GNU GPL.
      ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html )
 

This library is governed by the CeCILL-C or the CeCILL license under 
French law and abiding by the rules of distribution of free software.  
You can  use, modify and/ or redistribute the software under the terms 
of the CeCILL-C or CeCILL license as circulated by CEA, CNRS and INRIA  
at the following URL "http://www.cecill.info". 

As a counterpart to the access to the source code and  rights to copy,
modify and redistribute granted by the license, users are provided only
with a limited warranty  and the software's author,  the holder of the
economic rights,  and the successive licensors  have only  limited
liability. 

In this respect, the user's attention is drawn to the risks associated
with loading,  using,  modifying and/or developing or reproducing the
software by the user in light of its specific status of free software,
that may mean  that it is complicated to manipulate,  and  that  also
therefore means  that it is reserved for developers  and  experienced
professionals having in-depth computer knowledge. Users are therefore
encouraged to load and test the software's suitability as regards their
requirements in conditions enabling the security of their systems and/or 
data to be ensured and,  more generally, to use and operate it in the 
same conditions as regards security. 

The fact that you are presently reading this means that you have had
knowledge of the CeCILL-C or CeCILL license and that you accept its terms.
*/
/*-----------------------------------------------------------------*/

#if HAVE_CONFIG_H
#include "scscpconfig.h"
#endif /*HAVE_CONFIG_H*/
#if HAVE_WINDOWS_H
#include <windows.h>
#endif /*HAVE_CONFIG_H*/
#include <sys/types.h>
#if HAVE_STRING_H
#include <string.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_STDINT_H
#include <stdint.h>
#endif
#if HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#include <stdarg.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#define __SCSCP_WITHIN_SCSCP
#include "scscp.h"
#include "scscpinternal.h"
#include "scscpdebug.h"
#include "scscpfileclient.h"
#include "scscpfileserver.h"
#include "float.h"

/*-----------------------------------------------------------------*/
/* private functions */
/*-----------------------------------------------------------------*/
static int SCSCP_io_checkNULL(SCSCP_io* stream, SCSCP_status* status);
static int SCSCP_io_write_binarytagscharid(SCSCP_io* stream, unsigned char tags, const char *buffer, const char *id, SCSCP_status* status);
static int SCSCP_io_write_binarytagscharid2(SCSCP_io* stream, unsigned char tags, const char *buffer1, const char *buffer2, const char *id, SCSCP_status* status);
static int SCSCP_io_writeOMIchar_binary(SCSCP_io* stream, char x, SCSCP_status* status);
static int SCSCP_io_writeOMIint32_binary(SCSCP_io* stream, int32_t x, SCSCP_status* status);
static int SCSCP_io_write_XMLbegin(SCSCP_io* stream, const char *tag, const char *id, SCSCP_status* status);
static int SCSCP_io_write_binarybegin(SCSCP_io* stream, unsigned char tag, const char *id, SCSCP_status* status);

/*-----------------------------------------------------------------*/
/* private defines */
/*-----------------------------------------------------------------*/
/*! long flag set for binary encoding */
#define SCSCP_BINARY_SETLONGFLAG 128

/*-----------------------------------------------------------------*/
/*! Set the long flag if buffer is >256.
  and write the tags and buffer for the binary encoding.
 return 0 on error
 @param stream (inout) scscp stream
 @param tags (in) token identifier
 @param buffer (in) some data
 @param id (in) sharing id (could be NULL)
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
static int SCSCP_io_write_binarytagscharid(SCSCP_io* stream, unsigned char tags, const char *buffer, const char *id, SCSCP_status* status)
{
 size_t l0=strlen(buffer);
 uint32_t l=(uint32_t)l0;
 int res;
 size_t lenid=(id==NULL?0:strlen(id));
 uint32_t lenid32=(uint32_t)lenid;
 
 if (id!=NULL) tags+=64;
 if (l>=256 || lenid>=256) 
 {
  tags+=SCSCP_BINARY_SETLONGFLAG;  
  res = SCSCP_io_writelen(stream, &tags, sizeof(tags), status);
  l = htonl(l);
  if (res) res = SCSCP_io_writelen(stream, &l, sizeof(l), status);
  if (id!=NULL)
  {
   lenid32 = htonl(lenid32);
   if (res) res = SCSCP_io_writelen(stream, &lenid32, sizeof(lenid32), status);
  }
 }
 else
 {
  unsigned char sl = (unsigned char)l;
  res = SCSCP_io_writelen(stream, &tags, sizeof(tags), status);
  if (res) res = SCSCP_io_writelen(stream, &sl, sizeof(sl), status);
  if (id!=NULL)
  {
   unsigned char slenid = (unsigned char)lenid;
   if (res) res = SCSCP_io_writelen(stream, &slenid, sizeof(slenid), status);
  }
 }
 if (res) res = SCSCP_io_writelen(stream, buffer, l0, status);
 if (res && id!=NULL) res = SCSCP_io_writelen(stream, id, lenid, status);
 return res;
}

/*-----------------------------------------------------------------*/
/*! Set the long flag if buffer1 or buffer2 is >256.
  and write the tags and buffer1, and buffer2 for the binary encoding.
 return 0 on error
 @param stream (inout) scscp stream
 @param tags (in) token identifier
 @param buffer1 (in) some data
 @param buffer2 (in) some data
 @param id (in) sharing id (could be NULL)
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
static int SCSCP_io_write_binarytagscharid2(SCSCP_io* stream, unsigned char tags,  const char *buffer1, const char *buffer2, const char *id, SCSCP_status* status)
{
 uint32_t l1=(uint32_t)strlen(buffer1);
 uint32_t l2=(uint32_t)strlen(buffer2);
 int res;
 size_t lenid=(id==NULL?0:strlen(id));
 uint32_t lenid32=(uint32_t)lenid;

 if (id!=NULL) tags+=64;
 if (l1>=256 || l2>=256 || lenid>=256) 
 {
  tags+=SCSCP_BINARY_SETLONGFLAG;
  res = SCSCP_io_writelen(stream, &tags, sizeof(tags), status);
  l1 = htonl(l1);
  if (res) res = SCSCP_io_writelen(stream, &l1, sizeof(l1), status);
  l2 = htonl(l2);
  if (res) res = SCSCP_io_writelen(stream, &l2, sizeof(l2), status);
  if (id!=NULL)
  {
   lenid32 = htonl(lenid32);
   if (res) res = SCSCP_io_writelen(stream, &lenid32, sizeof(lenid32), status);
  }
 }
 else
 {
  unsigned char sl1 = (unsigned char)l1;
  unsigned char sl2 = (unsigned char)l2;
  res = SCSCP_io_writelen(stream, &tags, sizeof(tags), status);
  if (res) res = SCSCP_io_writelen(stream, &sl1, sizeof(sl1), status);
  if (res) res = SCSCP_io_writelen(stream, &sl2, sizeof(sl2), status);
  if (id!=NULL)
  {
   unsigned char slenid = (unsigned char)lenid;
   if (res) res = SCSCP_io_writelen(stream, &slenid, sizeof(slenid), status);
  }
 }
 if (res) res = SCSCP_io_writelen(stream, buffer1, l1, status);
 if (res) res = SCSCP_io_writelen(stream, buffer2, l2, status);
 if (res && id!=NULL) res = SCSCP_io_writelen(stream, id, lenid, status);
 return res;
}

/*-----------------------------------------------------------------*/
/*! write the beginning OpenMath tag with the id directly to the SCSCP client or server 
  usign the BINARY encoding. "id" follows the "tags".
 return 0 on error
 @param stream (inout) scscp stream
 @param tags (in) binary tag
 @param id (in) reference id (could be NULL)
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
static int SCSCP_io_write_binarybegin(SCSCP_io* stream, unsigned char tags, const char *id, SCSCP_status* status)
{
 int res;
  if (id==NULL)
  {
   res = SCSCP_io_writelen(stream, &tags, sizeof(tags), status);
  }
  else
  {
   size_t lenid = strlen(id);
   tags += 64U;
   if (lenid>=256) 
   { /* id is longer than a 256 bytes */
    uint32_t lenid32 = (uint32_t)lenid;
    lenid32 = htonl(lenid32);
    tags += SCSCP_BINARY_SETLONGFLAG;
    res = SCSCP_io_writelen(stream, &tags, sizeof(tags), status);
    if (res) res = SCSCP_io_writelen(stream, &lenid32, sizeof(lenid32), status);
   }
   else
   { /* id is shorter or equal than a 256 bytes */
    unsigned char sl1 = (unsigned char)lenid;
    res = SCSCP_io_writelen(stream, &tags, sizeof(tags), status);
    if (res) res = SCSCP_io_writelen(stream, &sl1, sizeof(sl1), status);
   }
   if (res) res = SCSCP_io_write(stream, id, status);
  }

 return res;
}

/*-----------------------------------------------------------------*/
/*! write x as an OpenMath integer in binary encoding using one byte directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param x (in) some data
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
static int SCSCP_io_writeOMIchar_binary(SCSCP_io* stream, char x, SCSCP_status* status)
{
   int res;
   const unsigned char tags = 1;
   SCSCP_debugprint("SCSCP_io_writeOMIchar_binary(%p,%d,%d) - enter\n", stream, (int)x, SCSCP_status_is(status));
   res = SCSCP_io_writelen(stream, &tags, sizeof(tags), status);
   if (res) res = SCSCP_io_writelen(stream, &x, sizeof(x), status);
   SCSCP_debugprint("SCSCP_io_writeOMIchar_binary() - return %d\n", res);
   return res;
}

/*-----------------------------------------------------------------*/
/*! write x as an OpenMath integer in binary encoding using 4 bytes directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param x (in) some data
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
static int SCSCP_io_writeOMIint32_binary(SCSCP_io* stream, int32_t x, SCSCP_status* status)
{
   int res;
   uint32_t ux;
   const unsigned char tags = 1+SCSCP_BINARY_SETLONGFLAG;
   SCSCP_debugprint("SCSCP_io_writeOMIint32_binary(%p,%ld,%d) - enter\n", stream, (long)x, SCSCP_status_is(status));
   res = SCSCP_io_writelen(stream, &tags, sizeof(tags), status);
   ux = htonl((uint32_t)x);
   if (res) res = SCSCP_io_writelen(stream, &ux, sizeof(ux), status);
   SCSCP_debugprint("SCSCP_io_writeOMIint32_binary() - return %d\n", res);
   return res;
}


/*-----------------------------------------------------------------*/
/*! write some data to the SCSCP server or client 
 return 0 on error
 @param stream (inout) scscp stream
 @param buffer (in) some data
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writelen(SCSCP_io* stream, const void *buffer, size_t len, SCSCP_status* status)
{
   int res;
   res = SCSCP_fileio_write(&(*stream)->m_OutStream, buffer, len);
   if (!res) SCSCP_status_seterror(status, SCSCP_STATUS_ERRNO);
   return res;
}

/*-----------------------------------------------------------------*/
/*! write some data to the SCSCP server or client 
 return 0 on error
 @param stream (inout) scscp stream
 @param buffer (in) some data
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_write(SCSCP_io* stream, const char *buffer, SCSCP_status* status)
{
 int res = SCSCP_io_checkNULL(stream, status);
 if (res)
 {
   res = SCSCP_fileio_write(&(*stream)->m_OutStream, buffer, strlen(buffer)*sizeof(char));
   if (!res) SCSCP_status_seterror(status, SCSCP_STATUS_ERRNO);
 }
 return res;
}


/*-----------------------------------------------------------------*/
/*! flush the connection
 return 0 on error
 @param stream (inout) scscp stream
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_flush(SCSCP_io* stream, SCSCP_status* status)
{
 int res = SCSCP_io_write(stream,"\n", status);
 if (res) 
 {
  res = SCSCP_fileio_flush(&(*stream)->m_OutStream);
  if (!res) SCSCP_status_seterror(status, SCSCP_STATUS_ERRNO);
 }
 return res;
}

/*-----------------------------------------------------------------*/
/*! write some data to the SCSCP server or client 
 The list must finish a NULL pointer
 return 0 on error
 @param client (inout) scscp stream
 @param buffer (in) some data
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writef(SCSCP_io* stream,  SCSCP_status* status, const char *buffer, ...)
{
 va_list buflist;
 const char *data;
 int res = SCSCP_io_checkNULL(stream, status);
 if (res)
 {
   res = SCSCP_io_write(stream, buffer, status);
   
   va_start(buflist, buffer);
   while (res && ((data=va_arg(buflist, const char*))!=NULL)) 
   {
    res = SCSCP_io_write(stream, data, status);
   }
   va_end(buflist);
  }
  return res;
}


/*-----------------------------------------------------------------*/
/*! write the beginning OpenMath tag with the id directly to the SCSCP client or server 
  usign the XML encoding
 return 0 on error
 @param stream (inout) scscp stream
 @param tag (in) XML tag
 @param id (in) sharing id (could be NULL)
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
static int SCSCP_io_write_XMLbegin(SCSCP_io* stream, const char *tag, const char *id, SCSCP_status* status)
{
 int res;
  if (id==NULL)
  {
   res = SCSCP_io_writef(stream, status, "<", tag, ">",NULL);
  }
  else
  {
   res = SCSCP_io_writef(stream, status, "<", tag, "id=\"", id ,"\" >",NULL);
  }

 return res;
}


/*-----------------------------------------------------------------*/
/*! write buffer as an OpenMath string <OMSTR></OMSTR> directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param buffer (in) some data
 @param id (in) sharing id (could be NULL)
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writeOMSTR(SCSCP_io* stream, const char *buffer, const char *id, SCSCP_status* status)
{
 int res;
 if (buffer!=NULL)
 {
  if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) 
  {
   res = SCSCP_io_write_binarytagscharid(stream, 6, buffer, id, status);
  }
  else
  {
   res = SCSCP_io_write_XMLbegin(stream, "OMSTR", id, status);
   if (res) res =  SCSCP_io_writef(stream, status, buffer, "</OMSTR>", NULL);
  } 
 }
 else
 {
  if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) 
  {
   /* send a string of length 1 instead of 0 to avoid Openmath INRIA C library assertion */
   res = SCSCP_io_writeOMSTR(stream," ", id, status);
  }
  else
  {
   res =  SCSCP_io_write(stream, "<OMSTR></OMSTR>", status);
  }
 }
 return res;
}

/*-----------------------------------------------------------------*/
/*!  write x as an OpenMath float-point <OMF></OMF> directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param x (in) some data
 @param id (in) sharing id (could be NULL)
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writeOMFdouble(SCSCP_io* stream, double x, const char *id, SCSCP_status* status)
{
 int res;
 if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) 
 {
   double nx;
   const unsigned char tags = 3;
   res = SCSCP_io_writelen(stream, &tags, sizeof(tags), status);
   nx = SCSCP_htond(x);
   if (res) res = SCSCP_io_writelen(stream, &nx, sizeof(nx), status);
 }
 else
 {
  char valeur[400];
  sprintf(valeur,"dec=\"%+-.*E\" />",(int)DBL_DIG+1,x);
  if (id==NULL)
  {
   res = SCSCP_io_writef(stream, status, "<OMF ", NULL);
  }
  else
  {
   res = SCSCP_io_writef(stream, status, "<OMF  id=\"", id, "\" />", NULL);
  }
  if (res) res =  SCSCP_io_write(stream, valeur, status);
 }
 return res; 
}

/*-----------------------------------------------------------------*/
/*! write buffer as an OpenMath floating-point <OMF></OMF> directly to the SCSCP client or server
 return 0 on error
 @param stream (inout) scscp stream
 @param buffer (in) some data
 @param id (in) sharing id (could be NULL)
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writeOMFstr(SCSCP_io* stream, const char *buffer, const char *id, SCSCP_status* status)
{
 int res;
 if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) 
 {
  double x = strtod(buffer, (char **)NULL);
  res = SCSCP_io_writeOMFdouble(stream, x, id, status);
 }
 else
 {
  if (id==NULL)
  {
   res = SCSCP_io_writef(stream, status, "<OMF dec=\"", buffer, "\" />", NULL);
  }
  else
  {
   res = SCSCP_io_writef(stream, status, "<OMF dec=\"", buffer, "\" id=\"", id, "\" />", NULL);
  }
 }
 return res; 
}

/*-----------------------------------------------------------------*/
/*!   write x as an OpenMath integer <OMI></OMI> directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param x (in) some data
 @param id (in) sharing id (could be NULL)
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writeOMIint(SCSCP_io* stream, int x, const char *id, SCSCP_status* status)
{
 int res;
 if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) 
 {
  if (-128<=x && x<=127)
  {
   res = SCSCP_io_writeOMIchar_binary(stream, (char)x, status);
  }
  else if (INT32_MIN<=x && x<=INT32_MAX)
  {
   res = SCSCP_io_writeOMIint32_binary(stream, (int32_t)x, status);
  }
  else
  {
   char valeur[400];
   sprintf(valeur,"%d",x);
   res = SCSCP_io_writeOMIstr(stream, valeur, id, status);
  }
 }
 else
 {
  char valeur[400];
  sprintf(valeur,"%d</OMI>",x);
  res = SCSCP_io_write_XMLbegin(stream, "OMI", id, status);
  if (res) res = SCSCP_io_write(stream, valeur, status);
 }
 return res; 
}

/*-----------------------------------------------------------------*/
/*!   write x as an OpenMath integer <OMI></OMI> directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param x (in) some data
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writeOMIsizet(SCSCP_io* stream, size_t x, SCSCP_status* status)
{
 int res;
 if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) 
 {
  if (x<=127)
  {
   res = SCSCP_io_writeOMIchar_binary(stream, (char)x, status);
  }
  else if (x<=INT32_MAX)
  {
   res = SCSCP_io_writeOMIint32_binary(stream, (int32_t)x, status);
  }
  else
  {
   char valeur[400];
   sprintf(valeur,"%llu",(unsigned long long)x);
   res = SCSCP_io_writeOMIstr(stream, valeur, NULL, status);
  }
 }
 else
 {
  char valeur[400];
  sprintf(valeur,"<OMI>%llu</OMI>",(unsigned long long)x);
  res = SCSCP_io_write(stream, valeur, status);
 }
 return res; 
}

/*-----------------------------------------------------------------*/
/*! write x as an OpenMath integer <OMI></OMI> directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param x (in) some data
 @param id (in) sharing id (could be NULL)
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writeOMIlonglong(SCSCP_io* stream, long long  x, const char *id, SCSCP_status* status)
{
 int res;
 if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) 
 {
  if (-128<=x && x<=127)
  {
   res = SCSCP_io_writeOMIchar_binary(stream, (char)x, status);
  }
  else if (INT32_MIN<=x && x<=INT32_MAX)
  {
   res = SCSCP_io_writeOMIint32_binary(stream, (int32_t)x, status);
  }
  else
  {
   char valeur[400];
   sprintf(valeur,"%lld",x);
   res = SCSCP_io_writeOMIstr(stream, valeur, id, status);
  }
 }
 else
 {
  char valeur[400];
  sprintf(valeur,"%lld</OMI>",x);
  res = SCSCP_io_write_XMLbegin(stream, "OMI", id, status);
  if (res) res = SCSCP_io_write(stream, valeur, status);
 }
 return res; 
}

/*-----------------------------------------------------------------*/
/*! write buffer as an OpenMath integer <OMI></OMI> directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param buffer (in) some data
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writeOMIstr(SCSCP_io* stream, const char *buffer, const char *id, SCSCP_status* status)
{
  int res;
  SCSCP_debugprint("SCSCP_io_writeOMIstr(%p,%s,%d) -enter\n", stream, buffer, SCSCP_status_is(status));
  
  if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) 
  {
   uint32_t l0=(uint32_t)strlen(buffer);
   unsigned char tags=2;
   uint32_t l = l0;
   if (*buffer=='-' || *buffer=='+') l--;
   if (l>=256) 
   {
    tags+=SCSCP_BINARY_SETLONGFLAG;
    res = SCSCP_io_writelen(stream, &tags, sizeof(tags), status);
    l = htonl(l);
    if (res) res = SCSCP_io_writelen(stream, &l, sizeof(l), status);
   }
   else
   {
    unsigned char sl = (unsigned char)l;
    res = SCSCP_io_writelen(stream, &tags, sizeof(tags), status);
    if (res) res = SCSCP_io_writelen(stream, &sl, sizeof(sl), status);
   }
   if (res) 
   {
    if (*buffer!='-' && *buffer!='+') res = SCSCP_io_writelen(stream, "+", 1, status);
    if (res) res = SCSCP_io_writelen(stream, buffer, l0, status);
   }
  }
  else 
  { /* XML encoding */
   res = SCSCP_io_write_XMLbegin(stream, "OMI", id, status);
   if (res) res = SCSCP_io_writef(stream, status, buffer, "</OMI>", NULL);
  }
  SCSCP_debugprint("SCSCP_io_writeOMIstr() - return %d\n", res);
  return res;
}

/*-----------------------------------------------------------------*/
/*! write symbolname/cdname  as an OpenMath symbol <OMS cd="..." name="..." id="..." > 
   directly to the SCSCP client or server
 return 0 on error
 @param stream (inout) scscp stream
 @param cdname (in) cd name
 @param symbolname (in) symbol name
 @param id (in) sharing id (could be NULL) => id isn't written
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writeOMS(SCSCP_io* stream, const char* cdname, const char* symbolname, const char *id, SCSCP_status* status)
{
  int res;
  if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) 
  {
   res = SCSCP_io_write_binarytagscharid2(stream, 8, cdname, symbolname, id, status);
  }
  else 
  { /* XML encoding */
   if (id==NULL)
   {
    res = SCSCP_io_writef(stream, status, "<OMS cd=\"", cdname, "\" name=\"", symbolname, "\" />", NULL);
   }
   else
   {
    res = SCSCP_io_writef(stream, status, "<OMS cd=\"", cdname, "\" name=\"", symbolname, "\" id=\"", id, "\" />", NULL);
   }
  }
  return res;
}

/*-----------------------------------------------------------------*/
/*! write buffer as an OpenMath variable <OMV name="..." id="..." /> directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param buffer (in) some data
 @param id (in) sharing id (could be NULL)
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writeOMV(SCSCP_io* stream, const char *buffer, const char *id, SCSCP_status* status)
{
  int res;
  if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) 
  {
   res = SCSCP_io_write_binarytagscharid(stream, 5, buffer, id, status);
  }
  else 
  { /* XML encoding */
   if (id==NULL)
   {
    res = SCSCP_io_writef(stream, status, "<OMV name=\"", buffer, "\" />", NULL);
   }
   else
   {
    res = SCSCP_io_writef(stream, status, "<OMV name=\"", buffer, "\" id=\"", id, "\" />", NULL);
   }
  }
  return res;
}

/*-----------------------------------------------------------------*/
/*! write buffer as an OpenMath  reference <OMR href="..."/> directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param buffer (in) some data
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writeOMR(SCSCP_io* stream, const char *buffer, SCSCP_status* status)
{
  int res;
  if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) 
  {
   res = SCSCP_io_write_binarytagscharid(stream, 31, buffer, NULL, status);
  }
  else 
  { /* XML encoding */
   res = SCSCP_io_writef(stream, status, "<OMR href=\"", buffer, "\" />", NULL);
  }
  return res;
}

/*-----------------------------------------------------------------*/
/*! write the beginning OpenMath tag <OMA>  directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param id (in) sharing id (could be NULL)
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writebeginOMA(SCSCP_io* stream, const char *id, SCSCP_status* status)
{
 int res;
 
 if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) 
 {
  res = SCSCP_io_write_binarybegin(stream, 16U, id, status);
 }
 else
 {
  res = SCSCP_io_write_XMLbegin(stream, "OMA", id, status);
 }

 return res;
}

/*-----------------------------------------------------------------*/
/*! write the end OpenMath tag </OMA>  directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writeendOMA(SCSCP_io* stream, SCSCP_status* status)
{
 const char binaryencoding[] = { 17,'\0'};
 const char* xmlencoding="</OMA>";
 const char *encoding = xmlencoding;
 
 if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) encoding = binaryencoding;

 return SCSCP_io_write(stream, encoding, status);
}

/*-----------------------------------------------------------------*/
/*! write the beginning OpenMath tag <OME>  directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param id (in) reference id (could be NULL)
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writebeginOME(SCSCP_io* stream, const char *id, SCSCP_status* status)
{
 int res;
 
 if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) 
 {
  res = SCSCP_io_write_binarybegin(stream, 22U, id, status);
 }
 else
 {
  res = SCSCP_io_write_XMLbegin(stream, "OME", id, status);
 }

 return res;
}

/*-----------------------------------------------------------------*/
/*! write the end OpenMath tag </OME>  directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writeendOME(SCSCP_io* stream, SCSCP_status* status)
{
 const char binaryencoding[] = { 23,'\0'};
 const char* xmlencoding="</OME>";
 const char *encoding = xmlencoding;
 
 if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) encoding = binaryencoding;

 return SCSCP_io_write(stream, encoding, status);
}


/*-----------------------------------------------------------------*/
/*! write the beginning OpenMath tag <OMOBJ>  directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writebeginOMOBJ(SCSCP_io* stream, SCSCP_status* status)
{
 const char binaryencoding[] = { 24,'\0'};
 const char* xmlencoding="<OMOBJ>";
 const char *encoding = xmlencoding;
 
 if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) encoding = binaryencoding;

 return SCSCP_io_write(stream, encoding, status);
}

/*-----------------------------------------------------------------*/
/*! write the end OpenMath tag </OMOBJ>  directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writeendOMOBJ(SCSCP_io* stream, SCSCP_status* status)
{
 const char binaryencoding[] = { 25,'\0'};
 const char* xmlencoding="</OMOBJ>";
 const char *encoding = xmlencoding;
 
 if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) encoding = binaryencoding;

 return SCSCP_io_write(stream, encoding, status);
}


/*-----------------------------------------------------------------*/
/*! write the beginning OpenMath tag <OMBIND> directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param id (in) reference id (could be NULL)
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writebeginOMBIND(SCSCP_io* stream, const char *id, SCSCP_status* status)
{
 int res;
 
 if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) 
 {
  res = SCSCP_io_write_binarybegin(stream, 26U, id, status);
 }
 else
 {
  res = SCSCP_io_write_XMLbegin(stream, "OMBIND", id, status);
 }

 return res;
}

/*-----------------------------------------------------------------*/
/*! write the end OpenMath tag </OMBIND>  directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writeendOMBIND(SCSCP_io* stream, SCSCP_status* status)
{
 const char binaryencoding[] = { 27,'\0'};
 const char* xmlencoding="</OMBIND>";
 const char *encoding = xmlencoding;
 
 if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) encoding = binaryencoding;

 return SCSCP_io_write(stream, encoding, status);
}

/*-----------------------------------------------------------------*/
/*! write the beginning OpenMath tag <OMFOREIGN> directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param id (in) reference id (could be NULL)
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writebeginOMFOREIGN(SCSCP_io* stream, const char *id, SCSCP_status* status)
{
 int res;
 
 if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) 
 {
  res = 0;
 }
 else
 {
  res = SCSCP_io_write_XMLbegin(stream, "OMFOREIGN", id, status);
 }

 return res;
}

/*-----------------------------------------------------------------*/
/*! write the end OpenMath tag </OMBIND>  directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writeendOMFOREIGN(SCSCP_io* stream, SCSCP_status* status)
{
 int res; 
 
 if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) 
 {
  res = 0;
 }
 else
 {
  res = SCSCP_io_write(stream, "</OMFOREIGN>", status);
 }
 return res;
}



/*-----------------------------------------------------------------*/
/*! write the beginning OpenMath tag <OMATTR id="...">  directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param id (in) sharing id (could be NULL) => id isn't written
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writebeginOMATTR(SCSCP_io* stream, const char *id, SCSCP_status* status)
{
 int res;
 
 if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) 
 {
  res = SCSCP_io_write_binarybegin(stream, 18U, id, status);
 }
 else
 {
  res = SCSCP_io_write_XMLbegin(stream, "OMATTR", id, status);
 }

 return res;
}

/*-----------------------------------------------------------------*/
/*! write the end OpenMath tag </OMATTR>  directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writeendOMATTR(SCSCP_io* stream, SCSCP_status* status)
{
 const char binaryencoding[] = { 19,'\0'};
 const char* xmlencoding="</OMATTR>";
 const char *encoding = xmlencoding;
 
 if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) encoding = binaryencoding;

 return SCSCP_io_write(stream, encoding, status);
}

/*-----------------------------------------------------------------*/
/*! write the beginning OpenMath tag <OMATP id="...">  directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param id (in) sharing id (could be NULL) => id isn't written
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writebeginOMATP(SCSCP_io* stream, const char *id,  SCSCP_status* status)
{
 int res;
 
 if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) 
 {
  res = SCSCP_io_write_binarybegin(stream, 20U, id, status);
 }
 else
 {
  res = SCSCP_io_write_XMLbegin(stream, "OMATP", id, status);
 }

 return res;
}

/*-----------------------------------------------------------------*/
/*! write the end OpenMath tag </OMATP>  directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writeendOMATP(SCSCP_io* stream, SCSCP_status* status)
{
 const char binaryencoding[] = { 21,'\0'};
 const char* xmlencoding="</OMATP>";
 const char *encoding = xmlencoding;
 
 if ((*stream)->m_sendencodingtype==SCSCP_encodingtype_Binary) encoding = binaryencoding;

 return SCSCP_io_write(stream, encoding, status);
}

/*-----------------------------------------------------------------*/
/*! write the expression <OME><OMS cd="cdname" name="symbolname><OMSTR>buffer</OMSTR></OME>"
   directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param status (inout) status error
 @param cdname (in) cd name
 @param symbolname (in) symbol name
 @param buffer (in) message
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writeOMEwithOMSOMSTR(SCSCP_io* stream, const char *cdname, const char*symbolname, 
                                  const char *buffer, SCSCP_status* status)
{
 int res = SCSCP_io_writebeginOME(stream, NULL, status);
 SCSCP_debugprint("SCSCP_io_writeOMEwithOMSOMSTR(%p, %s ,%s, %s,%p) - enter\n", stream, cdname, symbolname, buffer, status);
 
 res = res && SCSCP_io_writePairOMSOMSTR(stream, cdname, symbolname, buffer, status);
 res = res && SCSCP_io_writeendOME(stream, status);
 
 SCSCP_debugprint("SCSCP_io_writeOMEwithOMSOMSTR(%d) - returns %d\n", SCSCP_status_is(status), res);
 return res;
}

/*-----------------------------------------------------------------*/
/*! write the expression <OMS cd="cdname" name="symbolname><OMSTR>buffer</OMSTR>"
   directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param status (inout) status error
 @param cdname (in) cd name
 @param symbolname (in) symbol name
 @param buffer (in) message
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writePairOMSOMSTR(SCSCP_io* stream, const char *cdname, const char*symbolname, 
                                  const char *buffer, SCSCP_status* status)
{
 int res;
 SCSCP_debugprint("SCSCP_io_writePairOMSOMSTR(%p, %s ,%s, %s,%p) - enter\n", stream, cdname, symbolname, buffer, status);
 
 res = SCSCP_io_writeOMS(stream, cdname, symbolname, NULL, status);
 res = res && SCSCP_io_writeOMSTR(stream, buffer, NULL, status);

 SCSCP_debugprint("SCSCP_io_writePairOMSOMSTR(%d) - returns %d\n", SCSCP_status_is(status), res);
 return res;
}

/*-----------------------------------------------------------------*/
/*! write the expression <OMS cd="cdname" name="symbolname><OMI>value</OMI>"
   directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param status (inout) status error
 @param cdname (in) cd name
 @param symbolname (in) symbol name
 @param buffer (in) message
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writePairOMSOMIint(SCSCP_io* stream, const char *cdname, const char*symbolname, 
                               int value, SCSCP_status* status)
{
 int res;
 SCSCP_debugprint("SCSCP_io_writePairOMSOMIint(%p, %s ,%s, %d,%p) - enter\n", stream, cdname, symbolname, value, status);
 
 res = SCSCP_io_writeOMS(stream, cdname, symbolname, NULL, status);
 res = res && SCSCP_io_writeOMIint(stream, value, NULL, status);

 SCSCP_debugprint("SCSCP_io_writePairOMSOMIint(%d) - returns %d\n", SCSCP_status_is(status), res);
 return res;
}

/*-----------------------------------------------------------------*/
/*! write the expression <OMS cd="cdname" name="symbolname><OMI>value</OMI>"
   directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param status (inout) status error
 @param cdname (in) cd name
 @param symbolname (in) symbol name
 @param buffer (in) message
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writePairOMSOMIsizet(SCSCP_io* stream, const char *cdname, const char*symbolname, 
                               size_t value, SCSCP_status* status)
{
 int res;
 SCSCP_debugprint("SCSCP_io_writePairOMSOMIsizet(%p, %s ,%s, %d,%p) - enter\n", stream, cdname, symbolname, (int)value, status);
 
 res = SCSCP_io_writeOMS(stream, cdname, symbolname, NULL, status);
 res = res && SCSCP_io_writeOMIsizet(stream, value, status);

 SCSCP_debugprint("SCSCP_io_writePairOMSOMIsizet(%d) - returns %d\n", SCSCP_status_is(status), res);
 return res;
}


/*-----------------------------------------------------------------*/
/*! write the SCSCP tag <?scscp start ?>  directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writeSCSCPSTART(SCSCP_io* stream, SCSCP_status* status)
{
 int res= SCSCP_io_write(stream, "<?scscp start ?>", status);
 return res && SCSCP_io_flush(stream,status);
}

/*-----------------------------------------------------------------*/
/*! write the SCSCP tag <?scscp end ?>  directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writeSCSCPEND(SCSCP_io* stream, SCSCP_status* status)
{
 int res =  SCSCP_io_flush(stream,status);
 res &= SCSCP_io_write(stream, "<?scscp end ?>", status);
 res &= SCSCP_io_flush(stream,status);
 return res;
}

/*-----------------------------------------------------------------*/
/*! write the SCSCP tag <?scscp cancel ?>  directly to the SCSCP client or server 
 return 0 on error
 @param stream (inout) scscp stream
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_writeSCSCPCANCEL(SCSCP_io* stream, SCSCP_status* status)
{
 int res =  SCSCP_io_flush(stream,status);
 res &= SCSCP_io_write(stream, "<?scscp cancel ?>", status);
 res &= SCSCP_io_flush(stream,status);
 return res;
}

/*-----------------------------------------------------------------*/
/*! checks if SCSCP stream is null and returns 0 on error
  @param stream (in) object to be checked
  @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
static int SCSCP_io_checkNULL(SCSCP_io* stream, SCSCP_status* status)
{
   int res = 1;
   
   if (stream==NULL || *stream==NULL)
   {
     SCSCP_status_seterror(status,  SCSCP_STATUS_STREAMOBJECTNULL);
     res = 0;
   }
   return res;
}

/*-----------------------------------------------------------------*/
/*! \internal read one processing instruction (PI) of the stream.
  get the content of the PI from the stream
  Store the type of PI
  Store the content of PI
  @param stream (in) object to be checked
  @param msgtype (out) type of returned message.
   could be SCSCP_msgtype_ProcedureCall, SCSCP_msgtype_Interrupt, in the current specification
  @param bufferPI (out) buffer will contain the PI. 
   It must contain at least SCSCP_PI_MAXLENBUFFER characters
  @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_ReadPI(SCSCP_io* stream, SCSCP_msgtype* msgtype, char *bufferPI, SCSCP_status* status)
{
 const char *PIinfo = "<?scscp info";
 const char *PIstart = "<?scscp start ?>";
 const char *PIquit = "<?scscp quit ";
 const char *PIterminate = "<?scscp terminate ";
 int res;
 int finish;
 
  SCSCP_debugprint("SCSCP_io_ReadPI() - enter\n");
  res = SCSCP_io_checkNULL(stream, status);
  if (res)
  {
    do
    {
      char *buffer = bufferPI;
      finish=1;
      /* read the type of PI */
      res = SCSCP_sc_readline(*stream, buffer, SCSCP_PI_MAXLENBUFFER, status);
      /* check the type of PI */
      if (res)
      {
       /* locate "<?" */
       buffer = strstr(buffer, "<?");
       if (buffer==NULL)
       {
         SCSCP_debugprint("unknown PI: '%s'\n", buffer);
         finish = 0;
       }
       else if (strncmp(buffer,PIinfo, strlen(PIinfo))==0)
       {
        SCSCP_debugprint("information PI : '%s'\n", buffer);
        finish = 0;
       }
       else if (strncmp(buffer,PIstart, strlen(PIstart))==0)
       {
         SCSCP_debugprint("start PI : '%s'\n", buffer);
         *msgtype = SCSCP_msgtype_ProcedureCall;
       }
       else if (strncmp(buffer,PIquit, strlen(PIquit))==0)
       {
         SCSCP_debugprint("quit PI : '%s'\n", buffer);
         res = 0;
         SCSCP_status_seterror(status, SCSCP_STATUS_RECVQUIT);
       }
       else if (strncmp(buffer,PIterminate, strlen(PIterminate))==0)
       {
         SCSCP_debugprint("interrupt PI : '%s'\n", buffer);
         *msgtype = SCSCP_msgtype_Interrupt;
       }
       else
       {
         SCSCP_debugprint("unknown PI: '%s'\n", buffer);
         finish = 0;
       }
      } 
     } while(res==1 && finish==0); 
   }
  SCSCP_debugprint("SCSCP_io_ReadPI(,%d,%d) - returns %d\n",(int)*msgtype, SCSCP_status_is(status), res);
  return res;
}


/*-----------------------------------------------------------------*/
/*! \internal check the xml document and return an iterator on the head of the document.
  @param stream (inout) stream
  @param iter (out) racine du document
  @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_io_readxmldoc(SCSCP_io *stream, SCSCP_xmlnodeptr* iter, SCSCP_status *status)
{
 int res =  SCSCP_sc_readxmldoc(*stream, status);
 if (res) *iter =  SCSCP_xmldoc_getrootnode((*stream)->m_doc);
 return res;
}

