#if !defined(lint) && !defined(__INSIGHT__)
static char sos__rcsid[] = "xdr.c,v 1.1.1.1 1995/06/16 17:42:44 seth Exp";
static char sos__copyright[] = "Copyright (c) 1994, 1995 SOS Corporation";
static char sos__contact[] = "SOS Corporation <sos-info@soscorp.com> +1 800 SOS UNIX";
#endif /* not lint */

/*
 * ++Copyright Released Product++
 *
 * Copyright (c) 1994, 1995 Sources of Supply Corporation ("SOS").
 * All rights reserved.
 *
 * The SOS Released Product License Agreement specifies the terms and
 * conditions for redistribution.  You may find the License Agreement
 * in the file LICENSE.
 *
 * SOS Corporation
 * 461 5th Ave.; 16th floor
 * New York, NY 10017
 *
 * +1 800 SOS UNIX
 * <sos-info@soscorp.com>
 *
 * --Copyright Released Product--
 */

/*
 * Everything dealing with encoding data
 *
 * Deals with serialization and network encoding
 * of primative data types (char, short, int, long,
 * string, buffer).  Sorry, no float yet (unless
 * there is a htonf routine somewhere).
 *
 * Is little/big endian clean, but requires
 * 4 byte ints/longs. All the world's a Sun??
 */

#include "sos.h"

#if 0
#define XDR_DEBUG
#define DEBUG2
#define DEBUG3
#endif

/* Pack a variable of type type into buf.  Worry about non-word aligned buf.  Update buf ptr */
#define PACK(type, buf, val) do { type foo = val; memcpy(buf,(char *)&foo,sizeof(type)); buf+=sizeof(type); } while(0)
/* Copy a variable from src to dest.  Update dest ptr */
#define MPACK(dest, src, len) do { memcpy(dest, src, len); dest += len; } while (0)

/* Unpack a variable from buf into a variable of type type while doing a conv conversion. */
#define UNPACK(type, buf, val, conv) do { memcpy((char *)&val, buf, sizeof(type)); val = conv(val); buf+=sizeof(type); } while(0)

/* Unpack a variable from buf into a dereferenced variable of type type while doing conversion. */
#define PUNPACK(type, buf, pval, conv) do { type *packptr = pval; memcpy((char *)packptr, buf, sizeof(type)); *packptr = conv(*packptr); buf+=sizeof(type); } while(0)

/* Set src pointer to point into dest.  Update src ptr */
#define MUNPACK(dest, src, len) do { char **packptr = dest; *packptr = src; src += len; } while (0)


/* Well, there is no int conversion, so make a level of indirection */
#define SOS_HTONL htonl
#define SOS_HTONI (int)htonl
#define SOS_HTONS htons
#define SOS_NTOHL ntohl
#define SOS_NTOHI (int)ntohl
#define SOS_NTOHS ntohs


/* Buffer containing information to/from wire */
static sos_string staticspace;

/* Private functions */
static sos_string *sos_xdr_sencode_int(char *fmt, va_list args);
static int sos_xdr_sdecode_int(char *buf, char *fmt, va_list args);


/*
 * Encode data represented by fmt into a buffer
 * which is returned.  The buffer will be freed
 * if you call sos_xdr_sencode, sos_xdr_wencode,
 * sos_xdr_wdecode, or sos_xdr_freebuf
 */
sos_string *sos_xdr_sencode(char *fmt, ...)
{
  va_list args;
  sos_string *ret;

  va_start(args,fmt);
  ret = sos_xdr_sencode_int(fmt, args);
  va_end(args);

  return(ret);
}

