/* (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. */
#ifndef lint
static char sccsinfo[] = "@(#)xdr.c	1.17 2/26/92";
#endif

#ifdef _AIX
#  include <sys/types.h>
#  include <sys/select.h>
#endif
#include <rpc/rpc.h>

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

#include "accessors.h"
#include "interpform.cd"
#include "shape.h"

#define XDRBUFSIZE 1024

#define VARIANT_LENGTH (sizeof(dot_info) + sizeof(object))
#define PROGRAM_LENGTH (sizeof(dot_info) + (PROGRAM_SIZE * sizeof(object)))

/*ARGSUSED*/
xdr_status
hxdr_bottom(xdrs, objp, shape)
XDR *xdrs;
objectp objp;
shapep shape;
{
    return(xdr_void());
}


/*ARGSUSED*/
xdr_status
hxdr_noop(xdrs, objp, shape)
XDR *xdrs;
objectp objp;
shapep shape;
{
  if (shape->tsdr is &dr_bottom and xdrs->x_op isnt XDR_FREE)
    shape->tsdr = objp->tsdr;

  nilerror("hxdr_noop", "HXDR for this datatype unimplemented");
  return(XDR_FAIL);
}


#define xdr_enumval xdr_u_int

xdr_status
hxdr_enumeration(xdrs, objp, shape)
XDR *xdrs;
objectp objp;
shapep shape;
{
    xdr_status xdr_enumval();

    if (shape->tsdr is &dr_bottom and xdrs->x_op isnt XDR_FREE)
      shape->tsdr = objp->tsdr;
    
    return(xdr_enumval(xdrs, & objp->value.enumeration));
}


xdr_status
hxdr_boolean(xdrs, objp, shape)
XDR *xdrs;
objectp objp;
shapep shape;
{
    xdr_status myxdr_boolean();

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

    return(myxdr_boolean(xdrs, & objp->value.boolean));
}


xdr_status
myxdr_boolean(xdrs, boolp)
XDR *xdrs;
flag *boolp;
{
    flag num;

    if (xdrs->x_op is XDR_ENCODE)
      num = (*boolp) ? 1 : 0;

    if (!xdr_short(xdrs, &num))
      return(XDR_FAIL);

    if (xdrs->x_op is XDR_DECODE)
      if (num isnt 0 and num isnt 1)
	return(XDR_FAIL);
      else
	*boolp = (num is 1) ? nil_true : nil_false;

    return(XDR_OK);
}



xdr_status
hxdr_integer(xdrs, objp, shape)
XDR *xdrs;
objectp objp;
shapep shape;
{
    if (shape->tsdr is &dr_bottom and xdrs->x_op isnt XDR_FREE)
      shape->tsdr = objp->tsdr;

    return(xdr_int(xdrs, &(objp->value.integer)));
}


xdr_status
hxdr_real(xdrs, objp, shape)
XDR *xdrs;
objectp objp;
shapep shape;
{
    if (shape->tsdr is &dr_bottom and xdrs->x_op isnt XDR_FREE)
      shape->tsdr = objp->tsdr;

    if (xdrs->x_op is XDR_DECODE) {
      if ((objp->value.real = new(dfd_real)) is nil)
        return(XDR_FAIL);
    }
    else if (xdrs->x_op is XDR_FREE) {
      if (objp->value.real isnt nil)
        dispose(objp->value.real, dfd_real);
      objp->value.real = nil;
      return(XDR_OK);
    }

    return(xdr_double(xdrs, objp->value.real));
}


xdr_status
hxdr_nominal(xdrs, objp, shape)
XDR *xdrs;
objectp objp;
shapep shape;
{
    xdr_status xdr_nominal_struct();

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

    if (xdrs->x_op is XDR_DECODE)
      if ((objp->value.nominal = new(dfd_nominal)) is nil)
        return(XDR_FAIL);
      else
        objp->value.nominal->refcount = 1;

    if (!xdr_long(xdrs, & objp->value.nominal->time))
      return(XDR_FAIL);
    if (!xdr_u_int(xdrs, & objp->value.nominal->num))
      return(XDR_FAIL);
#ifdef DISTRIBUTED
    {
      int size = HOSTIDSIZE;
      char *hostid = objp->value.nominal->interp.hostname;
      if (!xdr_bytes(xdrs, &hostid, &size, HOSTIDSIZE))
        return(XDR_FAIL);
      if (!xdr_int(xdrs, &objp->value.nominal->interp.number))
	return(XDR_FAIL);
    }
#endif

    if (xdrs->x_op is XDR_FREE) {
      if (objp->value.nominal isnt nil)
        { dispose(objp->value.nominal, dfd_nominal); }
      objp->value.nominal = nil;
    }

    return(XDR_OK);
}

