/*
 * structcodec.c,v 1.2 1994/01/30 11:42:28 franktor Exp
 *
 * structcodec.c
 *
 * Coder / decoder for c structures 
 *
 * Geir Pedersen, 1993
 *
*/

/*

  TODO:

    o Handle pointers a more generalised way - how can we handle things like **int
    o Handle octetString



Interface routines for the structure codec 
------------------------------------------

Boolean initialise_socket ( int fd, elemDesc *sending_struct, elemDesc *rec_struct );

Call initialise_socket() to prepare for using the struct codec to
communicate over a socket. The second paramter is a reference to a
structure descriptor for the structure that will be sent over this
socket. The third parameter is a reference to a structure descriptor
for the structure that will be received over the socket. A Boolean
will be returned to sgnal success or failure of this function.

void free_socket ( int fd );

Call this function to release resources allocated by a previous call to
initialise_socket(). 


socketStat handle_socket ( int fd, Boolean read, Boolean write, Boolean exception,
                           Boolean *selectForWrite );

Call handle_socket() when select(2) shows activity on any of the
sockets used for structcodec based communication. Set the appropriate
Boolean parameters to indicate if read, write or exception was
indicated for this socket. A value is returned indicating if: 1) a
packet is ready to be read of this socket, 2) the socket has been
destroyed or 3) if no further action is required. The last parameter
is used to indicate if the using code should select for write on this fd. 


socketStat send_struct ( int fd, void *p, Boolean *selectForWrite )

Call send_struct() to serialise and send a structure over the
specified socket. The socket must previously have been initialised
through a call to initialise_socket(). The second parameter of this
function is the structure to be coded.  The function returnes sockStatOK
on success, otherwise sockStatClosed or sockStatFailed. The last parameter
is used to indicate if the using code should select for write on this fd.


socketStat read_struct ( int fd, void ** );

Call read_struct() when handle_socket() indicates a structure can be
read of this socket. A sockStatOK will be returned if the function
was successful, otherwise sockStatFailed or sockStatClosed.
The structure will be returned through the second parameter of this function. 

void free_struct ( void *ptr, elemDesc *free_struct);

Call free_struct() to free the structure pointed at by ptr and specified
by free_struc.  Everything within the structure which has been allocated
will be freed.

*/

#ifndef VMS
#include <high/structcodec.h>
#include <netdb.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h> /* Was sys/filio.h.  This is portable.  -Frank */
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <netinet/in.h>
#include <malloc.h>
#define netread read
#define netwrite write

#else /* VMS */

#include <sys/netdb.h>
#include <errno.h>
#include <sys/types.h>
#ifdef __STDDEF /* Incompatibility with Wollongong */
#define __STDDEF_LOADED 1
#endif
#ifdef __TYPES
#define __TYPES_LOADED 1
#define __SOCKET_TYPEDEFS 1
#endif
#include <sys/socket.h>
#include <sys/ioctl.h>			/* Has FIONREAD */
#include <ctype.h>
#include <netinet/in.h>

#define bcopy(a,b,c) memcpy(b,a,c)
#include <structcodec.h>

#endif /* VMS */

typedef struct qElem {
   struct qElem		*next;
   char			*data;
   int			len;
   int			idx;
} qElem;

typedef struct stackElem {
   elemDesc		*ed;
   int			edi;
   char			*p;
   int			pi;
   Boolean		b;
   struct stackElem	*next;
} stackElem;



typedef struct globalData {
   /* hook for holding data for user */
   void			*data;

   /* data to be used internally by structcodec */
   int			fd;
   Boolean		connectionOK; /* set to false if connection broken */
   Boolean		writeBlocked; /* set if previous write attempt blocked */
   qElem		*outQ; /* grown from the head */

   elemDesc		*sendStruct; /* descriptor for stucture sent over this socket */
   elemDesc		*recvStruct; /* descriptor for structure received over this socket */

   /* state variables for input parsing */
   char			*pkt;	/* data being parsed */
   int			pkti;	/* index into pkt */
   int			pktlen;	/* allocated length of pkt */
   elemDesc		*ed;	/* elemDesc basis for ongoing parsing */
   int			edi;	/* index into ed for current element */
   char			*p;	/* data structure parsed into */
   int			pi;	/* index into current element in p */
   Boolean		willParseUnion;	/* set if is to parse union next time round in the loop */
   Boolean		parsingUnion; /* set if union is being parsed */

   Boolean		parsingCompleted; /* set when a packet has been parsed */

   /* stack for input parsing */
   stackElem		*stack;	/* stack */

   /* reference to next globalData structure */
   struct globalData	*next;
} globalData;

#ifndef __CEXTRACT__
#ifdef VMS
#include <structproto.h>
#else
#include <high/structproto.h>
#endif
#endif

  /* list of globals for all intialised file descriptors */
static globalData	*Globals	= (globalData *) NULL;



char *encode_struct ( char *, elemDesc *, int * );



/* Routines relevant to the globalData structure */

