/*
 * Copyright 1993, 1994 by Ulrich Khn. All rights reserved.
 *
 * THIS PROGRAM COMES WITH ABSOLUTELY NO WARRANTY, NOT
 * EVEN THE IMPLIED WARRANTIES OF MERCHANTIBILITY OR
 * FITNESS FOR A PARTICULAR PURPOSE. USE AT YOUR OWN
 * RISK.
 */

/*
 * xdr.c functions dealing with the XDR standard
 *
 * Copyright Ulrich Khn, 1993 
 */


#include <string.h>
#include "types.h"
#include "xdr.h"




static void *
dummy_alloc(long len)
{
	return NULL;
}


static void
dummy_free(void *addr)
{
	return;
}



bool_t
xdr_init(xdrs *s, char *buffer, long len, int op, MEMSVC *ms)
{
	s->data = s->current = buffer;
	s->length = len;
	s->op = op;
	if (ms)
	{
		s->x_malloc = ms->alloc;
		s->x_free = ms->free;
	}
	else
	{
		s->x_malloc = dummy_alloc;
		s->x_free = dummy_free;
	}
	return TRUE;
}


long
xdr_getpos(xdrs *x)
{
	if (x->length >= 0)
		return (long)(x->current) - (long)(x->data);
	return 0;
}


bool_t
xdr_setpos(xdrs *x, long pos)
{
	if (x->length <= pos)
		return FALSE;
	x->length -= pos;
	x->current += pos;
	return TRUE;
}


long *
xdr_inline(xdrs *x, long len)
{
	long *p;

	if (len <= x->length)
	{
		p = (long*)x->current;
		x->length -= len;

		x->current += len;
		return p;
	}
	return FALSE;
}


bool_t
xdr_void(xdrs *x, ...)
{
	/* nothing to do */
	if (x->length >= 0)
		return TRUE;
	return FALSE;
}


bool_t
xdr_long(xdrs *x, long *val)
{
	if (x->length < sizeof(long))
		return FALSE;
	if (XDR_DECODE == x->op)
		*val = *(long*)x->current;
	else if (XDR_ENCODE == x->op)
		*(long*)x->current = *val;
	else if (XDR_FREE == x->op)
		return TRUE;
	else
		return FALSE;
	x->current += sizeof(long);
	x->length -= sizeof(long);
	return TRUE;
}


bool_t
xdr_enum(xdrs *x, enum_t *val)
{
	if (x->length < sizeof(u_long))
		return FALSE;
	if (XDR_DECODE == x->op)
		*val = *(u_long*)x->current;
	else if (XDR_ENCODE == x->op)
		*(u_long*)x->current = *val;
	else if (XDR_FREE == x->op)
		return TRUE;
	else
		return FALSE;
	x->current += sizeof(u_long);
	x->length -= sizeof(u_long);
	return TRUE;
}


bool_t
xdr_bool(xdrs *x, bool_t *val)
{
	if (x->length < sizeof(u_long))
		return FALSE;
	if (XDR_DECODE == x->op)
		*val = *(u_long*)x->current;
	else if (XDR_ENCODE == x->op)
		*(u_long*)x->current = *val;
	else if (XDR_FREE == x->op)
		return TRUE;
	else
		return FALSE;
	x->current += sizeof(u_long);
	x->length -= sizeof(u_long);
	return TRUE;
}


bool_t
xdr_ulong(xdrs *x, u_long *val)
{
	if (x->length < sizeof(u_long))
		return FALSE;
	if (XDR_DECODE == x->op)
		*val = *(u_long*)x->current;
	else if (XDR_ENCODE == x->op)
		*(u_long*)x->current = *val;
	else if (XDR_FREE == x->op)
		return TRUE;
	else
		return FALSE;
	x->current += sizeof(u_long);
	x->length -= sizeof(u_long);
	return TRUE;
}