static sos_string *sos_xdr_sencode_int(char *fmt, va_list args)
{
  va_list saveargs;
  char *curloc;
  int length = 0;
  int err = 0;
  char *ptr;
  int tmplen;
  char *tmpstr;
  sos_string *tmpbuf;
  int argcount = 0;

  sos_xdr_freebuf();

  length += 4;			/* Number of arguments */

  saveargs = args;

#ifdef DEBUG3
  fprintf(stderr,"Format is %s\n",fmt);
#endif /*DEBUG3*/

  /* Compute size and do error checking */
  for(ptr=fmt;*ptr;ptr++)
    {
      switch(*ptr)
	{
	case 'i':		/* Int */
	  argcount++;
	  (void)va_arg(args,int);
	  length += 4;		/* Size of data */
	  length++;		/* data type */
	  break;

	case 'l':		/* Long */
	  argcount++;
	  (void)va_arg(args,long);
	  length += 4;		/* Size of data */
	  length++;		/* data type */
	  break;

	case 's':		/* Short */
	  argcount++;
	  (void)va_arg(args,short);
	  length += 2;		/* Size of data */
	  length++;		/* data type */
	  break;

	case 'c':		/* Char */
	  argcount++;
	  (void)va_arg(args,char);
	  length += 1;		/* Size of data */
	  length++;		/* data type */
	  break;

	case 'S':		/* String */
	  argcount++;
	  tmpstr = va_arg(args,char *);
#ifdef DEBUG3
	  fprintf(stderr,"len tmpsrn = %d (len is %d)\n",strlen(tmpstr),length);
#endif /*DEBUG3*/
	  length += strlen(tmpstr)+1; /* Need to find the length */
	  length += 4;		/* Length of length :-) */
	  length++;		/* Data type */
	  break;

	case 'B':		/* Buffer */
	  argcount++;
	  tmpbuf = va_arg(args,sos_string *);
	  length += tmpbuf->len;/* Need to find the length */
#ifdef DEBUG3
	  fprintf(stderr,"len tmpbuf = %d (len is %d)\n",tmpbuf->len,length);
#endif /*DEBUG3*/
	  length += 4;		/* Length of length :-) */
	  length++;		/* Data type */
	  break;
	  
	default:
	  err++;		/* Invalid format */
	}
    }
  if (err)
    {
#ifdef DEBUG2
      fprintf(stderr,"type buffer invalid");
#endif /*DEBUG2*/
      return(NULL);
    }

  if ((staticspace.str = (char *)malloc(length)) == NULL)
    {
#ifdef DEBUG2
      perror("Malloc");
      fprintf(stderr,"Mallocing %d bytes\n",length);
#endif /*DEBUG2*/
      return(NULL);
    }

  curloc = staticspace.str;
  staticspace.len = length;

  PACK(int, curloc, SOS_HTONI(argcount));

  /* XDR encoding */
  for(ptr=fmt;*ptr;ptr++)
    {
      switch(*ptr)
	{
	case 'i':		/* Int */
	  PACK(char, curloc, *ptr);
	  PACK(int, curloc, SOS_HTONI(va_arg(saveargs, int)));
	  break;

	case 'l':		/* Long */
	  PACK(char, curloc, *ptr);
	  PACK(long, curloc, SOS_HTONL(va_arg(saveargs, long)));
	  break;

	case 's':		/* Short */
	  PACK(char, curloc, *ptr);
	  PACK(short, curloc, (short)SOS_HTONS(va_arg(saveargs, int)));
	  break;

	case 'c':		/* Char */
	  PACK(char, curloc, *ptr);
	  PACK(char, curloc, (char)(va_arg(saveargs, int)));
	  break;

	case 'S':		/* String */
	  PACK(char, curloc, *ptr);
	  tmpstr = va_arg(saveargs,char *);
	  tmplen = strlen(tmpstr)+1;
	  PACK(int, curloc, SOS_HTONI(tmplen));
	  MPACK(curloc,tmpstr,tmplen);
	  break;

	case 'B':		/* Buffer */
	  PACK(char, curloc, *ptr);
	  tmpbuf = va_arg(saveargs,sos_string *);
	  PACK(int, curloc, SOS_HTONI(tmpbuf->len));
	  MPACK(curloc, tmpbuf->str, tmpbuf->len);
	  break;

	default:
	  err++;		/* Invalid format */
	}
    }

  if (err)
    {
#ifdef DEBUG2
      fprintf(stderr,"Type buffer invalid (How did it get past the first check??");
#endif /*DEBUG2*/
      sos_xdr_freebuf();
      return(NULL);
    }

  va_end(args);
  va_end(saveargs);

  return(&staticspace);
}


