/* 
 * xdr_standalone.C
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1993,1991,1990,1996  Arizona Board of Regents
 */

#include "xrpc.h"
#include "xdr_standalone.h"

/* all of this taken from the 4.4BSD sources */
/* xdr_long and xdr_u_long have to be makde 64 bit clean */

#define XDR_FALSE	((long) 0)
#define XDR_TRUE	((long) 1)
#define LASTUNSIGNED	((u_int) 0-1)

static char xdr_zero[BYTES_PER_XDR_UNIT] = { 0, 0, 0, 0 };

#if 1

static bool_t	xdrmem_getlong(XDR *xdrs, long *lp);
static bool_t	xdrmem_putlong(XDR *xdrs, long *lp);
static bool_t	xdrmem_getbytes(XDR *xdrs, caddr_t addr, int len);
static bool_t	xdrmem_putbytes(XDR *xdrs, caddr_t addr, int len);
static u_int	xdrmem_getpos(XDR *xdrs);
static bool_t	xdrmem_setpos(XDR *xdrs, u_int pos);
static int *	xdrmem_inline(XDR *xdrs, int len);
static void	xdrmem_destroy(XDR *xdrs);


static struct	xdr_ops xdrmem_ops = {
	xdrmem_getlong,
	xdrmem_putlong,
	xdrmem_getbytes,
	xdrmem_putbytes,
	xdrmem_getpos,
	xdrmem_setpos,
	xdrmem_inline,
	xdrmem_destroy
};

static void xdrmem_destroy(XDR *xdrs)
{
}

static bool_t
xdrmem_getlong(XDR *xdrs, long *lp)
{
  if ((xdrs->x_handy -= 4) < 0)
    return (FALSE);
  *lp = (long)ntohl((u_long)(*((long *)(xdrs->x_private))));
  xdrs->x_private += 4;
  return (TRUE);
}

static bool_t
xdrmem_putlong(XDR *xdrs, long *lp)
{

  if ((xdrs->x_handy -= 4) < 0)
    return (FALSE);
  *(long *)xdrs->x_private = (long)htonl((u_long)(*lp));
  xdrs->x_private += 4;
  return (TRUE);
}

static bool_t
xdrmem_getbytes(XDR *xdrs, caddr_t addr, int len)
{

  if ((xdrs->x_handy -= len) < 0)
    return (FALSE);
  bcopy(xdrs->x_private, addr, len);
  xdrs->x_private += len;
  return (TRUE);
}

static bool_t
xdrmem_putbytes(XDR *xdrs, caddr_t addr, int len)
{

  if ((xdrs->x_handy -= len) < 0)
    return (FALSE);
  bcopy(addr, xdrs->x_private, len);
  xdrs->x_private += len;
  return (TRUE);
}

static u_int
xdrmem_getpos(XDR *xdrs)
{

  return (u_int)(xdrs->x_private - xdrs->x_base);
}

static bool_t
xdrmem_setpos(XDR *xdrs, u_int pos)
{
  register caddr_t newaddr = xdrs->x_base + pos;
  register caddr_t lastaddr = xdrs->x_private + xdrs->x_handy;
  
  if ((long)newaddr > (long)lastaddr)
    return (FALSE);
  xdrs->x_private = newaddr;
  xdrs->x_handy = (int)(lastaddr - newaddr);
  return (TRUE);
}

static int *
xdrmem_inline(XDR *xdrs, int len)
{
  int *buf = 0;

  if (xdrs->x_handy >= len) {
    xdrs->x_handy -= len;
    buf = (int *) xdrs->x_private;
    xdrs->x_private += len;
  }
  return (buf);
}

void xdr_free (xdrproc_t proc, char *objp)
{
  XDR x;
  
  x.x_op = XDR_FREE;
  (*proc)(&x, (caddr_t *)objp);
}

void xdrmem_create (XDR*xdrs, char *addr, u_int size, enum xdr_op op)
{
  xdrs->x_op = op;
  xdrs->x_ops = &xdrmem_ops;
  xdrs->x_private = xdrs->x_base = addr;
  xdrs->x_handy = size;
}

#endif

bool_t xdr_short(XDR *xdrs, short *sp)
{
  long l;

  switch (xdrs->x_op) {

  case XDR_ENCODE:
    l = (long) *sp;
    return (XDR_PUTLONG(xdrs, &l));

  case XDR_DECODE:
    if (!XDR_GETLONG(xdrs, &l)) {
      return (FALSE);
    }
    *sp = (short) l;
    return (TRUE);

  case XDR_FREE:
    return (TRUE);
  }
  return (FALSE);
}

