/* (C) Copyright International Business Machines Corporation 23 January */
/* 1990.  All Rights Reserved. */
/*  */
/* See the file USERAGREEMENT distributed with this software for full */
/* terms and conditions of use. */
/* File: pilxdr.c */
/* Author: David F. Bacon */
#ifndef lint
static char sccsinfo[] = "@(#)pilxdr.c	1.6 1/13/92";
#endif

#define _BSD 43

#include <rpc/rpc.h>

#include "ops.h"
#include "recursiv.h"
#include "storage.h"
#include "shape.h"

xdr_status
hxdr_outport(xdrs, objp, shape)
XDR *xdrs;
objectp objp;
shapep shape;
{
    predef_exception remport_enq();

    extern interpname localaddr;
    remote_address rem, *remptr;

    if (shape->tsdr is &dr_bottom and xdrs->x_op isnt XDR_FREE) 
      shape->tsdr = objp->tsdr;

    switch (xdrs->x_op) {
    case XDR_ENCODE: {
      if (objp->value.outport->type is RemotePort)
        remptr = & objp->value.outport->info.remote;
      else {
        remptr = &rem;
        make_local_remaddr(remptr, (remaddr) objp->value.outport);
      }
      break;
    }
      
    case XDR_DECODE: {
      if ((objp->value.outport = new(channel)) is nil)
        goto cleanup;
      remptr = & objp->value.outport->info.remote;
      break;
    }

    case XDR_FREE: {
      if (objp->value.outport is nil)
        return(XDR_OK);
      remptr = & objp->value.outport->info.remote;
      break;
    }
    }

    if (not hxdr_remote_address(xdrs, & remptr))
      goto cleanup;

    if (xdrs->x_op is XDR_DECODE) 
      if (compare_interpnames(remptr, & localaddr) is SUCCESS) {
        dispose(objp->value.outport, channel);
        objp->value.outport = (channel *) remptr->addr;
      }
      else {
        objp->value.outport->type = RemotePort;
        objp->value.outport->port_enq = remport_enq;
        objp->value.outport->disconnected = FALSE;
        objp->value.outport->refcount = 1;
      }
    else if (xdrs->x_op is XDR_FREE) {
      dispose(objp->value.outport, channel);
    }

    return(XDR_OK);

  cleanup: {
    abort_nili("hxdr_outport");
    /*NOTREACHED*/
  }
}


xdr_status
hxdr_remote_address(xdrs, rem)
XDR *xdrs;
remote_address **rem;
{
    if (xdrs->x_op is XDR_DECODE and *rem is nil)
      if ((*rem = new(remote_address)) is nil)
        goto cleanup;

    if (not hxdr_interpname(xdrs, & (*rem)->interp) or
        not hxdr_remaddr(xdrs, & (*rem)->addr))
      goto cleanup;

    return(XDR_OK);

  cleanup:
    abort_nili("hxdr_remote_address");
    /*NOTREACHED*/
}

    
xdr_status
hxdr_interpname(xdrs, interp)
XDR *xdrs;
interpname *interp;
{
    char *hp;

    hp = interp->hostname;

    if (not xdr_opaque(xdrs, hp, HOSTIDSIZE) or
        not xdr_u_int(xdrs, & interp->number))
      abort_nili("hxdr_interpname");

    return(XDR_OK);
}



xdr_status
hxdr_remaddr(xdrs, addr)
XDR *xdrs;
remaddr *addr;
{
    return(xdr_u_long(xdrs, addr));
}


xdr_status
hxdr_callmessage(xdrs, cmsg, shape)
XDR *xdrs;
valcell *cmsg;
shapep shape;
{
    extern interpname localaddr;
    remote_address rem, *remptr;
    counter size;
    int i;
    flag decode_to_local;
    callmessage_info *info;


    switch (xdrs->x_op) {
      case XDR_ENCODE: {
        size = cmsg->callmessage->info.callmessage->size;

        if (not cmsg->callmessage->info.callmessage->local)
          remptr = & cmsg->callmessage->info.callmessage->cminfo.remote;
        else {
	      remptr = &rem;
	      make_local_remaddr(remptr, (remaddr) cmsg->callmessage);
        }
        break;
      }

      case XDR_DECODE: {
        remptr = nil;
        break;
      }

      case XDR_FREE: {
        if (cmsg->callmessage is nil)
          return(XDR_OK);
        if (cmsg->callmessage->info.callmessage is nil)
          return(XDR_OK);

        size = cmsg->callmessage->info.callmessage->size;
        for (i = 0; i < size; i++) {
          if (not hxdr_object(xdrs, & cmsg->callmessage->data[i],
                              shape))
            return(XDR_FAIL);
        }

        dispose(cmsg->callmessage->info.callmessage, callmessage_info);
        freedotmain(cmsg->callmessage, size);

        return(XDR_OK);
        break;
      }
    }

    /* if (xdrs->x_op isnt XDR_FREE) */
    if (not hxdr_remote_address(xdrs, & remptr))
      goto cleanup;
    
    if (shape->tsdr is &dr_bottom) {
      if (not xdr_u_int(xdrs, &size))
        goto cleanup;
      if ((shape->value.callmessage = 
           (shape_callmessage *) getmain((counter) sizeof(shape_callmessage)+
                                         sizeof(shapeobj)*size))
          is nil)
        goto cleanup;

      shape->value.callmessage->size = size;
      for (i=0; i<size; i++)
        shape->value.callmessage->data[i].tsdr = &dr_bottom;
      shape->tsdr = &dr_callmessage;
    }
    else
      size = shape->value.callmessage->size;

    decode_to_local = FALSE;

    if (xdrs->x_op is XDR_DECODE) 
      if (compare_interpnames(remptr, & localaddr) is SUCCESS) {
        decode_to_local = TRUE;

        cmsg->callmessage = (dfd_callmessage *) remptr->addr;
        dispose(remptr, remote_address);
      }
      else {
        if ((cmsg->callmessage = getdotmain(size)) is nil)
          goto cleanup;
        if ((info = new(callmessage_info)) is nil)
          goto cleanup;
        
        cmsg->callmessage->info.callmessage = info;
        info->local = FALSE;
        info->size = size;
        info->cminfo.remote = *remptr;
        dispose(remptr, remote_address);
      }
    
    for (i = 0; i < size; i++) {
      if (decode_to_local) 
        re_finalize(& cmsg->callmessage->data[i], F_FREE,
                    (schedblock*) nil);
      if (not hxdr_object(xdrs, & cmsg->callmessage->data[i],
                          & shape->value.callmessage->data[i]))
        goto cleanup;
    }

    return(XDR_OK);

    cleanup: {
      abort_nili("hxdr_callmessage");
      /*NOTREACHED*/
    }
}

void
free_shape_callmessage(shape)
shapep shape;
{
  int i, size;

  if (shape->value.callmessage isnt nil) {
    size = shape->value.callmessage->size;
    for (i = 0; i < size; i++)
      free_shape(& shape->value.callmessage->data[i]);
    freemain(shape->value.callmessage, 
             sizeof(shape_callmessage)+sizeof(shapeobj)*size);
  }
}