globalData *alloc_globalData ( void )
{
   globalData		*G;
   
   G = (globalData *) malloc ( sizeof ( globalData ) );

   G->parsingUnion = False;
   G->willParseUnion = False;
   G->parsingCompleted = False;

   G->outQ = (qElem *) NULL;
   G->fd = -1;
   G->connectionOK = True;
   G->stack = (stackElem *) NULL;

   G->sendStruct = G->recvStruct = (elemDesc *) NULL;

   G->pkt = (char *) malloc ( G->pktlen = 512 );
   G->pkti = 0;
   G->ed = (elemDesc *) NULL;
   G->edi = 0;
   G->p = (char *) NULL;
   G->pi = 0;

   G->next = Globals;
   Globals = G;

   return (globalData *) G;
}


globalData *find_globalData ( int fd )
{
   globalData		*g;

   for ( g = Globals; g && g->fd != fd; g = g->next );

   return g;
}

void free_globalData ( int fd )
{
   globalData		*g, *gp;
   stackElem		*se, *sen;

   if ( !Globals )
      return;

   g = Globals;
   if ( g->fd == fd )
      Globals = g->next;
#if 0				/* Was this the old code????	--hbf */
   else
      for ( gp = g, g = g->next; g->fd != fd; g = gp, gp = g ? g->next : (globalData *) NULL );
#else

   for ( gp = (globalData *) NULL, g = Globals;
	 g && g->fd != fd;
	 gp = g, g = g->next );
#endif

   if ( !g )
   {
      LOG ( facStruct, llevExceptions, "free_globalData() called with unknown reference %d",
	    fd );
      return;
   }

#if 1				/* New code???? -hbf */
   if ( !gp )
      Globals = g->next;
   else
      gp->next = g->next;
#endif

   /* free the outQ */
   while ( g->outQ )
      free_out_packet ( g, g->outQ );

   if ( g->pkt )
      free ( g->pkt );

   for ( se = g->stack, se ? sen = se->next : (stackElem *) NULL;
	 se;
	 se = sen, se ? sen = se->next : (stackElem *) NULL )
      free ( se );

   free ( g );
}



/* Utility routines */

			/* stack for output encoding */
stackElem		*outputStack		= (stackElem *) NULL;

			/* free stack elements - used by all stacks */
stackElem		*freeStackElems		= (stackElem *) NULL;

			/* current stack */
stackElem		*stack			= (stackElem *) NULL;	
      

void push ( elemDesc *ed, int edi, char *p, int pi, Boolean b )
{
   stackElem		*se;

   if ( !ed )
   {
      /* initialising */
      for ( se = stack; se && se->next; se = se->next );
      if ( se )
      {
	 se->next = freeStackElems;
	 freeStackElems = stack;
      }
      stack = (stackElem *) NULL;
   }

   if ( !freeStackElems )
      se = (stackElem *) malloc ( sizeof ( stackElem ) );
   else
   {
      se = freeStackElems;
      freeStackElems = se->next;
   }

   se->ed = ed;
   se->edi = edi;
   se->p = p;
   se->pi = pi;
   se->b = b;

   se->next = stack;
   stack = se;
}

Boolean pop ( elemDesc **ed, int *edi, char **p, int *pi, Boolean *b )
{
   stackElem			*se;
   
   if ( !stack ) {
      LOG(facStruct, llevExceptions, "Tried to pop empty stack.");
      return False;
   }

   se = stack;
   *ed = se->ed;
   *edi = se->edi;
   *p = se->p;
   *pi = se->pi;
   *b = se->b;
   
   stack = se->next;
   se->next = freeStackElems;
   freeStackElems = se;

   return True;
}

/*
 * append ( char *data, int datalen, char **pkt, int *pkti, int *pktlen )
 *
 * copies datalen bytes from data to *pkt[*pkti]. pkt is expected to be *pktlen bytes
 * and will be realloced if too short
 *
*/

char *append ( char *data, int datalen, char **pkt, int *pkti, int *pktlen )
{
   if ( *pktlen - *pkti <= datalen )
      *pkt = (char *) realloc ( *pkt, *pktlen = ((*pktlen + datalen + 512) / 512) * 512);
      
   bcopy ( data, *pkt + *pkti, datalen );
   *pkti += datalen;

   return *pkt;
}


int elemSize ( elemDesc *ed )	/* compute size of structure described in elemDesc */
{
   int			i;
   int			s;

   for ( i = 0, s = 0; ed[i].type != elemDone; i++ )
      switch ( ed[i].type )
      {
       case elemUnion:
	 s += elemMaxElemSize ( ed[i].desc );
	 break;

       default:
	 s += ed[i].size;
	 break;
      }

   return s;
}

int elemSizeMax ( elemDesc *ed, int max )	/* compute size of structure described in elemDesc */
{
   int			i;
   int			s;

   for ( i = 0, s = 0; i < max && ed[i].type != elemDone; i++ )
      switch ( ed[i].type )
      {
       case elemUnion:
	 s += elemMaxElemSize ( ed[i].desc );
	 break;

       default:
	 s += ed[i].size;
	 break;
      }

   return s;
}