void
free_shape_scalar(shape)
shapep shape;
{
  /* shape->tsdr isnt &dr_bottom, but shape->value contains no
     information.  So there's still nothing to do here.
     Applies to all the above types. */
}


status
hxdr_record(xdrs, objp, shape)
XDR *xdrs;
objectp objp;
shapep shape;
{
    dfd_record *new_record();

    counter size, i;

    switch (xdrs->x_op) {
    case XDR_ENCODE:
      size = objp->value.record->info.record_size;
      break;
    case XDR_DECODE:
      objp->value.record = nil;
      break;
    case XDR_FREE:
      if (objp->value.record isnt nil) {
        size = objp->value.record->info.record_size;
        
        /* shape is unused in XDR_FREE, so give 'em what we got */
        for (i = 0; i < size; i++)
          if (not hxdr_object(xdrs, & objp->value.record->data[i], 
                              shape))
            return(XDR_FAIL);  /* what the hell do we do if freeing fails? */
        freedotmain(objp->value.record, size);
      }
      return(XDR_OK);
      break;
    }

    if (shape->tsdr is &dr_bottom) {
      if (!xdr_u_int(xdrs, &size))
        return(XDR_FAIL);
      if ((shape->value.record = 
           (shape_record *) getmain((counter) sizeof(shape_record)+
                                    sizeof(shapeobj)*size))
          is nil)
        return(XDR_FAIL);

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

    if (xdrs->x_op is XDR_DECODE) {
      if ((objp->value.record = new_record(size)) is nil)
        return(XDR_FAIL);
    }

    for (i = 0; i < size; i++)
      if (not hxdr_object(xdrs, & objp->value.record->data[i], 
                          & shape->value.record->data[i]))
        return(XDR_FAIL);

    return(XDR_OK);
}

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

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


xdr_status
hxdr_variant(xdrs, objp, shape)
XDR *xdrs;
objectp objp;
shapep shape;
{
    xdr_status xdr_enumval();
    shapep datashape;

    if (xdrs->x_op is XDR_DECODE) {
      if ((objp->value.variant = (dfd_variant *) getdotmain(VARIANT_SIZE)) is nil)
        return(XDR_FAIL);
    }
    else if (xdrs->x_op is XDR_FREE) {
      if (objp->value.variant isnt nil) {
        if (!hxdr_object(xdrs, &objp->value.variant->data[VARIANT_COMPONENT],
                         shape))
          return(XDR_FAIL);
        freedotmain(objp->value.variant, VARIANT_SIZE);
      }
      return(XDR_OK);
    }

    if (!xdr_enumval(xdrs, &objp->value.variant->info.variant_case))
      return(XDR_FAIL);

    if (shape->tsdr is &dr_bottom) {
      if ((shape->value.variant = new(shape_variant)) is nil)
        return(XDR_FAIL);
      shape->value.variant->contents_shape.tsdr = &dr_bottom;
      shape->value.variant->variant_case = objp->value.variant->info.variant_case;
      shape->value.variant->next = nil;
      shape->tsdr = objp->tsdr;

      datashape = & shape->value.variant->contents_shape;
    }
    else {
      /* do stupid linear search for this case */
      int found = 0;
      shape_variant *here;

      here = shape->value.variant;
      if (here->variant_case isnt objp->value.variant->info.variant_case) {
        while (here->next isnt nil) {
          here = here->next;
          if (here->variant_case is objp->value.variant->info.variant_case) {
            found = 1;
            break;
          }
        }
        if (found is 0) {
          if ((here->next = new(shape_variant)) is nil)
            return(XDR_FAIL);
          here = here->next;
          here->contents_shape.tsdr = &dr_bottom;
          here->variant_case = objp->value.variant->info.variant_case;
          here->next = nil;
        }
      }
      datashape = & here->contents_shape;
    }

    return(hxdr_object(xdrs, &objp->value.variant->data[VARIANT_COMPONENT],
                     datashape));
}


void
free_shape_variant(shape)
shapep shape;
{
  shape_variant *next;

  while (shape->value.variant isnt nil) {
    free_shape(& shape->value.variant->contents_shape);
    next = shape->value.variant->next;
    dispose(shape->value.variant, shape_variant);
    shape->value.variant = next;
  }
}


status
hxdr_program(xdrs, objp, shape)
XDR *xdrs;
objectp objp;
shapep shape;
{
    int i;
    int shareidx;
    void preload_ccode();
    pd_program *sharedprog;
    pd_program *get_share_prog();
    int add_share_prog(), find_share_prog();
    status refprog();

    if (xdrs->x_op is XDR_DECODE) {
      if (! xdr_int(xdrs, &shareidx))
	return(XDR_FAIL);
      if ((sharedprog = (pd_program *) get_share_prog(shareidx)) == nil) {
	if ((objp->value.program = (pd_program *) getdotmain(PROGRAM_SIZE))
	    is nil)
	  return(XDR_FAIL);
	objp->value.program->info.program_refcount = 1;
	if (add_share_prog(objp->value.program) == -1)
	  return(XDR_FAIL);
      }
      else {
	/* this is a shared instance of something we've already seen */
	objp->value.program = sharedprog;
	return(refprog(objp) ? XDR_OK : XDR_FAIL);
	/* nothing else to do in this case */
      }
    }
    else if (xdrs->x_op is XDR_ENCODE) {
      /* see if this is a shared instance of something already encoded */
      if ((shareidx = find_share_prog(objp->value.program)) == -1) {
	/* nope, add it to the share list and continue */
	shareidx = add_share_prog(objp->value.program);
	if (! xdr_int(xdrs, &shareidx))
	    return(XDR_FAIL);
      }
      else			/* yes - just encode the share index */
	return(xdr_int(xdrs, &shareidx));
    }
    else if (xdrs->x_op is XDR_FREE) {
      if (objp->value.program isnt nil)
	if (--(objp->value.program->info.program_refcount) == 0) {
	  for (i = 0; i < PROGRAM_SIZE; i++)
	    if (!hxdr_object(xdrs, &objp->value.program->data[i],
			     shape))
	      return(XDR_FAIL);
	  freedotmain(objp->value.program, PROGRAM_SIZE);
	}
      return(XDR_OK);
    }

    /* we're either encoding or decoding the first occurrence of this */
    /* program... prepeare the shape, then do each program component */
    if (shape->tsdr is &dr_bottom) {
      if ((shape->value.program = 
           (shape_program *) getmain((counter) sizeof(shape_program)+
                                    sizeof(shapeobj)*PROGRAM_SIZE))
          is nil)
        return(XDR_FAIL);

      for(i=0; i<PROGRAM_SIZE; i++)
        shape->value.program->data[i].tsdr = &dr_bottom;
      shape->tsdr = objp->tsdr;
    }
    
    for (i = 0; i < PROGRAM_SIZE; i++)
      if (!hxdr_object(xdrs, &objp->value.program->data[i],
                       & shape->value.program->data[i]))
        return(XDR_FAIL);
    
    /* preload the ccoded object files for this program if needed */
    if (xdrs->x_op is XDR_DECODE) {
      if (objp->value.program->data[program__CODE_MAP].tsdr->number 
	  = dr_table.number)
	preload_ccode(&objp->value.program->data[program__CODE_MAP]);
    }

    return(XDR_OK);
}


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

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

/* if the path or object field of the LI program is non-empty, attempt */
/* to pre-load the corresponding object module into the running system */
/* Also visit all embedded program literals and preload them, and */
/* recurse as needed */
void
preload_ccode(codemap)
objectp codemap;		/* interpform!codemap */
{
  valcell path;
  valcell objectcode;
  void *link_process();
  flag save_cherm_flag;
  extern flag cherm_flag;
  predef_exception save_cherm_excep;
  extern predef_exception cherm_excep;
  struct {valcell val;} qh;
# define x (&x_obj)
  object x_obj;
  predef_exception h_call(), h_qcall();
  void o_initget(), o_get_or_err();
  
  /* make sure the world knows this isn't a Hermes process calling */
  save_cherm_flag = cherm_flag;
  save_cherm_excep = cherm_excep;
  cherm_flag = TRUE;

  /* inspect x in codemap[] begin */
  /*   call preload_ccode(x.liprog); */
  /* end inspect; */
  qh.val.record = nil;
  h_qcall(o_initget, qh, x, codemap, 0);
  while (h_call(o_get_or_err, x, codemap, 0) is Normal) {
    path = dot(dot(x,codemapentry__liprog), prog__path)->value;
    objectcode = dot(dot(x,codemapentry__liprog), prog__object)->value;
    if (size_of(path) != 0 || size_of(objectcode) != 0)
      (void) link_process(dot(x,codemapentry__liprog)->value);
  }

  cherm_flag = save_cherm_flag;
  cherm_excep = save_cherm_excep;
# undef x
}
  
xdr_status
hxdr_polymorph(xdrs, objp, shape)
XDR *xdrs;
objectp objp;
shapep shape;
{
  if (xdrs->x_op is XDR_FREE) {
    if (!hxdr_object(xdrs, & objp->value.polymorph->typename, shape))
      return(XDR_FAIL);  /* shouldn't happen on free */
    if (!hxdr_object(xdrs, & objp->value.polymorph->typestate, shape))
      return(XDR_FAIL);
    if (!hxdr_object(xdrs, & objp->value.polymorph->obj, shape))
      return(XDR_FAIL);

    dispose(objp->value.polymorph, dfd_polymorph);
    return(XDR_OK);
  } 
  else if (xdrs->x_op is XDR_DECODE) {
	objp->value.polymorph = new(dfd_polymorph);
	if (objp->value.polymorph is nil)
	  return(XDR_FAIL);
	set_bottom(& objp->value.polymorph->typestate);
	set_bottom(& objp->value.polymorph->typename);
	set_bottom(& objp->value.polymorph->obj);
  }

  /* we know that the typename and typestate fields will have the same
     shape every time, but we know nothing whatsoever about obj,
     so it should always be bottom */
  if (shape->tsdr is &dr_bottom) {
	if ((shape->value.polymorph = new(shape_polymorph)) is nil)
	  return(XDR_FAIL);
	shape->value.polymorph->typename.tsdr = &dr_bottom;
	shape->value.polymorph->typestate.tsdr = &dr_bottom;
	shape->value.polymorph->obj.tsdr = &dr_bottom;
    shape->tsdr = objp->tsdr;
  }
  else {
    /* Want to politely free up shape->value.polymorph->obj, 
       and set it to bottom */ 
    free_shape(& shape->value.polymorph->obj);
    shape->value.polymorph->obj.tsdr = &dr_bottom;
  }

  if (!hxdr_object(xdrs, & objp->value.polymorph->typename,
                   & shape->value.polymorph->typename))
    goto cleanup;
  if (!hxdr_object(xdrs, & objp->value.polymorph->typestate,
                   & shape->value.polymorph->typestate))
    goto cleanup;
  if (hxdr_object(xdrs, & objp->value.polymorph->obj,
                  & shape->value.polymorph->obj))
    return(XDR_OK);

  cleanup:
  if (xdrs->x_op is XDR_DECODE) {
    fin_polymorph(objp->value.polymorph, F_DISCARD, nil);
    /* let shape get freed by the free_shape from the top */
  }
  return(XDR_FAIL);
}

void
free_shape_polymorph(shape)
shapep shape;
{
  if (shape->value.polymorph isnt nil) {
    free_shape(& shape->value.polymorph->typename);
    free_shape(& shape->value.polymorph->typestate);
    free_shape(& shape->value.polymorph->obj);
    dispose(shape->value.polymorph, shape_polymorph);
  }
}