bool_t xdr_opaque (XDR *xdrs, char *cp, u_int cnt)
{
  register u_int rndup;
  static crud[BYTES_PER_XDR_UNIT];

  /*
   * if no data we are done
   */
  if (cnt == 0)
    return (TRUE);

  /*
   * round byte count to full xdr units
   */
  rndup = cnt % BYTES_PER_XDR_UNIT;
  if (rndup > 0)
    rndup = BYTES_PER_XDR_UNIT - rndup;

  if (xdrs->x_op == XDR_DECODE) {
    if (!XDR_GETBYTES(xdrs, cp, cnt)) {
      return (FALSE);
    }
    if (rndup == 0)
      return (TRUE);
    return (XDR_GETBYTES(xdrs, (caddr_t)crud, rndup));
  }

  if (xdrs->x_op == XDR_ENCODE) {
    if (!XDR_PUTBYTES(xdrs, cp, cnt)) {
      return (FALSE);
    }
    if (rndup == 0)
      return (TRUE);
    return (XDR_PUTBYTES(xdrs, xdr_zero, rndup));
  }

  if (xdrs->x_op == XDR_FREE) {
    return (TRUE);
  }

  return (FALSE);
}

bool_t xdr_enum (XDR *xdrs, enum_t *ep)
{
  (void) (xdr_short(xdrs, (short *)ep));
  return xdr_int(xdrs, (int *)ep);
}

bool_t xdr_int (XDR *xdrs, int *ip)
{
  long l;

  switch (xdrs->x_op) {

  case XDR_ENCODE:
    l = (long) *ip;
    return (XDR_PUTLONG(xdrs, &l));

  case XDR_DECODE:
    if (!XDR_GETLONG(xdrs, &l)) {
      return (FALSE);
    }
    *ip = (int) l;
    return (TRUE);

  case XDR_FREE:
    return (TRUE);
  }
  return (FALSE);
}

bool_t xdr_u_int (XDR *xdrs, unsigned *up)
{
  unsigned long l;

  switch (xdrs->x_op) {

  case XDR_ENCODE:
    l = (long) *up;
    return (XDR_PUTLONG(xdrs, &l));

  case XDR_DECODE:
    if (!XDR_GETLONG(xdrs, &l)) {
      return (FALSE);
    }
    *up = (unsigned) l;
    return (TRUE);

  case XDR_FREE:
    return (TRUE);
  }
  return (FALSE);
}

bool_t xdr_u_long (XDR *xdrs, unsigned long *ulp)
{
  
  if (xdrs->x_op == XDR_DECODE)
    return (XDR_GETLONG(xdrs, (long *)ulp));
  if (xdrs->x_op == XDR_ENCODE)
    return (XDR_PUTLONG(xdrs, (long *)ulp));
  if (xdrs->x_op == XDR_FREE)
    return (TRUE);
  return (FALSE);
}

bool_t xdr_long(XDR *xdrs, long *lp)
{

  if (xdrs->x_op == XDR_ENCODE)
    return (XDR_PUTLONG(xdrs, lp));

  if (xdrs->x_op == XDR_DECODE)
    return (XDR_GETLONG(xdrs, lp));

  if (xdrs->x_op == XDR_FREE)
    return (TRUE);

  return (FALSE);
}

bool_t xdr_bytes (XDR *xdrs, char **cpp, u_int *sizep, u_int maxsize)
{
  register char *sp = *cpp;	/* sp is the actual string pointer */
  register u_int nodesize;

  /*
   * first deal with the length since xdr bytes are counted
   */
  if (! xdr_u_int(xdrs, sizep)) {
    return (FALSE);
  }
  nodesize = *sizep;
  if ((nodesize > maxsize) && (xdrs->x_op != XDR_FREE)) {
    return (FALSE);
  }

  /*
   * now deal with the actual bytes
   */
  switch (xdrs->x_op) {

  case XDR_DECODE:
    if (nodesize == 0) {
      return (TRUE);
    }
    if (sp == NULL) {
      *cpp = sp = (char *)mem_alloc(nodesize);
    }
    if (sp == NULL) {
      /* (void) fprintf(stderr, "xdr_bytes: out of memory\n"); */
      return (FALSE);
    }
    /* fall into ... */

  case XDR_ENCODE:
    return (xdr_opaque(xdrs, sp, nodesize));

  case XDR_FREE:
    if (sp != NULL) {
      mem_free(sp, nodesize);
      *cpp = NULL;
    }
    return (TRUE);
  }
  return (FALSE);
}

bool_t xdr_union (XDR *xdrs, int *dscmp, char *unp, 
		  struct xdr_discrim *choices,
		  bool_t (*dfault)())
{
  register enum_t dscm;

  /*
   * we deal with the discriminator;  it's an enum
   */
  if (! xdr_enum(xdrs, dscmp)) {
    return (FALSE);
  }
  dscm = *dscmp;

  /*
   * search choices for a value that matches the discriminator.
   * if we find one, execute the xdr routine for that value.
   */
  for (; choices->proc != NULL_xdrproc_t; choices++) {
    if (choices->value == dscm)
      return ((*(choices->proc))(xdrs, (caddr_t *)unp, LASTUNSIGNED));
  }

  /*
   * no match - execute the default xdr routine if there is one
   */
  return ((dfault == NULL) ? FALSE :
	  (*dfault)(xdrs, unp, LASTUNSIGNED));
}