bool_t
xdr_string(xdrs *x, char **cpp, long maxlen)
{
	long fringe;
	char *p;

	if (x->length < sizeof(u_long))
		return FALSE;
	if (XDR_DECODE == x->op)
	{
		u_long l = *(u_long*)x->current;

		x->current += sizeof(u_long);
		x->length -= sizeof(u_long);
		fringe = l & 0x03;
		if (fringe)
			fringe = 4-fringe;
		if (x->length < l+fringe)
			return FALSE;
		x->length -= l+fringe;
		if (l > maxlen)
			return FALSE;

		memcpy(*cpp, x->current, l);
		*(*cpp + l) = '\0';
		x->current += l+fringe;

		return TRUE;
	}
	else if (XDR_ENCODE == x->op)
	{
		u_long l = strlen(*cpp);

		*(u_long*)x->current = l;
		x->current += sizeof(u_long);
		x->length -= sizeof(u_long);
		fringe = l & 0x03;
		if (fringe)
			fringe = 4-fringe;
		if (x->length < l+fringe)
			return FALSE;
		x->length -= l+fringe;
		if (l > maxlen)
			return FALSE;

		memcpy(x->current, *cpp, l);
		p = x->current + l;
		for ( ;  fringe;  fringe--)
			*p++ = '\0';
		x->current = p;

		return TRUE;
	}
	else if (XDR_FREE == x->op)
		return TRUE;
	else
		return FALSE;
}


bool_t
xdr_opaque(xdrs *x, opaque **opp, long *len, long maxlen)
{
	long fringe;
	char *p;

	if (x->length < sizeof(u_long))
		return FALSE;
	if (XDR_DECODE == x->op)
	{
		u_long l = *(u_long*)x->current;

		x->current += sizeof(u_long);
		x->length -= sizeof(u_long);
		*len = l;
		fringe = l & 0x03;
		if (fringe)
			fringe = 4-fringe;
		if (x->length < l+fringe)
			return FALSE;
		if (l > maxlen)
			return FALSE;
		x->length -= l+fringe;
		memcpy(*opp, x->current, l);
		x->current += l+fringe;
		return TRUE;
	}
	else if (XDR_ENCODE == x->op)
	{
		u_long l = *len;

		*(u_long*)x->current = l;

		x->current += sizeof(u_long);
		x->length -= sizeof(u_long);
		fringe = l & 0x03;
		if (fringe)
			fringe = 4-fringe;
		if (x->length < l+fringe)
			return FALSE;
		x->length -= l+fringe;
		if (l > maxlen)
			return FALSE;
		memcpy(x->current, *opp, l);
		p = (char*)((long)x->current+l);
		for ( ;  fringe;  fringe--)
			*p++ = 0;
		x->current = p;
		return TRUE;
	}
	else if (XDR_FREE == x->op)
		return TRUE;
	else
		return FALSE;
}


bool_t
xdr_fixedopaq(xdrs *x, opaque *val, long len)
{
	u_long fringe;
	char *p, *s;

	fringe = len & 0x03;
	if (fringe)
		fringe = 4-fringe;
	if (x->length < len+fringe)
		return FALSE;
	p = x->current;
	s = (char*)val;
	if (XDR_DECODE == x->op)
	{
		x->length -= len + fringe;

		for ( ;  len;  len--)
			*s++ = *p++;
		p += fringe;
	}
	else if (XDR_ENCODE == x->op)
	{
		x->length -= len + fringe;

		for ( ;  len;  len--)
			*p++ = *s++;
		for ( ;  fringe;  fringe--)
			*p++ = 0;
	}
	else if (XDR_FREE == x->op)
		return TRUE;
	else
		return FALSE;
	x->current = p;
	return TRUE;
}



bool_t
xdr_pointer(xdrs *x, char **objpp, long objlen, xdrproc_t proc)
{
	char *where = *objpp;
	bool_t more_data;

	more_data = (where != NULL);
	if (!xdr_bool(x, &more_data))
		return FALSE;
	if (!more_data)
	{
		*objpp = NULL;
		return TRUE;
	}
	return (*proc)(x, where);
}