/*
 * Encode the data formatted by fmt and send it over the wire (fd) using wfun
 *
 * Returns 0 on write returning 0
 * Returns -1 on any error
 * Returns number of bytes written
 *
 * (if fd is NBIO, you must provide a wfun which takes care of that)
 */
int sos_xdr_wencode(int fd, int (*wfun)(int fd, caddr_t buf, __SIZE_TYPE__ len), char *fmt, ...)
{
  va_list args;
  sos_string *buf;
  int cnt = 0;
  int netlen;
  int ret;

  va_start(args,fmt);

  buf = sos_xdr_sencode_int(fmt, args);
  va_end(args);

  if (!buf)
    {
#ifdef DEBUG2
      fprintf(stderr,"Nothing encoded\n");
#endif /*DEBUG2*/
      return(0);	
    }

#ifdef DEBUG2
fprintf(stderr,"XDRBUFLEN = %d\n",buf->len);
#endif /*DEBUG2*/

  /* Send over length of data */
  netlen = SOS_HTONI(buf->len);
  cnt = 0;
  while (cnt < sizeof(int))
    {
      ret = (*wfun)(fd, ((char *)(&netlen)) + cnt, sizeof(int) - cnt);

      if (ret < 1)
	{
#ifdef DEBUG2
	  perror("write1");
#endif /*DEBUG2*/
	  sos_xdr_freebuf();
	  return(ret);
	}

      cnt += ret;
    }

#ifdef DEBUG2
fprintf(stderr,"Sending over data\n");
#endif /*DEBUG2*/

  /* Send over data */
  cnt = 0;
  while (cnt < buf->len)
    {
      ret = (*wfun)(fd, buf->str + cnt, buf->len - cnt);

      if (ret < 1)
	{
#ifdef DEBUG2
	  perror("write");
#endif /*DEBUG2*/
	  sos_xdr_freebuf();
	  return(ret);
	}

      cnt += ret;
    }

  sos_xdr_freebuf();
  return(cnt);
}




/*
 * Decode a buffer containing xdred data in fmt
 *
 * Returns -1 on error
 * Returns number of arguments decoded
 */
int sos_xdr_sdecode(char *buf, char *fmt, ...)
{
  va_list args;
  int ret;

  va_start(args,fmt);
  ret = sos_xdr_sdecode_int(buf, fmt, args);
  va_end(args);

  return(ret);
}

int sos_xdr_sdecode_int(char *buf, char *fmt, va_list args)
{
  char *curloc;
  int err = 0;
  char *ptr;
  int tmplen;
  sos_string *tmpbuf;
  int argcount = 0;
  int argfound = 0;
  char type;

  curloc = buf;

  UNPACK(int, curloc, argcount, SOS_NTOHI);

  /* XDR decoding */
  for(ptr=fmt;*ptr;ptr++)
    {
      switch(*ptr)
	{
	case 'i':		/* Int */
	  UNPACK(char, curloc, type, (char));
	  if (type != *ptr) { err++; break; }
	  PUNPACK(int, curloc, (va_arg(args, int *)), SOS_NTOHI);
	  argfound++;
	  break;

	case 'l':		/* Long */
	  UNPACK(char, curloc, type, (char));
	  if (type != *ptr) { err++; break; }
	  PUNPACK(long, curloc, (va_arg(args, long *)), SOS_NTOHL);
	  argfound++;
	  break;

	case 's':		/* Short */
	  UNPACK(char, curloc, type, (char));
	  if (type != *ptr) { err++; break; }
	  PUNPACK(short, curloc, (va_arg(args, short *)), SOS_NTOHS);
	  argfound++;
	  break;

	case 'c':		/* Char */
	  UNPACK(char, curloc, type, (char));
	  if (type != *ptr) { err++; break; }
	  PUNPACK(char, curloc, (va_arg(args, char *)), (char));
	  argfound++;
	  break;

	case 'S':		/* String */
	  UNPACK(char, curloc, type, (char));
	  if (type != *ptr) { err++; break; }
	  UNPACK(int, curloc, tmplen, SOS_NTOHI);
	  MUNPACK((va_arg(args, char **)), curloc, tmplen);
	  argfound++;
	  break;

	case 'B':		/* Buffer */
	  UNPACK(char, curloc, type, (char));
	  if (type != *ptr) { err++; break; }
	  tmpbuf = va_arg(args, sos_string *);
	  UNPACK(int, curloc, tmpbuf->len, SOS_NTOHI);
#ifdef XDR_DEBUG
	  {
	    char *p;
	    int i;
	    printf("sdecode_int(): Decoding Buffer:\n");
	    printf("Length: %d\nBuffer: **", tmpbuf->len);
	    for (p=curloc,i=0; i < tmpbuf->len; p++,i++)
	      printf("%c", *p);
	    printf("**\n");
	  }
#endif /* XDR_DEBUG */	  
	  MUNPACK((&(tmpbuf->str)), curloc, tmpbuf->len);
	  argfound++;
	  break;

	default:
	  err++;		/* Invalid format */
	}
      if (err) break;
    }

  if (err)
    {
      return(-1);
    }

  if (argfound != argcount)
    {
      return(-1);
    }

  va_end(args);

  return(argfound);
}