int elemMaxElemSize ( elemDesc *ed ) /* compute size of largest element described in elemDesc */
{
   int			i;
   int			s;

   for ( i = 0, s = 0; ed[i].type != elemDone; i++ )
      if ( ed[i].size > s )
	 switch ( ed[i].type )
	 {
	  case elemUnion:
	    s = elemMaxElemSize ( ed[i].desc );
	    break;

	  default:
	    s = ed[i].size;
	    break;
	 }

   return s;
}

void insert ( char *d, int i, char *p )
{
   int			j;

   for ( j = 0; j < i; j++ )
      p[j] = d[j];
}


/* INITIALISATION OF SOCKET */

Boolean initialise_socket ( int fd, elemDesc *sending_struct, elemDesc *recv_struct )
{
   globalData		*g;

   if ( (g = find_globalData ( fd )) != NULL )
   {
      LOG ( facStruct, llevExceptions, "initialise_socket() failed for socket=%d becuase socket already initialised", fd );
      return False;
   }

   if ( !(g = alloc_globalData()) )
   {
      LOG ( facStruct, llevExceptions, "initialise_socket() failed to allocate structure for global socket information" );
      return False;
   }

   g->fd = fd;
   g->sendStruct = sending_struct;
   g->recvStruct = recv_struct;

   return True;
}



/* FREE SOCKET */

void free_socket ( int fd )
{
   globalData		*g;
   
   if ( (g = find_globalData ( fd )) != NULL )
   {
      LOG ( facStruct, llevExceptions, "free_socket() failed for socket=%d becuase socket not initialised", fd );
      return;
   }

   free_globalData ( fd );
}




/* HANDLE SOCKET */

socketStat handle_socket ( int fd, Boolean read, Boolean write, Boolean exception,
			   Boolean *selectForWrite )
{
   globalData		*g;

   if ( !(g = find_globalData ( fd )) )
   {
      LOG ( facStruct, llevExceptions, "handle_socket(): Socket is not intialised" );
      return sockStatFailed;
   }

#ifndef VMS
   if ( exception )
   {
      LOG ( facStruct, llevExceptions,
	    "handle_socket() received an exception for socket %d, closing down socket", fd );
      free_globalData ( fd );
      return sockStatClosed;
   }
#endif

   if ( write )
   {
      if ( !send_data ( g ) )
      {
	 free_globalData ( fd );
	 return sockStatClosed;
      }
      else
      {
	 *selectForWrite = g->writeBlocked;
	 return sockStatOK;
      }
   }

   if ( read )
   {
      if ( !parse_stream ( g ) )
      {
	 free_globalData ( fd );
	 return sockStatClosed;
      }
   
      if ( g->parsingCompleted )
	 return sockStatPacketReady;
      else
	 return sockStatOK;
   }

#ifdef VMS
   if ( exception )
      LOG ( facStruct, llevDebug,
	    "handle_socket(): VMS select() cannot handle except and write");
   else
#endif

  LOG ( facStruct, llevExceptions,
        "handle_socket(): read, write or exception flag was not set - nothing to do");

  return sockStatFailed;
}



/* SEND STRUCT */

socketStat send_struct ( int fd, void *p, Boolean *selectForWrite )
{
   char			*Struct;
   int			structLen;
   globalData		*g;

   if ( !(g = find_globalData ( fd )) )
   {
      LOG ( facStruct, llevExceptions, "send_struct(): Socket is not intialised" );
      return sockStatFailed;
   }

   if ( !(Struct = encode_struct ( p, g->sendStruct, &structLen )) )
   {
      LOG ( facStruct, llevExceptions, "send_struct(): encode_struct() failed, closing socket" );
      free_globalData ( fd );
      return sockStatClosed;
   }

   queue_out_packet ( g, Struct, structLen );
   if (send_data ( g ) == False)
   {
      LOG ( facStruct, llevExceptions, "send_struct(): Failed to send data.");
      free_globalData ( fd );
      return sockStatClosed;
   }
   
   *selectForWrite = g->writeBlocked;
   return sockStatOK;
}



/* READ STRUCT */

socketStat read_struct ( int fd, void **p )
{
   globalData		*g;
   int			structLen;

   if ( !(g = find_globalData ( fd )) )
   {
      LOG ( facStruct, llevExceptions, "read_struct(): Socket is not intialised" );
      return sockStatFailed;
   }

   if ( !g->parsingCompleted )
   {
      LOG ( facStruct, llevExceptions, "read_struct(): Structure not completely parsed" );
      return sockStatFailed;
   }

   g->parsingCompleted = False;
   *p = (void *) g->p;

   return sockStatOK;
}




/* ENCODER */

