/* (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[] = "@(#)o_nomin.c	1.10 3/13/90";
#endif

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

#include <string.h>

#include "ops.h"
#include "storage.h"
#include "sysdep.h"

#define Dst (DstObj->value.nominal)
#define Src (SrcObj->value.nominal)
#define Src1 (Src1Obj->value.nominal)
#define Src2 (Src2Obj->value.nominal)

#define init_nominal(objectp, val) (set_init((objectp), dr_nominal), (objectp)->value.nominal = (val))

extern datarep dr_nominal;

#define HOSTBUFSIZE 128

/*
 * nominal values.
 *
 * a nominal is a globally unique value.  we produce these as follows:  for
 * a single machine implementation, we use the time() function, which returns
 * the number of seconds since 1970 in GMT.  but since more than one nominal 
 * per second might be generated, a nominal has a second field which is the
 * value of the counter uniquenum, which is incremented each time we build a
 * nominal.  so as long as we can't generate more than 4 billion nominals per
 * second (assuming a 32-bit architecture), this pair will be unique
 */

static counter uniquenum = 0;


/* in a distributed environment, a uniqueid has to include the originating
 *   host.
 */

static char hostid[HOSTIDSIZE];

void
nominit()
{
    void distributed_init();


    uniquenum = 0;

#ifdef DISTRIBUTED
    distributed_init();
#endif

}


void
distributed_init()
{
    void abort_nili();

    struct hostent *h;
    char hname[HOSTBUFSIZE];
    int i;


    if (gethostname(hname, HOSTBUFSIZE) isnt 0) {
	nilperror("distributed_init", "Gethostname() failed");
	abort_nili("Can't get hostname.");
    }

    h = gethostbyname(hname);

    if (h is nil) {
	nilerror("distributed_init", "Gethostbyname() failed.");
	abort_nili("Can't get host information.");
    }

    for (i=0; i < HOSTIDSIZE; i++) 
      if (i < h->h_length)
	hostid[i] = h->h_addr[i];
      else
	hostid[i] = 0;
}


NILOP(o_unique)
{
    time_t time();

    dfd_nominal *newnominal;


				/* allocate storage for a new nominal */
    if ((newnominal = new(dfd_nominal)) is nil)
      raise(Depletion);
    else {
	newnominal->refcount = 1; /* set reference count to 1 */
	newnominal->time = time((time_t *) nil);
	newnominal->num = uniquenum;

#ifdef undefined (assume incrementing wraps all by itself)
	if (uniquenum is MAXCOUNTER)
	  uniquenum = 0;		/* wrap around if at max... */
	else
#endif
	  uniquenum++;		/* otherwise just bump counter. */

#ifdef DISTRIBUTED
	(void) strncpy(newnominal->hostid, hostid, HOSTIDSIZE);
#endif

	init_nominal(DstObj, newnominal);
    }
}


/*ARGSUSED*/
void
fin_nominal(value, f_op, sched)
valcell value;
finalize_op f_op;
schedblock *sched;
{
    if (--value.nominal->refcount <= 0)
				/* decrement the reference count.  if 0... */
      { dispose(value.nominal, dfd_nominal); }
				/* ...free the nominal cell. */
}



predef_exception
cp_nominal(dst, src)
valcell *dst, src;
{
    valcell newnominal;

    if (src.nominal->refcount is MAXCOUNTER) {
	/* allocate a complete new nominal if we can't bump ref count */
	if ((newnominal.nominal = new(dfd_nominal)) is nil)
	  return(Depletion);
	else {
	    *newnominal.nominal = *src.nominal;
	    newnominal.nominal->refcount = 1;
	    dst->nominal = newnominal.nominal;
	    return(Normal);
	}
    }
    else {
	/* share storage with source nominal */
	src.nominal->refcount++;
	dst->nominal = src.nominal;
	return(Normal);
    }
}

status 
eq_nominal(val1, val2)
valcell val1, val2;
{
#ifdef DISTRIBUTED
    status compare_interpnames();
#endif

    if (val1.nominal is val2.nominal)
      return(SUCCESS);		/* quick check for sharing */
    if (val1.nominal->time is val2.nominal->time and
	val1.nominal->num  is val2.nominal->num
#ifdef DISTRIBUTED
	and compare_interpnames(& val1.nominal->interp, & val2.nominal->interp)
	is SUCCESS
#endif
	)
      return(SUCCESS);
    else
      return(FAILURE);
}
