/*
 * Copyright (c) 2009, Sun Microsystems, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 * - Neither the name of Sun Microsystems, Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * xdr_stdio.c, XDR implementation on standard i/o file.
 *
 * Copyright (C) 1984, Sun Microsystems, Inc.
 *
 * This set of routines implements a XDR on a stdio stream.
 * XDR_ENCODE serializes onto the stream, XDR_DECODE de-serializes
 * from the stream.
 */

#include <stdio.h>

#include <rpc/types.h>
#include <rpc/xdr.h>

#include "xdr_private.h"

#ifndef ntohl
# define ntohl(x) xdr_ntohl(x)
#endif
#ifndef htonl
# define htonl(x) xdr_htonl(x)
#endif

static void _EXFUN (xdrstdio_destroy, (XDR *));
static bool_t _EXFUN (xdrstdio_getlong, (XDR *, long *));
static bool_t _EXFUN (xdrstdio_putlong, (XDR *, _CONST long *));
static bool_t _EXFUN (xdrstdio_getbytes, (XDR *, char *, u_int));
static bool_t _EXFUN (xdrstdio_putbytes, (XDR *, _CONST char *, u_int));
static u_int _EXFUN (xdrstdio_getpos, (XDR *));
static bool_t _EXFUN (xdrstdio_setpos, (XDR *, u_int));
static int32_t * _EXFUN (xdrstdio_inline, (XDR *, u_int));
static bool_t _EXFUN (xdrstdio_getint32, (XDR*, int32_t *));
static bool_t _EXFUN (xdrstdio_putint32, (XDR*, _CONST int32_t *));

/*
 * Ops vector for stdio type XDR
 */
static _CONST struct xdr_ops xdrstdio_ops = {
  xdrstdio_getlong,             /* deseraialize a long int */
  xdrstdio_putlong,             /* seraialize a long int */
  xdrstdio_getbytes,            /* deserialize counted bytes */
  xdrstdio_putbytes,            /* serialize counted bytes */
  xdrstdio_getpos,              /* get offset in the stream */
  xdrstdio_setpos,              /* set offset in the stream */
  xdrstdio_inline,              /* prime stream for inline macros */
  xdrstdio_destroy,             /* destroy stream */
  xdrstdio_getint32,            /* deseraialize an int */
  xdrstdio_putint32             /* seraialize an long int */
};

/*
 * Initialize a stdio xdr stream.
 * Sets the xdr stream handle xdrs for use on the stream file.
 * Operation flag is set to op.
 */
void
_DEFUN (xdrstdio_create, (xdrs, file, op),
        XDR * xdrs _AND
	FILE * file _AND
	enum xdr_op op)
{
  xdrs->x_op = op;
  xdrs->x_ops = (struct xdr_ops *) &xdrstdio_ops;
  xdrs->x_private = (void *) file;
  xdrs->x_handy = 0;
  xdrs->x_base = 0;
}

/*
 * Destroy a stdio xdr stream.
 * Cleans up the xdr stream handle xdrs previously set up by xdrstdio_create.
 */
static void
_DEFUN (xdrstdio_destroy, (xdrs),
        XDR * xdrs)
{
  (void) fflush ((FILE *) xdrs->x_private);
  /* XXX: should we close the file ?? */
}

static bool_t
_DEFUN (xdrstdio_getlong, (xdrs, lp),
        XDR * xdrs _AND
	long *lp)
{
  u_int32_t temp;

  if (fread (&temp, sizeof (int32_t), 1, (FILE *) xdrs->x_private) != 1)
    return FALSE;
  *lp = (long) (int32_t) ntohl (temp);
  return TRUE;
}

static bool_t
_DEFUN (xdrstdio_putlong, (xdrs, lp),
        XDR * xdrs _AND
	_CONST long *lp)
{
  u_int32_t temp = htonl ((u_int32_t) * lp);

  if (fwrite (&temp, sizeof (int32_t), 1, (FILE *) xdrs->x_private) != 1)
    return FALSE;
  return TRUE;
}

static bool_t
_DEFUN (xdrstdio_getbytes, (xdrs, addr, len),
        XDR * xdrs _AND
        char *addr _AND
	u_int len)
{
  if ((len != 0) && (fread (addr, (size_t) len, 1,
                            (FILE *) xdrs->x_private) != 1))
    return FALSE;
  return TRUE;
}

static bool_t
_DEFUN (xdrstdio_putbytes, (xdrs, addr, len),
        XDR * xdrs _AND
        _CONST char *addr _AND
	u_int len)
{
  if ((len != 0) && (fwrite (addr, (size_t) len, 1,
                             (FILE *) xdrs->x_private) != 1))
    return FALSE;
  return TRUE;
}

static u_int
_DEFUN (xdrstdio_getpos, (xdrs),
        XDR * xdrs)
{
  return ((u_int) ftell ((FILE *) xdrs->x_private));
}

static bool_t
_DEFUN (xdrstdio_setpos, (xdrs, pos),
        XDR * xdrs _AND
        u_int pos)
{
  return ((fseek ((FILE *) xdrs->x_private, (long) pos, 0) < 0) ?
          FALSE : TRUE);
}

/* ARGSUSED */
static int32_t *
_DEFUN (xdrstdio_inline, (xdrs, len),
        XDR * xdrs _AND
	u_int len)
{
  /*
   * Must do some work to implement this: must insure
   * enough data in the underlying stdio buffer,
   * that the buffer is aligned so that we can indirect through a
   * long *, and stuff this pointer in xdrs->x_buf.  Doing
   * a fread or fwrite to a scratch buffer would defeat
   * most of the gains to be had here and require storage
   * management on this buffer, so we don't do this.
   */
  return NULL;
}

static bool_t
_DEFUN (xdrstdio_getint32, (xdrs, ip),
        XDR *xdrs _AND
	int32_t *ip)
{
  int32_t temp;

  if (fread (&temp, sizeof (int32_t), 1, (FILE *) xdrs->x_private) != 1)
    return FALSE;
  *ip = ntohl (temp);
  return TRUE;
}

static bool_t
_DEFUN (xdrstdio_putint32, (xdrs, ip),
        XDR *xdrs _AND
	_CONST int32_t *ip)
{
  int32_t temp = htonl (*ip);

  if (fwrite (&temp, sizeof (int32_t), 1, (FILE *) xdrs->x_private) != 1)
    return FALSE;
  return TRUE;
}