char *encode_struct ( char *P, elemDesc *topEd, int *len )
{
   elemDesc		*ed;
   int			edi;	/*  */
   char			*pkt		= (char *) malloc ( 512 );
   int			pkti;
   int			pktlen		= 512;
   char			*p;	/* structure being coded */
   int			pi;
   Boolean		willCodeUnion	= False;
   Boolean		codingUnion	= False;

   stack = outputStack;
      
   ed = topEd;
   
   push ( (elemDesc *) NULL, 0, (char *) NULL, 0, False );/* when this is popped the encoding is done */

   for ( edi = 0, p = (char *) P, pi = 0, pkti = 0; ed; )
   {
      codingUnion = willCodeUnion;
      willCodeUnion = False;
      
      switch ( ed[edi].type )
      {
	 int			i;
	 
       case elemDone:
         LOG(facStruct, llevTrace, "Encoding DONE");
	 pop ( &ed, &edi, &p, &pi, &codingUnion );
	 break;
	 
       case elemInt:
         bcopy(&p[pi], &i, sizeof(int));
         LOG(facStruct, llevTrace, "Encoding INT (%d)", i);
         i = (int) htonl((u_long) i);
	 pkt = append ( (char *) &i, ed[edi].size, &pkt, &pkti, &pktlen );
	 pi += ed[edi].size;
	 edi++;
	 break;

       case elemEnum:
         bcopy(&p[pi], &i, sizeof(int));
         LOG(facStruct, llevTrace, "Encoding ENUM (%d)", i);
         i = (int) htonl((u_long) i);
         pkt = append ( (char *) &i, ed[edi].size, &pkt, &pkti, &pktlen );
	 pi += ed[edi].size;
	 edi++;
	 break;
	 
       case elemBoolean:
	 switch ( *(Boolean *) &p[pi] )
	 {
	  case True:
            LOG(facStruct, llevTrace, "Encoding BOOLEAN (True)");
	    pkt = append ( "T", 1, &pkt, &pkti, &pktlen );
	    break;
	    
	  case False:
            LOG(facStruct, llevTrace, "Encoding BOOLEAN (False)");
	    pkt = append ( "F", 1, &pkt, &pkti, &pktlen );
	    break;
	 }
	 pi += ed[edi].size;
	 edi++;
	 break;
	 
       case elemCharPtr:
	 if ( *(char **)&p[pi] == (char *) NULL)
         {
           LOG(facStruct, llevTrace, "Encoding CHARPTR (False)");
           i = (int) htonl((u_long) (-1));
         }
         else
         {
	   i = strlen ( *(char **)&p[pi] );
           LOG(facStruct, llevTrace, "Encoding CHARPTR (True: %d)", i);
         }
         {
           int tmp = (int) htonl((u_long) i);
	   pkt = append ( (char *) &tmp, sizeof(int), &pkt, &pkti, &pktlen );
         }
	 if ( i > 0 )
	    pkt = append ( *(char **)&p[pi], i, &pkt, &pkti, &pktlen );
	 pi += ed[edi].size;
	 edi++;
	 break;
	 
       case elemStruct:
         LOG(facStruct, llevTrace, "Ecoding STRUCT");
	 push ( ed, edi + 1, p, pi + elemSize ( ed[edi].desc ), codingUnion );
	 p += pi;
	 pi = 0;
	 ed = ed[edi].desc;
	 edi = 0;
	 codingUnion = False;
	 break;

       case elemStructPtr:
	 /* struct ptr is encoded as a Boolean which is set to reflect if the pointer is NULL 
            optionally followed by the structure */
	 if ( *(char **) &p[pi] )
	 {
            LOG(facStruct, llevTrace, "Encoding STRUCTPTR (True)");
	    pkt = append ( "T", 1, &pkt, &pkti, &pktlen );

	    push ( ed, edi + 1, p, pi + ed[edi].size, codingUnion );
	    codingUnion = False;
	    p = *(char **) &p[pi];
	    pi = 0;
	    ed = ed[edi].desc;
	    edi = 0;
	 }
	 else
	 {
            LOG(facStruct, llevTrace, "Encoding STRUCTPTR (False)");
	    pkt = append ( "F", 1, &pkt, &pkti, &pktlen );
	    edi++;
	 }
	 break;

       case elemUnion:
         LOG(facStruct, llevTrace, "Encoding UNION");
         {
	    /* find element pointing out the valid union elment */
	    char			*discptr;
	    int				disc;
	    int				s;

	    discptr = p + pi - (elemSizeMax ( ed, edi ) -  elemSizeMax ( ed, ed[edi].unionDiscriminator ) );
	    s = ed[ed[edi].unionDiscriminator].size;
	    switch ( s )
	    {
	       unsigned short		twobytes;
	       unsigned int		fourbytes;
	       char   			*cp1;
	       int		       	i;
	       
	     case 1:
	       disc = (int) *discptr;
	       break;

	     case 2:
	       for ( i = 0, cp1 = (char *) &twobytes; i < 2; i++, discptr++, cp1++ )
		  *cp1 = *discptr;
	       disc = (int) twobytes;
	       break;

	     case 4:
	       for ( i = 0, cp1 = (char *) &fourbytes; i < 4; i++, discptr++, cp1++ )
		  *cp1 = *discptr;
	       disc = (int) fourbytes;
	       break;

	     default:
	       LOG ( facStruct, llevExceptions, "encode_struct() Can't handle %d byte sized union discriminator", s );
	       outputStack = stack;
	       return (char *) NULL;
	    }
            LOG(facStruct, llevTrace, "Encoding UNION (choice %d, size %d)", disc, s);
	    
	    /* push current state and set up for coding union */
	    push ( ed, edi + 1, p, pi + elemMaxElemSize ( ed[edi].desc ), codingUnion );
	    willCodeUnion = True;
	    ed = ed[edi].desc;
	    edi = disc;
	 }
	 break;

       default:
	 LOG ( facStruct, llevExceptions, "encode_struct() Can't code this data type" );
	 outputStack = stack;
	 return (char *) NULL;
      }

      if ( codingUnion )
      {
	 /* the union element has been coded - fast forward to end of descriptor */
	 while ( ed[edi].type != elemDone )
	    edi++;
      }
   }

   outputStack = stack;

   *len = pkti;
#if 0 /* Will hopefully not need this */
   { /* Debug, added by Frank */
     int i;
     char buf[256],buf2[10];
     buf[0]='\0';
     for (i=0;i<pkti;i++) {
       sprintf(buf2,"%2.2x:",pkt[i]);
       strcat(buf,buf2);
     }
     LOG(facStruct, llevTrace, "Packet: %s",buf);
   }
#endif
   return pkt;
}