/*
 * Decode from wire
 *
 * Read in the data, unpack it, return number of arguments parsed to user
 *
 * Returns -1 on any error
 * Returns 0 on EOF on fd
 *
 * All pointer types (string, sos_string) are set to point into a
 * space which is freed if you call sos_xdr_sencode, sos_xdr_wencode,
 * sos_xdr_wdecode, or sos_xdr_freebuf People wanting permanence
 * should copy the data.
 */

int sos_xdr_wdecode(int fd, int (*rfun)(int fd, caddr_t buf, __SIZE_TYPE__ len), char *fmt, ...)
{
  va_list args;
  int cnt = 0;
  int ret; 
  int netlen;

#ifdef DEBUG2
fprintf(stderr,"Wire decode\n");
#endif /*DEBUG2*/

  sos_xdr_freebuf();

  /* read the length of the xdr buffer */
  cnt = 0;
  while (cnt < sizeof(int))
    {
      ret = (*rfun)(fd, ((char *)(&netlen)) + cnt, sizeof(int) - cnt);

      if (ret < 1)
	{
#ifdef DEBUG2
fprintf(stderr,"fooe %d\n",ret);
#endif
	  return(ret);
	}

      cnt += ret;
    }

  staticspace.len = SOS_NTOHI(netlen);

#ifdef DEBUG2
fprintf(stderr,"XDR buflen = %d\n",staticspace.len);
#endif /*DEBUG2*/

  if (sos_allocstr(&staticspace, staticspace.len) == NULL)
    {
#ifdef DEBUG2
      perror("alloc");
#endif
      return(-1);
    }

#ifdef DEBUG2
fprintf(stderr,"B4read\n");
#endif /*DEBUG2*/

  /* read the buffer */
  cnt = 0;
  while (cnt < staticspace.len)
    {
      ret = (*rfun)(fd, staticspace.str + cnt, staticspace.len - cnt);

      if (ret < 1)
	{
#ifdef DEBUG2
fprintf(stderr,"foor %d\n",ret);
#endif
	  sos_xdr_freebuf();
	  return(ret);
	}

      cnt += ret;
    }

  va_start(args,fmt);
  ret = sos_xdr_sdecode_int(staticspace.str, fmt, args);
  va_end(args);

#ifdef DEBUG2
fprintf(stderr,"food %d\n",ret);
#endif

  if (ret < 0)
    sos_xdr_freebuf();


  return(ret);
}


/*
 * Free buffer allocated by sos_xdr_wdecode()
 */
void sos_xdr_freebuf()
{
  if (staticspace.str)
    {
      free(staticspace.str);
      staticspace.str = NULL;
      staticspace.len = 0;
    }
}