/* DECODER */

/* 
 * Returns False if the connection on the specified socket is
 * broken, else True. 
 */

Boolean parse_stream ( globalData *g )
{
   int			parsed;
   int			fd;
   long			i 		= 0;


   LOG ( facStruct, llevTrace, "Entering parse_stream");

   fd = g->fd;
   stack = g->stack;

   if ( g->ed == (elemDesc *) NULL )
   {
      /* We are about to start parsing a new packet */
      g->ed = g->recvStruct;
      g->edi = 0;
      g->p = (char *) malloc ( elemSize ( g->ed ) );
      g->pi = 0;
      g->parsingCompleted = False;
   }

   if ( !(g->pkt) )
   {
      g->pkt = (char *) malloc ( g->pktlen = 512 );
      g->pkti = 0;
      g->willParseUnion = False;
      g->parsingUnion = False;
   }

   if ( ioctl ( fd, FIONREAD, (char *) &i ) == -1 )
   {
     LOG ( facStruct, llevExceptions, "parse_input: ioctl() failed: %s", strerror() );
     return False;
   }
  
   if ( i == 0 )
   {
     LOG ( facStruct, llevExceptions, "parse_stream: encounted end-of-file - closing connection" );
     g->stack = stack;
     return False;
   }

   for ( parsed = 0; !((g->ed == g->recvStruct) && (g->ed[g->edi].type == elemDone)); )
   {
      long            i = 0;
#ifdef OLDVMSHACK
      long              goal = 512;   /* 512 bytes each read() */
      long              got = 0;
      char              debugoutput[513];
#endif
      
      g->parsingUnion = g->willParseUnion;
      g->willParseUnion = False;

#ifdef OLDVMSHACK
      LOG ( facStruct, llevTrace, "Beginning to read it all");
        /* read all there is to read */
      do {
       /* nothing more to read just now */
       if ( g->pktlen - g->pkti <= goal )
          g->pkt = (char *) realloc ( g->pkt, g->pktlen = ((g->pktlen + goal + 512) / 512) * 512);
      
       LOG ( facStruct, llevTrace, "Read one chunk");
       if ( (got = read ( fd, &g->pkt[g->pkti], goal )) <= 0 )
       {
          LOG ( facStruct, llevExceptions, "Failed to read: %s", strerror() );
          g->stack = stack;
          return False;
       }
       strncpy(debugoutput, &g->pkt[g->pkti], got);
         debugoutput[got] = 0;
       LOG ( facStruct, llevTrace, "Read: %s", debugoutput);
      
       g->pkti += got;
      } while (got == goal); /* read() will return as much as possible */
#else
      if ( ioctl ( fd, FIONREAD, (char *) &i ) == -1 )
      {
	 LOG ( facStruct, llevExceptions, "parse_input: ioctl() failed: %s", strerror() );
	 g->stack = stack;
	 return False;
      }

      if ( i > 0 )
      {
	 if ( g->pktlen - g->pkti <= i )
	    g->pkt = (char *) realloc ( g->pkt, g->pktlen = ((g->pktlen + i + 512) / 512) * 512);
      
	 if ( (i = netread ( fd, &g->pkt[g->pkti], i )) == -1 )
	 {
	    LOG ( facStruct, llevExceptions, "Failed to read: %s", strerror() );
	    g->stack = stack;
	    return False;
	 }
      
	 g->pkti += i;
      }
#endif

      switch ( g->ed[g->edi].type )
      {
       case elemDone:
	 LOG ( facStruct, llevTrace, "Parsing DONE with struct" );
	 /* done with this structure - pop */
	 pop ( &g->ed, &g->edi, &g->p, &g->pi, &g->parsingUnion );
	 break;
	 
       case elemInt:
	 
	 if ( g->pkti >= sizeof(int) )
	 {
            int tmp;
            bcopy ((char *) g->pkt, (char *) &tmp, sizeof(int));
            tmp = (int) ntohl((u_long) tmp);
	    LOG ( facStruct, llevTrace, "Parsing INT (%d)", tmp );
            bcopy ((char *) &tmp, (char *) &g->p[g->pi], sizeof(int));

	    g->pi += sizeof(int);
	    parsed = sizeof(int);
	    g->edi++;
	 }
	 else
	    goto lab999;
	 break;

       case elemEnum:
	 if ( g->pkti >= g->ed[g->edi].size )
	 {
            long tmp = 0;
            /* This might go wrong? */
            bcopy ((char *) g->pkt, (char *) &tmp, g->ed[g->edi].size);
            tmp = (long) ntohl((u_long) tmp);
	    LOG ( facStruct, llevTrace, "Parsing ENUM (%d)", tmp );
            bcopy ((char *) &tmp, (char *) &g->p[g->pi], g->ed[g->edi].size);

	    g->pi += g->ed[g->edi].size;
	    parsed = g->ed[g->edi].size;
	    g->edi++;
	 }
	 else
	    goto lab999;
	 break;
	 
       case elemBoolean:
	 if ( g->pkti >= 1 )
	 {
	    Boolean dummy;
            switch (*g->pkt) {
            case 'T':
	       LOG ( facStruct, llevTrace, "Parsing BOOLEAN (True)" );
	       dummy = True;
	       break;
	    case 'F':
	       LOG ( facStruct, llevTrace, "Parsing BOOLEAN (False)" );
	       dummy = False;
	       break;
	    default:
	       LOG(facStruct, llevExceptions, "Boolean was not True or False.");
	    }
	    insert ( (char *) &dummy, sizeof(Boolean), &g->p[g->pi] );
	    g->pi += sizeof(Boolean);
	    parsed = 1;
	    g->edi++;
	 }
	 else
	    goto lab999;
	 break;
	 
       case elemCharPtr:
	 if ( g->pkti >= sizeof(int) )
	 {
	    int			len;

            bcopy((char *) g->pkt, (char *) &len, sizeof(int));
            len = (int) ntohl((u_long)len);
            if (len < 0)
            {
	       char	*s = (char *) NULL;
	       LOG ( facStruct, llevTrace, "Parsing CHARPTR (False)" );
	       insert ( (char *) &s, sizeof(char *), &g->p[g->pi] );
	       g->pi += sizeof(char *);

               parsed = sizeof(int);
               g->edi++;
            }
            else /* check if we have the whole text string ready */
	    if ( g->pkti >= sizeof(int) + len )
	    {
	       /* OK - parse it */
	       char		*s;
	       LOG ( facStruct, llevTrace, "Parsing CHARPTR (True: %d)", len );
	       s = (char *) malloc ( len + 1 );
	       bcopy ( &g->pkt[sizeof(int)], s, len );
	       s[len] = '\0';
	       insert ( (char *) &s, sizeof(char *), &g->p[g->pi] );
	       g->pi += sizeof(char *);

	       parsed = sizeof(int) + len;
	       g->edi++;
	    }
	    else
	       goto lab999;
	 }
	 else
	    goto lab999;
	 break;
	 
       case elemStruct:
	 LOG ( facStruct, llevTrace, "Parsing STRUCT" );
         
	 push ( g->ed, g->edi + 1, g->p, g->pi + elemSize ( g->ed[g->edi].desc ), g->parsingUnion );
	 g->p += g->pi;
         g->pi = 0;
	 g->ed = g->ed[g->edi].desc;
	 g->edi = 0;
	 g->parsingUnion = False;
	 break;
	 
       case elemStructPtr:
	 /* check if struct was present */
	 if ( g->pkti >= 1 )
	 {
	    if ( *g->pkt == 'T' )
	    {
	       char			*tmp;
	       LOG ( facStruct, llevTrace, "Parsing STRUCTPTR (True)" );

	       tmp = (char *) malloc ( elemSize ( g->ed[g->edi].desc ) );
	       insert ( (char *) &tmp, sizeof ( char * ), &g->p[g->pi] );
	       g->pi += sizeof(char *);

	       push ( g->ed, g->edi + 1, g->p, g->pi, g->parsingUnion );

	       g->ed = g->ed[g->edi].desc;
	       g->edi = 0;

	       g->p = tmp;
	       g->pi = 0;


	       g->parsingUnion = False;
	    }
	    else
	    {
	       char			*tmp	= (char *) NULL;
	       
	       LOG ( facStruct, llevTrace, "Parsing STRUCTPTR (False)" );
	       insert ( (char *) &tmp, sizeof ( char * ), &g->p[g->pi] );
	       g->pi += sizeof(char *);
	       g->edi++;
	    }
	    parsed++;
	 }
	 else
	    goto lab999;
	 break;

       case elemUnion:
         {
	    /* find element pointing out the valid union elment */
	    char			*discptr;
	    int				disc;

	    discptr = g->p + g->pi - (elemSizeMax ( g->ed, g->edi ) -  elemSizeMax ( g->ed, g->ed[g->edi].unionDiscriminator ) );
	    switch ( g->ed[g->ed[g->edi].unionDiscriminator].size )
	    {
	       unsigned short		twobytes;
	       unsigned int		fourbytes;
	       char   			*cp1;
	       int		       	i;
	       
	     case 1:
	       disc = *discptr;
	       break;

	     case 2:
	       for ( i = 0, cp1 = (char *) &twobytes; i < 2; i++, discptr++, cp1++ )
		  *cp1 = *discptr;
	       disc = twobytes;
	       break;

	     case 4:
	       for ( i = 0, cp1 = (char *) &fourbytes; i < 4; i++, discptr++, cp1++ )
		  *cp1 = *discptr;
	       disc = fourbytes;
	       break;

	     default:
	       LOG ( facStruct, llevExceptions, "parse_input() Can't handle %d byte sized union discriminator",
		     g->ed[g->ed[g->edi].unionDiscriminator].size );
	       g->stack = stack;
	       return False;
	    }
	    
	    LOG ( facStruct, llevTrace, "Parsing UNION, choice %d", disc );

	    /* push current state and set up for parsing union */
	    push ( g->ed, g->edi + 1, g->p, g->pi + elemMaxElemSize ( g->ed[g->edi].desc ),
		   g->parsingUnion );
	    g->willParseUnion = True;
	    g->ed = g->ed[g->edi].desc;
	    g->edi = disc;
	 }
	 break;

       default:
	 LOG ( facStruct, llevExceptions, "Failed to parse data from rmeote, unknown data type in structure descriptor" );
	 g->stack = stack;
	 return False;
      }

/*    LOG(facStruct, llevTrace, "Struct: %2x%2x%2x%2x.",g->p[0],g->p[1],g->p[2],g->p[3]); */
      /* reclaim space from pkt */
      bcopy ( &g->pkt[parsed], g->pkt, g->pkti - parsed );
      g->pkti -= parsed;
      parsed = 0;

      if ( g->parsingUnion )
      {
	 g->willParseUnion = False;

	 /* the union element has been parsed - fast forward to end of descriptor */
	 while ( g->ed[g->edi].type != elemDone )
	    g->edi++;
      }
   }

   if ( (g->ed == g->recvStruct) && (g->ed[g->edi].type == elemDone) )
   {
      /* have parsed a packet */
      g->parsingCompleted = True;
      g->ed = (elemDesc *) NULL;
   }

 lab999:;			/* come here when need more data - wait til next time called */
   g->stack = stack;

   return True;
}



/* Routines to send coded data over a socket */

void queue_out_packet ( globalData *G, char *p, int len )
{
   qElem		*qe	= (qElem *) malloc ( sizeof ( qElem ) );

   qe->data = p;
   qe->len = len;
   qe->idx = 0;

   qe->next = G->outQ;
   G->connectionOK = True;
   G->writeBlocked = False;
   G->outQ = qe;
}

qElem *get_out_packet ( globalData *G )
{
   qElem		*qe;

   for ( qe = G->outQ; qe && qe->next; qe = qe->next );

   return qe;
}

void free_out_packet ( globalData *G, qElem *qe )
{
   qElem		*p;

   if ( qe == G->outQ )
      G->outQ = qe->next;
   else
   {
      for ( p = G->outQ; p->next != qe; p = p->next );
      p->next = qe->next;
   }

   free ( qe->data );
   free ( qe );
}

Boolean send_data ( globalData *G )	/* returns False if the connection has broken */
{
   qElem		*qe;
   int			i;

   for ( qe = get_out_packet ( G ); qe; free_out_packet ( G, qe ), qe = get_out_packet( G ) )
   {
      G->writeBlocked = False;
      
      /* VMS will block on stdout, but that is not important */

#ifdef VMS
      /* VMS does not seem to handle select on write/except.      -hbf/geir */
    do {			/* loop here until all is written */
#endif

      if ( (i = netwrite ( G->fd, &qe->data[qe->idx], qe->len - qe->idx )) == -1 )
      {
	 if ( errno != EWOULDBLOCK )
	 {
	    LOG ( facStruct, llevExceptions, "send_data: Failed to write data to remote databaseserver: %s" , strerror() );
	    G->connectionOK = False;
	    return False;
	 }
	 else
	 {
	    G->writeBlocked = True;
	    break;
	 }
      }
      	 
      if ( i != qe->len - qe->idx )
      {
	 qe->idx += i;
	 G->writeBlocked = True;
	 break;			/* can't write any more */
      }
#ifdef VMS
    } while (G->writeBlocked);
#endif
   }

   return True;
}

/*
 * I started to work on this... Mostly to find out more about
 * the structcodec.  -Frank.
 */

void **pointer_list = (void **) NULL;

int current_position, current_length = 0;

#define ALLOCATE_CHUNK 20

void add_pointer (void *pointer)
{
  if (current_length == current_position)
  {
    current_length += ALLOCATE_CHUNK;
    if (pointer_list == (void **) NULL)
      pointer_list = (void **) malloc(sizeof(void **) * current_length);
    else
      pointer_list = (void **) realloc(pointer_list, sizeof(void **) * current_length);
  }
  * (pointer_list + current_position) = pointer;
  current_position++;
}

void free_pointers()
{
  int pos;
  if (!current_position)
  {
    LOG(facStruct, llevDebug, "free_pointers: Had nothing to do.");
    return;
  }
  LOG(facStruct, llevTrace, "free_pointers: Freeing %d pointers.",current_position);
  for (pos = 0; pos < current_position; pos++)
    free( * (pointer_list + pos));
  free (pointer_list);
  pointer_list = (void **) NULL;
  current_position = 0;
  current_length = 0;
}

void free_struct (void *P, elemDesc *topEd)
{
  elemDesc	*ed;
  int		edi;
  int		pi;
  char		*p;	/* structure being freed */
  Boolean	willFreeUnion;
  Boolean	freeingUnion;

  willFreeUnion = False;
  ed = topEd;

  push ( (elemDesc *) NULL, 0, (char *) NULL, 0, False); /* When this is popped, the freeing is done */

  for (edi = 0, p = (char *) P, pi = 0; ed; )
  {
    freeingUnion = willFreeUnion;
    willFreeUnion = False;

    switch (ed[edi].type)
    {
    case elemDone:
      LOG(facStruct, llevTrace, "free_struct: elemDone.");
      pop (&ed, &edi, &p, &pi, &freeingUnion);
      break;

    case elemInt:
      LOG(facStruct, llevTrace, "free_struct: INT");
      pi += ed[edi++].size;
      break;

    case elemEnum:
      LOG(facStruct, llevTrace, "free_struct: ENUM");
      pi += ed[edi++].size;
      break;

    case elemBoolean:
      LOG(facStruct, llevTrace, "free_struct: BOOLEAN");
      pi += ed[edi++].size;
      break;

    case elemCharPtr:
      if (*(char **)&p[pi])
      {
        LOG(facStruct, llevTrace, "free_struct: CHARPTR (True)");
        add_pointer((void *) * (char **) &p[pi]);
      }
      else
        LOG(facStruct, llevTrace, "free_struct: CHARPTR (False)");
      pi += ed[edi++].size;
      break;

    case elemStruct:
      LOG(facStruct, llevTrace, "free_struct: STRUCT");
      push (ed, edi+1, p, pi + elemSize ( ed[edi].desc ), freeingUnion );
      p += pi;
      pi = 0;
      ed = ed[edi].desc;
      edi = 0;
      freeingUnion = False;
      break;

    case elemStructPtr:
      if ( * (char **) &p[pi] )
      {
        LOG(facStruct, llevTrace, "free_struct: STRUCTPTR (True)");
        add_pointer ((void *) * (char **) &p[pi]); /* Correct? */
        push ( ed, edi + 1, p, pi + ed[edi].size, freeingUnion );
        freeingUnion = False;
        p = *(char **) &p[pi];
        pi = 0;
        ed = ed[edi].desc;
        edi = 0;
      }
      else
      {
        LOG(facStruct, llevTrace, "free_struct: STRUCTPTR (False)");
        edi++; /* No pi += ed[edi].size ? */
      }
      break;

    case elemUnion:
      {
        /* find element pointing out the valid union element */
        char	*discptr;
	int	disc;
	int	s;

	discptr = p + pi - (elemSizeMax ( ed, edi ) -  elemSizeMax ( ed, ed[edi].unionDiscriminator ) );
	s = ed[ed[edi].unionDiscriminator].size;
	switch ( s )
	{
	  unsigned short	twobytes;
	  unsigned int		fourbytes;
	  char   		*cp1;
	  int		       	i;
	       
	case 1:
	  disc = *discptr;
	  break;

	case 2:
	  for ( i = 0, cp1 = (char *) &twobytes; i < 2; i++, discptr++, cp1++ )
  	    *cp1 = *discptr;
	  disc = twobytes;
	  break;

	case 4:
	  for ( i = 0, cp1 = (char *) &fourbytes; i < 4; i++, discptr++, cp1++ )
  	    *cp1 = *discptr;
	  disc = (int) fourbytes;
	  break;

	default:
	  LOG ( facStruct, llevExceptions, "free_struct: Can't handle %d byte sized union discriminator", s );
	  return;
	}
	    
	discptr = p + pi - (elemSizeMax ( ed, edi ) - elemSizeMax ( ed, ed[edi].unionDiscriminator ) );
	push (ed, edi + 1, p, pi + elemMaxElemSize (ed[edi].desc), freeingUnion);
	willFreeUnion = True;
	ed = ed[edi].desc;
        LOG(facStruct, llevTrace, "free_struct: UNION (choice %d, size %d)", disc, s);
	edi = disc;
      }
      break;

    default:
      LOG (facStruct, llevExceptions, "free_struct: Can't free this data type");
      return;
    }

    if ( freeingUnion )
    {
      /* Done with union element, fast forward to end of descriptor */
      while ( ed[edi].type != elemDone )
        edi++;
    }
  }

  free_pointers();
}
