/*
 * Copyright (C) 1992, Board of Trustees of the University of Illinois.
 *
 * Permission is granted to copy and distribute source with out fee.
 * Commercialization of this product requires prior licensing
 * from the National Center for Supercomputing Applications of the
 * University of Illinois.  Commercialization includes the integration of this 
 * code in part or whole into a product for resale.  Free distribution of 
 * unmodified source and use of NCSA software is not considered 
 * commercialization.
 *
 */
#if ! defined(lint) && ! defined(LINT)
static char rcs_id[] = "$Id: net.c,v 1.1.1.1 1995/01/11 00:03:38 alanb Exp $";
#endif

/* $Log: net.c,v $
 * Revision 1.1.1.1  1995/01/11  00:03:38  alanb
 * New CVS source tree, Mosaic 2.5 beta 4
 *
 * Revision 2.5  1994/12/29  23:42:05  alanb
 * I'm committing with a new symbolic revision number.
 *
 * Revision 1.1.1.1  1994/12/28  21:37:42  alanb
 *
 * Revision 1.5  1994/03/28  17:20:49  gbourhis
 * Update to be more in synch with the collage distribution.
 *
 * Revision 1.4  1993/10/31  06:30:23  marca
 * Tweaks.
 *
 * Revision 1.3  1993/10/25  06:23:25  marca
 * Tweaks.
 *
 * Revision 1.2  1993/07/19  06:35:51  marca
 * Removed stupid if 0.
 *
 * Revision 1.1.1.1  1993/07/04  00:03:20  marca
 * Mosaic for X version 2 distribution
 *
 * Revision 1.8  1993/06/03  18:47:35  davet
 * Version name change
 *
 * Revision 1.7  1993/05/17  16:47:10  gbourhis
 * remove the warning for 3D data.
 *
 * Revision 1.6  1993/05/05  19:22:12  gbourhis
 * Add rcs Id and Log. handling of the new associated field, new param for
 * NetPALDistribute & NetSendPalette8.
 * Changes in NetArrayDistribute() & NetRISDistribute().
 * call failCB() parameter in NetClientSendMessage() if not connected.
 * give NetFreeDataCB as the failCB parameter of NetClientSendMessage in
 * most case.
 *
 *
 *  mods: 
 * 2/22/93 (ddt)  print out message if user has loaded unsupported 
 *		 dimensional array
*/

#include <stdio.h>
#include <sys/time.h>

#include <libdtm/dtm.h>
#include <libdtm/sds.h>
#include <libdtm/ris.h>
#include <libdtm/text.h>
#include <libdtm/srv.h>
#include <libdtm/col.h>
#include <libdtm/anim.h>
#include <libdtm/vdata.h>
#include <libdtm/sdl.h>
#include <libdtm/com.h>
#include <libdtm/exec.h>

#define FREE	free
#include "list.h"
#include "netdata.h"
#include "netP.h"
#include "doodle.h"
#include "collage.h"

/* Solaris. */
#if defined(sun) && defined(__svr4__)
#define bcopy(a, b, c) memmove(b, a, c)
#define bzero(a, b)    memset(a, 0, b)
#endif

#define TRUE 1
#define FALSE 0

/* USE_AVAIL_WRITE will allow for a send queue.  only send when reader ready*/
#define USE_AVAIL_WRITE
/* USE_WRITEMSG if set should reduce number packets sent on write */
#define USE_WRITEMSG
/* USE_FEEDBACK_CALLS if set will call UI routines when DTM traffic is present*/
/* #define USE_FEEDBACK_CALLS */

#define VERSION_STRING	"1.2"
#define VERSION_NUMBER  2

#ifndef DTM_STRING_SIZE
#define DTM_STRING_SIZE 1024
#endif

#define MAX_SDL_VERTICES	10000


/* docb(dataObject,client_data); */
typedef struct {	/* Data Object Call Back */
	char *moduleName;
	void (*newCB)();
	caddr_t newData;
	void (*changeCB)();
	caddr_t changeData;
	void (*destroyCB)();
	caddr_t destroyData;
	} DOCB;

typedef struct {	 /* Send Queue */
	NetPort *netPort;
	char	*header;
	char	*data;
	long	num;
	DTMTYPE	type;
	int	numTries;	/* number of attempted sends */
	void	(*cb)();	/* called on succes cb(data,cbData) */
	caddr_t	cbData;		/* call back data */
	void	(*failCB)();	/* called on failure failCB(data,failCBData) */
	caddr_t	failCBData;	/* fail call back data */
	} SQueue;

typedef struct {
	caddr_t internal;
	void (*cb)();
	caddr_t cbData;
	void (*failCB)();
	caddr_t failCBData;
	} ExecCBData;


static List netInList;
static List netOutList;

static List sendQueue;

static List ANIMList;
static List RIS8List;
static List SDSList;
static List PALList;
static List TXTList;
static List SRVList;
static List DTMList;
static List COLList;
static List VDATAList;
static List SDLList;
static List COMList;
static List EXECList;
static List MSGList;
static List userList;
static int NetSendConnect();
static int NetFlushPort();
static int dtmFlowControl = (int)DTM_SYNC;

static int netTimeOut;
static int netMaxAttemptsToSend;

#ifdef USE_FEEDBACK_CALLS
extern void SetReadFeedback();
extern void SetWriteFeedback();
extern void UnsetFeedback();
#endif

static char *userID = NULL;

void NetSetASync(set)
/* set DTM to behave in either async or syncronous mode */
int	set; /* Boolean */
{
	if (set)  {
		dtmFlowControl = (int)DTM_ASYNC;
#ifdef DEBUG
		printf("NetSetASync(): setting to DTM_ASYNC\n");
#endif
		}
	else {
		dtmFlowControl = (int)DTM_SYNC;
#ifdef DEBUG
		printf("NetSetASync(): setting to SYNC\n");
#endif
		}
}

void NetSetTimeOut(seconds)
int seconds;
{
	netTimeOut = seconds;
}

int NetGetTimeOut()
{
	return(netTimeOut);
}

void NetSetMaxAttemptsToSend(numberTries)
int numberTries;
{
	netMaxAttemptsToSend = numberTries;
}

int NetGetMaxAttemptsToSend()
{
	return(netMaxAttemptsToSend);
}

char *NetDTMErrorString(error)
{
    switch (error) {
	case DTMNOERR:   return(" no error ");
	case DTMMEM:     return(" (1) Out of memory ");
	case DTMHUH:     return(" (2) Unknown port definition ");
	case DTMNOPORT:  return(" (3) No DTM ports available ");
	case DTMPORTINIT:return(" (4) DTM port not initialized ");
	case DTMCALL:    return(" (5) calling routines in wrong order ");
	case DTMEOF:     return(" (6) EOF error ");
	case DTMSOCK:    return(" (7) Socket error ");
	case DTMHOST:    return(" (8) That hostname is not found/bad ");
	case DTMTIMEOUT: return(" (9) Timeout waiting for connection ");
	case DTMCCONN:   return(" (10) DTM cannot connect (network down?) ");
	case DTMREAD:    return(" (11) error returned from system read ");
	case DTMWRITE:   return(" (12) error returned from system write(v) ");
	case DTMHEADER:  return(" (13) Header to long for buffer ");
	case DTMSDS:     return(" (14) SDS error ");
	case DTMSELECT:  return(" (15) Select call error ");
	case DTMENV:     return(" (16) Environment not setup ");
	case DTMBUFOVR:  return(" (17) User buffer overflow ");
	case DTMCORPT:   return(" (18) Port table corrupted ");
	case DTMBADPORT: return(" (19) Port identifier is bad/corrupt/stale ");
	case DTMBADACK:  return(" (20) Bad ack to internal flow control ");
	case DTMADDR:    return(" (21) Bad address ");
	case DTMSERVER:  return(" (22) Problem communicating with the server ");
	default:	 return(" Unknown error ");
	};
}

NetPort *NetIsConnected()
/* is collage connected? if so, return port */
{
	return((NetPort *)ListHead(netOutList));
}

static void NetRemovePortFromSendQueue(netPort)
NetPort *netPort;
{
SQueue *sq;

	sq = (SQueue *) ListHead(sendQueue);
	while(sq) {
		if (sq->netPort == netPort) {
			ListDeleteEntry(sendQueue,sq);
			if (sq->failCB) 
				sq->failCB(sq->data,sq->failCBData);
			FREE(sq->header);
			FREE(sq);
			sq = (SQueue *) ListCurrent(sendQueue);
			}
		else {
			sq = (SQueue *) ListNext(sendQueue);
			}
		}
}

static int NetUserListAdd(name)
/* return 1 if new user else 0; -1 on error*/
char *name;
{
char *p;
	
	if ((!name) || (!strlen(name)))
		return(-1);
	p = (char *) ListHead(userList);
	while(p) {
		if (!strcmp(p,name)) {
			return(0);
			}
		p = (char *) ListNext(userList);
		}

	if (!(p = (char *) MALLOC(strlen(name)+1))) {
		ErrMesg("Out of memory adding new user\n");
		return(-1);
		}
	strcpy(p,name);
	ListAddEntry(userList,p);
	return(1);
}

static int
DEFUN(NetUserListRemove,(name),char *name)
{
	int rc;
	return name ?
		(rc = ListDeleteEntry(userList, name), FREE(name), rc) : -1;
}

int NetGetListOfUsers(max,users)
int max; 	/* size of users array */
char **users;	/* List of users put in here */
/* returns the number of users */
{
int count;
char *p;

	p = (char *) ListHead(userList);
	count= 0;
	while(p && (count < max)) {
		users[count++] = p;
		p = (char *) ListNext(userList);
		}
	return(count);
}

void DEFUN(NetSetUserID,(user), char *user)
{
	if (user && userID && strcmp(userID, user) ||
	    !user && userID)
		NetUserListRemove(userID);

	if (user && userID && strcmp(userID, user) ||
	    !userID && user) {
		if (!(userID = (char *) MALLOC(strlen(user)+1))) {
			ErrMesg("Out of memory setting user id\n");
			return;
		}
		strcpy(userID,user);
		ListAddEntry(userList,userID);
	}
	else if (!user && userID)
		userID = NULL;
}
		

static DOCB *NetSearchByName(name,list)
char *name;
List list;
{
DOCB *docb;

	docb = (DOCB *) ListHead(list);
	while (docb) {
		if (!strcmp(docb->moduleName,name)) {
			return(docb);
			}
		docb = (DOCB *) ListNext(list);
		}
	return((DOCB *) 0);
	
}


int NetRegisterModule(name,netType,new,newData,change,changeData,
		 			destroy,destroyData)
char *name;		/* module Name */
NetType netType;	/* DTM class */
void (*new)();		/* New data Object callback */
caddr_t newData;
void (*change)();	/* Data object has changed callback */
caddr_t changeData;
void (*destroy)();	/* Data object destroyed callback */
caddr_t destroyData;
{
	DOCB *docb; 
	char	itsNew;

	/*Yeah this is huge,repetitive and could easily be condensed, but it
	  wasn't when I started, and I don't feel like changing it now 
	  condensed on Feb 93 by gbourhis */
#define REGISTERMODULE(list)						\
 do {									\
	if (!(docb = NetSearchByName(name,list)) ) {			\
		if (!(docb =(DOCB *) MALLOC(sizeof(DOCB)))){		\
			ErrMesg("Out of Memory\n");			\
			return(0);					\
			}						\
		if (!(docb->moduleName = (char *)			\
					MALLOC(strlen(name)+1))) {	\
			ErrMesg("Out of Memory\n");			\
			return(0);					\
			}						\
		strcpy(docb->moduleName,name);				\
		itsNew = TRUE;						\
		}							\
	else								\
		itsNew = FALSE;						\
	docb->newCB = new;						\
	docb->changeCB = change;					\
	docb->destroyCB = destroy;					\
	docb->newData = newData;					\
	docb->changeData = changeData;					\
	docb->destroyData = destroyData;				\
	if (itsNew)							\
		ListAddEntry(list,(char *)docb);			\
    } while (0)

	switch (netType) {
		case NETRIS8:
			REGISTERMODULE(RIS8List);
			break;
		case NETSDS:
			REGISTERMODULE(SDSList);
			break;
		case NETANIM:
			REGISTERMODULE(ANIMList);
			break;
		case NETPAL:
			REGISTERMODULE(PALList);
			break;
		case NETTXT:
			REGISTERMODULE(TXTList);
			break;
		case NETCOL:
			REGISTERMODULE(COLList);
			break;
		case NETSRV:
			REGISTERMODULE(SRVList);
			break;
		case NETDTM:
			REGISTERMODULE(DTMList);
			break;
		case NETVDATA:
			REGISTERMODULE(VDATAList);
			break;
		case NETSDL:
			REGISTERMODULE(SDLList);
			break;
		case NETCOM:
			REGISTERMODULE(COMList);
			break;
		case NETEXEC:
			REGISTERMODULE(EXECList);
			break;
		case NETMSG:
			REGISTERMODULE(MSGList);
			break;
		default:
#ifdef DEBUG
			fprintf(stderr,"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n");
			fprintf(stderr,"Internal Error: NetRegisterModule():");
			fprintf(stderr,"Unknown type\n");
#endif
			return(0);
		};


	
	return(1);
}



static int DimensionsEqual(dim1,rank1,dim2,rank2) 
int *dim1,rank1,*dim2,rank2;
{
register int x;
	if (rank1 != rank2)
		return(0);
	for (x=0; x < rank1; x++) {
		if (dim1[x] != dim2[x])
			return(0);
		}
	return(1);
}


static void CopyDimensions(dim1,rank1,dim2,rank2) 
/* copies dim2 to dim1 */
int *dim1,*rank1;
int *dim2,rank2;
{
register int x;

	for (x = 0; x < rank2; x++) {
		dim1[x] = dim2[x];
		}
	*rank1 = rank2;
}



static NetPort *NetPortNew()
{
NetPort *n;

	if (!(n = (NetPort *) MALLOC(sizeof(NetPort)))) {
		return(0);
		}
	n->port = 0;
	n->portName[0] = (char) 0;
	n->open = FALSE;
	n->type = NET_UNDEF;
	n->queueTime = 0;
	return(n);
}

void NetDestroyPort(netPort)
NetPort *netPort;
{
NetPort *np;
SQueue *sq;

	switch (netPort->type) {
		case NET_IN:
			np = (NetPort *) ListHead(netInList);
			while (np) {
				if (np == netPort) {
					ListDeleteEntry(netInList,netPort);
					np = (NetPort *) ListCurrent(netInList);
					}
				else {
					np = (NetPort *) ListNext(netInList);
					}
				}
			break;
		case NET_OUT:
			np = (NetPort *) ListHead(netOutList);
			while (np) {
				if (np == netPort) {
					ListDeleteEntry(netOutList,netPort);
					np = (NetPort *)ListCurrent(netOutList);
					}
				else {
					np = (NetPort *) ListNext(netOutList);
					}
				}
			NetRemovePortFromSendQueue(netPort);
			break;
		};
	if (netPort->open)
	  DTMdestroyPort(netPort->port);
	FREE(netPort);
}

NetPort *NetCreateInPort(inPortAddr)
char *inPortAddr;
{
int in;
NetPort *n;

#ifdef DEBUG
	printf("NetCreateInPort(\"%s\"): I've been called\n",inPortAddr);
#endif
	if ((!inPortAddr) || (!strlen(inPortAddr))){
		inPortAddr = ":0";
		}
#ifdef DEBUG
	if (dtmFlowControl == DTM_ASYNC)
		fprintf(stderr, "DTMmakeInPort(DTM_ASYNC)\n");
	else
		fprintf(stderr, "DTMmakeInPort(DTM_SYNC)\n");
#endif
        if ( DTMERROR == (in = DTMmakeInPort(inPortAddr, dtmFlowControl))) {
                ErrMesg(stderr,"Can't make DTM in port %s: %s\n",
                                        inPortAddr,DTMerrmsg(1));
                return(0);
                }

	if (!(n = NetPortNew())) {
		ErrMesg("Out of Memory");
		return(0);
		}

	n->port = in;
	n->open = TRUE;
	DTMgetPortAddr(n->port,n->portName,PORTNAMESIZE);
	n->type = NET_IN;

#ifdef DEBUG
	if (dtmFlowControl == DTM_ASYNC) {
		printf("Just made an ASYNC in port %s (%d)\n",
				n->portName,n->port);
		}
        else if (dtmFlowControl == DTM_SYNC) {
		printf("Just made an SYNC in port %s (%d)\n",
				n->portName,n->port);
		}
	else {
		printf("********Just made an *UNKOWN* in port %s (%d)\n",
				n->portName,n->port);
		printf("yow.... check me out\n");
		}
#endif

	ListAddEntry(netInList,(char *)n);
	return(n);
}



static NetPort *NetInternalCreateOutPort(outPortAddr,sendConnect)
char *outPortAddr;
int sendConnect;	/* Send connect message */
			/* connect protocol requires this be done */
{
int out;
NetPort *n;
char **portNames;
int numPortNames;


#ifdef DEBUG
	printf("NetCreateOutPort(\"%s\"): I've been called\n",outPortAddr);
#endif
	if ((!outPortAddr) || (!strlen(outPortAddr)))
		return(0);
#ifdef DEBUG
	if (dtmFlowControl == DTM_ASYNC)
		fprintf(stderr, "DTMmakeOutPort(DTM_ASYNC)\n");
	else
		fprintf(stderr, "DTMmakeOutPort(DTM_SYNC)\n");
#endif
        if ( DTMERROR == (out = DTMmakeOutPort(outPortAddr, dtmFlowControl))) {
                ErrMesg(stderr,"Can't make DTM out port %s: %s\n",
                                        outPortAddr,DTMerrmsg(1));
                return(0);
                }

	if (!(n = NetPortNew())) {
		ErrMesg("Out of Memory");
		return(0);
		}

	n->port = out;
	n->open = TRUE;
	n->type = NET_OUT;

        DTMgetRemotePortAddr(n->port,&portNames,&numPortNames);
        if (numPortNames) {
                strncpy(n->portName,portNames[0],PORTNAMESIZE);
                }
        else {
                /* use address passed in */
                strncpy(n->portName,outPortAddr,PORTNAMESIZE);
                }

#ifdef DEBUG
        if (dtmFlowControl == DTM_ASYNC) {
                printf("Just made an ASYNC out netPort=%x port %x (%s)\n",
                                n,n->port,n->portName);
                }
        else if (dtmFlowControl == DTM_SYNC) {
                printf("Just made an SYNC out netPort=%x port %x (%s)\n",
                                n,n->port,n->portName);
                }       
        else {
                printf("********Just made an *UNKOWN* out port %s (%d)\n",
                                n->portName,n->port);
                printf("yow.... check me out\n");
                }
#endif

	ListAddEntry(netOutList,(char *)n);

	if (sendConnect) {
		NetSendConnect((NetPort *) ListHead(netInList),n,0);
		}
	return(n);
}


NetPort *NetCreateOutPort(outPortAddr)
char *outPortAddr;
{
	return(NetInternalCreateOutPort(outPortAddr,TRUE));
}

static void NetChangeOutPort(address,oldOut) 
char *address;
NetPort *oldOut;
{
NetPort *n;

#ifdef DEBUG
	printf("Changing OutPort Address from netPort=%x port=%x (%s) to %s\n",
			oldOut,oldOut->port,oldOut->portName,address);
#endif
	DTMdestroyPort(oldOut->port);
	n = NetInternalCreateOutPort(address,FALSE);
	oldOut->port = n->port;
	strcpy(oldOut->portName,n->portName);
	ListDeleteEntry(netOutList,n);
	NetSendConnect((NetPort *) ListHead(netInList),oldOut,0);
#ifdef DEBUG
	printf("NetChangeOut: now netPort = %x port %x (%s) \n",
			oldOut, oldOut->port, oldOut->portName);
#endif
#ifdef DEBUG
	{
	NetPort *bla;
	bla = (NetPort *) ListHead(netOutList);
	while (bla) {
		printf("In netOutList: now netPort = %x port %x (%s) \n",
			bla, bla->port, bla->portName);
		bla = (NetPort *) ListNext(netOutList);
		}
	}
#endif
	FREE(n);
}


int DEFUN(NetInit,(user), char *user)
{
	InitData();
	netInList = ListCreate();
	netOutList = ListCreate();

	sendQueue = ListCreate();

	ANIMList = ListCreate();
	RIS8List = ListCreate();
	SDSList = ListCreate();
	PALList = ListCreate();

	TXTList = ListCreate();
	SRVList = ListCreate();
	DTMList = ListCreate();
	COLList = ListCreate();
	VDATAList = ListCreate();
	SDLList = ListCreate();
	COMList = ListCreate();
	EXECList = ListCreate();
	MSGList = ListCreate();

	userList = ListCreate();
	NetSetUserID(user);

	NetSetTimeOut(30);
	NetSetMaxAttemptsToSend(200);

	return(1);
}


static void NetReject(in,header)
int in;
char *header;
{
char buff[DTM_MAX_HEADER+50];

#ifdef DEBUG
	sprintf(buff,"Rejecting:  %s\n",header);
	WriteMesg(buff);
#endif
	DTMendRead(in);
}

static Data *NetReadSDL(n,header)
NetPort *n;
char *header;
{
#if 0
char    title[DTM_STRING_SIZE];
char    id[DTM_STRING_SIZE];
struct	DTM_TRIPLET	primbuff[MAX_SDL_VERTICES];
Data *d;
					/* SDL header doesn't say how big */
					/* it is so have to alloc max */
int	num;

	SDLgetTitle(header,title,DTM_STRING_SIZE);
	if (!(d = DataNew()))
		return(0);
	if (!(d->label = (char *) MALLOC(strlen(title)+1))) {
		ErrMesg("Out of Memory reading in vdata label \n");
        	DTMendRead(n->port);
		return(0);
		}
	strcpy(d->label,title);
	d->dot = DOT_SDL;
	SDLgetPrimitive(header,&(d->dost));
	
	if ((num = DTMreadDataset(n->port, primbuff, 
		MAX_SDL_VERTICES, DTM_TRIPLET)) == DTMERROR){
			ErrMesg("Error reading DTM SDL");
			NetReject(n->port,header);
			return(0);
			}
       	DTMendRead(n->port);

	if (!(d->data = (char *) MALLOC(num))) {
		ErrMesg("Out of memory reading DTM SDL");
		return(0);
		}
	memcpy(d->data,(char *)primbuff,num);
	d->rank = 1;
	d->dim[0] = num / sizeof(DTM_TRIPLET);
	d->entity = ENT_Network;

	return(d);
#endif	
}

static Com *NetReadCOM(n,header)
NetPort *n;
char *header;
{
Data *d;
static char	id[DTM_STRING_SIZE];
static char	domain[DTM_STRING_SIZE];
static char	mesg[DTM_STRING_SIZE];
static Com	c;


	DTMendRead(n->port);

	COMgetID(header,id,DTM_STRING_SIZE);
	COMgetDomain(header,domain,DTM_STRING_SIZE);
	COMgetMesg(header,mesg,DTM_STRING_SIZE);
	c.id = id;
	c.domain = domain;
	c.mesg = mesg;

	return(&c);
}


static Data *NetReadVDATA(n,header)
NetPort *n;
char *header;
{
#if 0
Data *d;
char	title[DTM_STRING_SIZE];
char	id[DTM_STRING_SIZE];
char	tmp[DTM_STRING_SIZE];
DTMTYPE type;
int	elementSize;
int	x;
int	size;

        VDATAgetTitle(header,title,DTM_STRING_SIZE);
	if ((!title) || (!strlen(title)))
		 strcpy(title,"Untitled");

	if (!(d = DataNew()))
		return(0);
	if (!(d->label = (char *) MALLOC(strlen(title)+1))) {
		ErrMesg("Out of Memory reading in vdata label \n");
        	DTMendRead(n->port);
		return(0);
		}
	strcpy(d->label,title);
	d->entity = ENT_Network;
	d->dot = DOT_VData;

	if (VDATAgetType(header,&type) == -1 ) {
		ErrMesg("Error getting VDATA type\n");
		NetReject(n->port,header);
		return(0);
		}
	switch(type) {
		case DTM_CHAR:
			d->dost = DOST_Char;
			elementSize = sizeof(char);
			break;
		case DTM_FLOAT:
			d->dost = DOST_Float;
			elementSize = sizeof(float);
			break;
		case DTM_INT:
			d->dost = DOST_Int32;
			elementSize = 4;
			break;
		case DTM_SHORT:
			d->dost = DOST_Int16;
			elementSize = 2;
			break;
		case DTM_DOUBLE:
			d->dost = DOST_Double;
			elementSize = sizeof(double);
			break;
		default: 
			d->dost = DOST_Char;
			elementSize = 1;
			printf(
			"VDATA of unknown type just received casting to char\n");
		};
	VDATAgetNumElements(header,&(d->dim[0]));
	VDATAgetNumRecords(header,&(d->dim[1]));
	d->rank = 2;
	VDATAgetPathLength(header,&(d->pathLength));
	if (!(d->magicPath = (VdataPathElement **) 
				MALLOC(sizeof(VdataPathElement *) 
					* d->pathLength))){
		ErrMesg("Out of Memory reading VDATA path\n");
        	DTMendRead(n->port);
		return(0);
		}
	for (x = 0; x < d->pathLength; x++) {
		if (!(d->magicPath[x] = (VdataPathElement *)
				MALLOC(sizeof(VdataPathElement)))) {
			ErrMesg("Out of Memory reading VDATA path 2\n");
        		DTMendRead(n->port);
			return(0);
			}
		}
	VDATAgetPath(header,d->magicPath,&(d->pathLength));
	VDATAgetNodeID(header,&(d->nodeID));
	VDATAgetNodeName(header,tmp,DTM_STRING_SIZE);
	if (!(d->nodeName = (char *)MALLOC(strlen(tmp)+1))) {
		ErrMesg("Out of Memory reading VDATA node Name\n");
       		DTMendRead(n->port);
		return(0);
		}
	strcpy(d->nodeName,tmp);
	VDATAgetField(header,tmp,DTM_STRING_SIZE);
	if (!(d->fields= (char *)MALLOC(strlen(tmp)+1))) {
		ErrMesg("Out of Memory reading VDATA field\n");
       		DTMendRead(n->port);
		return(0);
		}
	strcpy(d->fields,tmp);
	size = d->dim[0] * d->dim[1];
	if (d->data = (char *) MALLOC(size * elementSize)) {
		ErrMesg("Out of Memory making space for VDATA\n");
       		DTMendRead(n->port);
		return(0);
		}

	if (DTMreadDataset(n->port, d->data, size, type) == DTMERROR){
			ErrMesg("Error reading DTM VDATA");
			NetReject(n->port,header);
			return(0);
			}
	DTMendRead(n->port);
	return(d);
	
#endif
}

static Data *NetReadSDS(n,header)
NetPort *n;
/* get n dimensional, n type SDS.  Return 0 on failure */
char *header;
{
#if 0
Data *d;
DTMCMD  cmd;
DTMTYPE type;
char	title[DTM_STRING_SIZE];
char	id[DTM_STRING_SIZE];
char	itsNew;
int 	rank,dims[MAX_ARRAY_DIM];
int 	size;
int	x;
int 	dostType;
int	stat;


#ifdef DEBUG
	printf("NetReadSDS(): going to read data for %s\n",header);
#endif


	if (-1 == SDSgetDimensions(header,&rank,dims,
			sizeof(MAX_ARRAY_DIM))) {
		ErrMesg("Failed at getting dimensions of DTM\n");
		NetReject(n->port,header);
		return(0);
		}

	for(x = 0, size = 1; x < rank; x++) 
		size *= dims[x];

        ANIMgetID(header,id,DTM_STRING_SIZE);
	NetUserListAdd(id);

	SDSgetTitle(header,title,DTM_STRING_SIZE);
	if ((!title) || (!strlen(title)))
		 strcpy(title,"Untitled");

	if (SDSgetType(header,&type) == -1 ) {
		ErrMesg("Error getting SDS type\n");
		NetReject(n->port,header);
		return(0);
		}
	switch(type) {
		case DTM_CHAR:
			dostType = DOST_Char;
			break;
		case DTM_FLOAT:
			dostType = DOST_Float;
			break;
		case DTM_INT:
			dostType = DOST_Int32;
			break;
		case DTM_SHORT:
			dostType = DOST_Int16;
			break;
		case DTM_DOUBLE:
			dostType = DOST_Double;
			break;
		default: 
			dostType = DOST_Char;
			printf(
			"SDS of unknown type just received casting to char\n");
		};
	if (d = (Data *) DataSearchByLabelAndDOTAndDOST(title,DOT_Array,
			dostType)) {
		itsNew = FALSE;
		}
	else {
		itsNew = TRUE;
		if (!(d = DataNew())) {
			ErrMesg("Out of memory reading in DTM SDS\n");
			NetReject(n->port,header);
			return(0);
			}
		d->entity = ENT_Network;
		d->dot	= DOT_Array;
		d->dost = 0;
		CopyDimensions(d->dim,&(d->rank),dims,rank);
                if (!(d->label= (char *) MALLOC(strlen(title)+1))) {
                	ErrMesg("Can't allocate memory for DTM SDS name");
			NetReject(n->port,header);
                        return(0);
                        }
		strcpy(d->label,title);
		}

	ANIMgetExpansion(header,&(d->expandX),&(d->expandY));
#if 0
	COLgetView(header, &(d->view_type));
#endif

	if (type == DTM_FLOAT || type == DTM_DOUBLE)
	  {
	    if (SDSgetMinMax(header, &d->min.f, &d->max.f) == DTMERROR)
		d->min.f = d->max.f = 0.;
	  }
	else
	  {
	    if (SDSgetMinMax(header, &d->min.f, &d->max.f) == DTMERROR)
		d->min.i = d->max.i = 0;
	    else d->min.i = (int)d->min.f, d->max.i = (int)d->max.f;
	  }
	if (d->associated)
		FREE(d->associated);
	if (COLgetAssoc(header, title, DTM_STRING_SIZE) != DTMERROR) {
		if (!(d->associated = (char *) MALLOC(strlen(title)+1))) {
			ErrMesg("Out of Memory\n");
			return(0);
			}
		strcpy(d->associated,title);
		}
	else
		d->associated = (char *)NULL;

#define READSDS(EltSize)						\
 do {									\
	if (! DimensionsEqual(d->dim,d->rank,dims,rank)) {		\
		if (!itsNew)						\
			FREE(d->data);					\
		itsNew = TRUE;						\
		CopyDimensions(d->dim,&(d->rank),dims,rank);		\
		}							\
	if (d->dost != dostType) {					\
		if (!itsNew)						\
			FREE(d->data);					\
		itsNew = TRUE;						\
		CopyDimensions(d->dim,&(d->rank),dims,rank);		\
		}							\
	d->dost = dostType;						\
	if (itsNew) {							\
		if (!(d->data = (char *) MALLOC(size * EltSize))) {	\
			ErrMesg("Out of Memory");			\
			NetReject(n->port,header);			\
			return(0);					\
			}						\
		}							\
	if (DTMreadDataset(n->port,d->data,size,			\
			   type) == DTMERROR){				\
		ErrMesg("Error reading DTM dataset");			\
		NetReject(n->port,header);				\
		return(0);						\
		}							\
    } while (0)

	if (type == DTM_CHAR) {
		READSDS(1);
		}
	else if (type == DTM_FLOAT) {
		READSDS(sizeof(float));
#ifdef DEBUG
			printf("\n\n\n\n#######\nThe first number is %f\n\n\n",
					*((float*) d->data));
#endif
		}
	else if (type == DTM_INT) {
		READSDS(4);
		}
	else if (type == DTM_SHORT) {
		READSDS(2);
		}
	else if (type == DTM_DOUBLE) {
		READSDS(sizeof(double));
		}
	else {
#ifdef DEBUG
		ErrMesg("ReadDTM(): Unknown type %d\n",type);
#endif
		NetReject(n->port,header);
		return(0);
		}

	DTMendRead(n->port);
	return(d);

#endif /* 0 */
} /* NetReadSDS() */




static Data *NetReadPal(n,header)
NetPort *n;
char *header;
{
#if 0
Data *d;
char title[DTM_STRING_SIZE];
char	id[DTM_STRING_SIZE];

#ifdef DEBUG
	printf("NetReadPal(): going to read data for %s\n",header);
#endif

        COLgetID(header,id,DTM_STRING_SIZE);
	NetUserListAdd(id);
	PALgetTitle(header,title,DTM_STRING_SIZE);
	if ((!title) || (!strlen(title)))
		strcpy(title,"Untitled");

	if (!(d = (Data *)DataSearchByLabelAndDOT(title,DOT_Palette8))) {
		if (!( d = DataNew())) {
			ErrMesg("Out of memory reading palette\n");
			return(0);
			}
		d->dot = DOT_Palette8;
		d->dost = DOST_Char;
		d->entity = ENT_Network;
		if (!( d->label = (char *) MALLOC(strlen(title) +1))) {
			ErrMesg("Out of memory reading palette\n");
			return(0);
			}
		strcpy(d->label,title);
		if (!( d->data= (char *) MALLOC(768))) {
			ErrMesg("Out of memory reading palette\n");
			return(0);
			}
		}
	if (d->associated)
		FREE(d->associated);
	if (COLgetAssoc(header, title, DTM_STRING_SIZE) != DTMERROR) {
		if (!(d->associated = (char *) MALLOC(strlen(title)+1))) {
			ErrMesg("Out of Memory\n");
			return(0);
			}
		strcpy(d->associated,title);
		}
	else
		d->associated = (char *)NULL;
	DTMreadDataset(n->port,d->data,768,DTM_CHAR);
	DTMendRead(n->port);
	return(d);

#endif /* 0 */
} /* NetReadPal() */


static Data *NetReadRIS8(n,header)
NetPort *n;
char *header;
{
#if 0
Data 	*d;
char	title[DTM_STRING_SIZE];
int	xdim,ydim;
char	itsNew;
char	id[DTM_STRING_SIZE];

#ifdef DEBUG
	printf("NetReadRIS8(): going to read data for %s\n",header);
#endif

	RISgetTitle(header,title,DTM_STRING_SIZE);
	RISgetDimensions(header,&xdim,&ydim);
        COLgetID(header,id,DTM_STRING_SIZE);
	NetUserListAdd(id);
	if ((!title) || (!strlen(title)))
		strcpy(title,"Untitled");
	if (d = (Data *) DataSearchByLabelAndDOTAndDOST(title,DOT_Array,
							DOST_Char)) {
		itsNew = FALSE;
		}
	else {
		itsNew = TRUE;
		if (!( d = DataNew())) {
			ErrMesg("Out of memory reading raster\n");
			NetReject(n->port,header);
			return(0);
			}
		d->dot = DOT_Array;
		d->dost = DOST_Char;
		d->entity = ENT_Network;
		d->rank = 2;
		d->dim[0] = xdim;
		d->dim[1] = ydim;
		if (!( d->label = (char *) MALLOC(strlen(title) +1))) {
			ErrMesg("Out of memory reading raster\n");
			NetReject(n->port,header);
			return(0);
			}
		strcpy(d->label,title);
		}

	if (SDSgetMinMax(header, &d->min.f, &d->max.f) == DTMERROR)
		d->min.f = d->max.f = 0.;
#if 0
	COLgetView(header, &(d->view_type));
#endif
	ANIMgetExpansion(header,&(d->expandX),&(d->expandY));
	if (d->associated)
		FREE(d->associated);
	if (COLgetAssoc(header, title, DTM_STRING_SIZE) != DTMERROR) {
		if (!(d->associated = (char *) MALLOC(strlen(title)+1))) {
			ErrMesg("Out of Memory\n");
			return(0);
			}
		strcpy(d->associated,title);
		}
	else
		d->associated = (char *)NULL;

	if (!(d->rank == 2 && d->dim[0] == xdim && (d->dim[1] == ydim) )) {
		if (!itsNew) FREE(d->data);
		itsNew = TRUE;
		d->rank = 2;
		d->dim[0] = xdim;
		d->dim[1] = ydim;
		}

	if (itsNew) {
		if (!( d->data = (char *) MALLOC(xdim*ydim))) {
			ErrMesg("Out of memory reading image\n");
			NetReject(n->port,header);
			return(0);
			}
		}

	if (DTMreadDataset(n->port,d->data,xdim*ydim,DTM_CHAR) == 
	    DTMERROR) {
		ErrMesg("Error reading RIS dataset");
		NetReject(n->port,header);
		return(0);
		}

	DTMendRead(n->port);

	return(d);

#endif
} /* NetReadRIS8() */




static Text *NetReadText(n,header)
/* Returns a static allocated text message.  However, t->textString is
   not static */
NetPort *n;
char *header;
{
#if 0
Data *d;
static	Text t;
static	char id[DTM_STRING_SIZE];
static	char title[DTM_STRING_SIZE];

#ifdef DEBUG
	printf("NetReadText(): I've been called.  header=%s\n",header);
#endif
	t.id = id;
	t.title = title;
	if (DTMERROR == TXTgetTitle(header,title,DTM_STRING_SIZE)) {
		strcpy(title,"Untitled");
		}
        if (DTMERROR == TXTgetID(header,id,DTM_STRING_SIZE)) {
		strcpy(id,"Unknown");
		}
	NetUserListAdd(id);

	t.selLeft = 0;
	t.selRight = 0;

	if (DTMERROR != TXTgetSelectionLeft(header,&(t.selLeft))) {
                if (DTMERROR == TXTgetSelectionRight(header,&(t.selRight))) {
                  ErrMesg("This DTM TXT select message is hosed. discarding\n");
                  }
		/*return(0);*/
                }

        if (DTMERROR == TXTgetDimension(header,&(t.dim))) {
                t.dim = 0;
                }
	if (!(t.textString = (char *) MALLOC(t.dim+1))) {
		ErrMesg("Out of Memory reading text\n");
		return(0);
		}
        if (DTMERROR == TXTgetInsertionPt(header,&(t.insertPt))) {
		t.insertPt = 0;
		}
	if (DTMERROR == TXTgetNumReplace(header,&(t.numReplace))) {
		t.numReplace = 0;
		}

        {int garbage;
        t.replaceAll = TXTshouldReplaceAll(header,garbage);
        }
#ifdef DEBUG
	printf("t.dim = %d\n",t.dim);
#endif
        if ((t.dim = DTMreadDataset(n->port,t.textString,t.dim,DTM_CHAR))
			== DTMERROR){
		ErrMesg("Error reading DTM dataset\n");
		DTMendRead(n->port);
		return(0);
		}
		
	t.textString[t.dim] = '\0';
#ifdef DEBUG
	printf("NetReadText(): *t.textString = %c dim = %d\n",
				*t.textString,t.dim);
	printf("NetReadText(): t.textString = \"%s\"\n",t.textString);
#endif
        DTMendRead(n->port);

	return(&t);
#endif
} /* NetReadText() */


static Col *NetReadCOL(n,header)
NetPort *n;
char *header;
{
#if 0
static	char title[DTM_STRING_SIZE];
static	char id[DTM_STRING_SIZE];
static	char func[DTM_STRING_SIZE];
static	Col col;
static	struct COL_TRIPLET	triplet[MAXDRAWDOODLE];
int	selType;
int	num, width;
char	buff[1024];

#ifdef DEBUG
	printf("NetReadCOL(): I've been called.  header=%s\n",header);
#endif
	col.title = title;
	col.id = id;
	col.func = func;

        COLgetTitle(header,col.title,DTM_STRING_SIZE);
        COLgetID(header,col.id,DTM_STRING_SIZE);
        COLgetFunc(header,col.func,DTM_STRING_SIZE,&(col.selType));

	if (((col.selType == COL_DOODLE_DISC)||(col.selType == COL_DOODLE_CONT))
		&&(strcmp(col.func, "DOODLE") == 0))
	{
		if (COLgetWidth(header,&width) == -1)
		{
#ifdef DEBUG
			fprintf(stderr, "NetReadCOL(): SENDER DIDN'T SET LINE WIDTH IN HEADER\n");
#endif
			width = 1;
		}
		col.width = width;
	}
	else
	{
		col.width = 1;
	}

        if (-1 == COLgetDimension(header,&(col.dim))) {
#ifdef DEBUG
		printf("NetReadCOL(): SENDER DIDN'T SET DIMENSIONS IN HEADER\n");
#endif
		col.dim = 1;
		}
	NetUserListAdd(col.id);
	selType = col.selType;
	if (selType == COL_DOODLE_DISC) {
		if ((num = DTMreadDataset(n->port,triplet,MAXDRAWDOODLE,
				COL_TRIPLET)) ==DTMERROR){
			sprintf(buff,"Error reading DTM dataset\n%s\n",
				NetDTMErrorString(DTMerrno));
			ErrMesg(buff);
			DTMendRead(n->port);
			return(0);
			}
		}
	else if (selType == COL_DOODLE_CONT) {
		if ((num = DTMreadDataset(n->port,triplet,MAXDRAWDOODLE,
				COL_TRIPLET)) ==DTMERROR){
			sprintf(buff,"Error reading DTM dataset\n%s\n",
				NetDTMErrorString(DTMerrno));
			ErrMesg(buff);
			DTMendRead(n->port);
			return(0);
			}
		}
        else if (selType == COL_AREA) {
                if ((num = DTMreadDataset(n->port,triplet,3,COL_TRIPLET))
                        ==DTMERROR){
			sprintf(buff,"Error reading DTM dataset\n%s\n",
				NetDTMErrorString(DTMerrno));
			ErrMesg(buff);
                        DTMendRead(n->port);
                        return(0);
                        }
		}
	else if (selType == COL_LINE) {
                if ((num = DTMreadDataset(n->port,triplet,2,COL_TRIPLET))
                        ==DTMERROR){
			sprintf(buff,"Error reading DTM dataset\n%s\n",
				NetDTMErrorString(DTMerrno));
			ErrMesg(buff);
                        DTMendRead(n->port);
                        return;
                        }
		}
	else if (selType == COL_POINT) {
                if ((num = DTMreadDataset(n->port,triplet,1,COL_TRIPLET))
                        ==DTMERROR){
			sprintf(buff,"Error reading DTM dataset\n%s\n",
				NetDTMErrorString(DTMerrno));
			ErrMesg(buff);
                        DTMendRead(n->port);
                        return;
                        }
		}
	else {
#ifdef DEBUG
		printf("Got a DTM COL class selType that I don't know\n");
#endif
		num = 0;
		}
	col.dim = num;
	col.data = triplet;
	DTMendRead(n->port);

#ifdef DEBUG
printf("col.dim is %d\n",col.dim);
#endif
	return(&col);
#endif
} /* NetReadCOL() */

char *NetReadMSG(n,header)
NetPort *n;
char *header;
{
static  char s[2*DTM_STRING_SIZE];

	DTMendRead(n->port);
	if (COLgetID(header, s, DTM_STRING_SIZE) != DTMERROR)
		strcat(s, ":");
	else
		s[0] = 0;
	MSGgetString(header,s+strlen(s),DTM_STRING_SIZE);
	return(s);
}

static NetPort *NetSearchListForPortName(list,portName)
List *list;
char *portName;
{
NetPort *netPort;
	
	netPort = (NetPort *) ListHead(list);
	while (netPort) {
		if (!strcmp(portName,netPort->portName)) {
#ifdef DEBUG
printf("NetSearchListForPortName:\"%s\" == \"%s\" RETURNING a netPort\n",
			portName,netPort->portName);
#endif
			return(netPort);
			}
#ifdef DEBUG
		printf("NetSearchListForPortName:\"%s\" != \"%s\"\n",
			portName,netPort->portName);
#endif
		netPort = (NetPort *) ListNext(list);
		}
	return(0);
}

static Server *NetReadSRV(n,header)
NetPort *n;
char *header;
{
static Server s;
char inPort[80];
NetPort *out;
char buff[256];

#ifdef DEBUG
	printf("NetReadSRV(): I've been called.  header=%s\n",header);
#endif

	DTMendRead(n->port);
	SRVgetID(header,s.id,80);
        SRVgetFunction(header,&(s.func));
	NetUserListAdd(s.id);

	switch (s.func) {
	    case SRV_FUNC_CONNECT:
                SRVgetInPort(header,s.inPort,80);
#ifdef DEBUG
		printf("Just got a SRV connect from id=%s, inPort=%s\n",
			s.id,s.inPort);
		printf("This was read from port=%s\n", n->portName);
#endif
		if (s.netPort = NetSearchListForPortName(netOutList,s.inPort)){
#ifdef DEBUG
			printf("Already connected to this address\n");
#endif
			return(0);
			}
		
		sprintf(buff,
			"Just established a connection with\n%s (%s)\n",
			s.id,s.inPort);
		WriteMesg(buff);
		if (s.netPort = (NetPort *) ListHead(netOutList))
			NetChangeOutPort(s.inPort,s.netPort);
		else {
			/* don't have an out port... make one */
			s.netPort = NetCreateOutPort(s.inPort);
			}
		strcpy(s.inPort,s.netPort->portName);
                break;

	    case SRV_FUNC_DISCONNECT:
#ifdef DEBUG
		printf("Just got a SRV disconnect from portName=%s\n",
			n->portName);
#endif
		s.inPort[0]='\0';
		s.netPort = n;
		out = (NetPort *) ListHead(netOutList);
                SRVgetInPort(header,s.inPort,80);
		sprintf(buff,
			"Just received a disconnect from\n(%s)\n",
			 s.inPort);
		WriteMesg(buff);
		if (strlen(s.inPort)) {
			while(out) {
				if (!strcmp(out->portName,s.inPort)) {
					NetRemovePortFromSendQueue(out);
					ListDeleteEntry(netOutList,out);
					DTMdestroyPort(out->port);
					out->open = FALSE;
					out->type = NET_UNDEF;
					break;
					}
				out = (NetPort *) ListNext(netOutList);
				}
			if (!out)
			    ErrMesg("Got a disconnect with a bogus port\n");
			}
		else {
			/***** this assumes only one out port **********/
			ListDeleteEntry(netOutList,ListHead(netOutList)); 
			}
                break;

	    case SRV_FUNC_LOCK:
	    case SRV_FUNC_UNLOCK:
	    case SRV_FUNC_ADD_USER:
	    case SRV_FUNC_REMOVE_USER:
		break;

	    default:
		printf("Just received an unknown SRV function\n");
		return(0);
	}


	return(&s);
} /* NetReadSRV() */


static Server *NetReadDTM(n,header)
NetPort *n;
char *header;
{
static Server s;
char buff[256];

#ifdef DEBUG
	printf("NetReadDTM(): I've been called.  header=%s\n",header);
#endif
	DTMendRead(n->port);
	header +=4;
	strncpy(s.inPort,header,79);
	s.inPort[79]='\0';
        s.func = SRV_FUNC_CONNECT;
#ifdef DEBUG
	printf("NetReadDTM(): s.inPort = \"%s\"\n",s.inPort);
#endif
	if (s.netPort = NetSearchListForPortName(netOutList,s.inPort)){
#ifdef DEBUG
		printf("Already connected to this address\n");
#endif
		return(0);
		}
	sprintf(buff, "Just established a connection with\nport=%s\n",
			s.inPort);
	WriteMesg(buff);
	if (s.netPort = (NetPort *) ListHead(netOutList))
		NetChangeOutPort(s.inPort,s.netPort);
	else {
		/* don't have an out port... make one */
		s.netPort = NetCreateOutPort(s.inPort);
		}
	strcpy(s.inPort,s.netPort->portName);
	return(&s);
	
} /* NetReadDTM() */


static Exec *NetReadEXEC(n,header)
NetPort *n;
char *header;
{
static	char 	id[80];
static	char	retAddress[80];
static	char	authentication[256];
static	char	timeStamp[80];
static	Exec	exec;


	EXECgetID(header,id,80);
	EXECgetAddress(header,retAddress,80);
	EXECgetAuthentication(header,authentication,256);
	EXECgetTimeStamp(header,timeStamp,80);
	exec.id = id;
	exec.retAddress = retAddress;
	exec.authentication = authentication;
	exec.timeStamp = timeStamp;
	EXECgetType(header,&(exec.type));
	switch(exec.type) {
		case EXEC_HOST_STATUS_QUERY:
			break;
		case EXEC_HOST_STATUS_RETURN:
			EXECgetLoad1(header, &(exec.info.hsReturn.load1));
			EXECgetLoad5(header, &(exec.info.hsReturn.load5));
			EXECgetLoad15(header, &(exec.info.hsReturn.load15));
			EXECgetNumUsers(header,&(exec.info.hsReturn.numUsers));
			break;
		case EXEC_EXECUTE:
			break;
		case EXEC_EXECUTE_RETURN:
			break;
		case EXEC_PROC_STATUS_QUERY:
			break;
		case EXEC_PROC_STATUS_RETURN:
			break;
		case EXEC_FILE_PUT:
			break;
		case EXEC_FILE_GET:
			break;
		};

	DTMendRead(n->port);

	return(&exec);
}




static AnimMesg *NetReadANIM(n,header)
NetPort *n;
char *header;
{
#if 0
static AnimMesg a;
static char id[80];
static char title[1024];
int func;
int runType;

	DTMendRead(n->port);
	ANIMgetTitle(header,title,1024);
	a.title = title;
	ANIMgetID(header,id,80);
	a.id = id;
	ANIMgetFrame(header,&(a.frameNumber));
	if (-1 == ANIMgetFunc(header,(&func))) 
		a.func = AF_NO_FUNC;
	else {
		switch(func) {
		    case  ANIM_FUNC_STOP:
				a.func = AF_STOP;
				break;
		    case ANIM_FUNC_FPLAY:
				a.func = AF_FPLAY;
				break;
		    case ANIM_FUNC_RPLAY:
				a.func = AF_RPLAY;
				break;
		    };
		}
	if (-1 == ANIMgetRunType(header,(&runType))) 
		a.runType= ART_NONE;
	else {
		switch (runType) {
		    case ANIM_RUN_TYPE_SINGLE:
				a.runType = ART_SINGLE;
				break;
		    case ANIM_RUN_TYPE_CONT:
				a.runType = ART_CONT;
				break;
		    case ANIM_RUN_TYPE_BOUNCE:
				a.runType = ART_BOUNCE;
				break;
		    };
		}
		
	a.data = 0;
	return(&a);
#endif
}

#define CALLCB(List, CallB, CallData, ClientData)			\
 do {									\
	while (docb) {							\
		if (ExceptModuleName)  {				\
			if (docb->CallB &&				\
			    strcmp(ExceptModuleName,docb->moduleName)) {\
				(docb->CallB)(CallData,docb->ClientData);\
			    }						\
			}						\
                else {							\
			if (docb->CallB) {				\
				(docb->CallB)(CallData,docb->ClientData);\
                        	}					\
			}						\
                docb = (DOCB *) ListNext(List);				\
		}							\
    } while (0)

static int NetTextDistribute(text,ExceptModuleName)
Text *text;
char *ExceptModuleName;
{
DOCB *docb;

	if (!(docb = (DOCB *) ListHead(TXTList))) {
		return(0);
		}
        if (ExceptModuleName &&(!strcmp(docb->moduleName,ExceptModuleName))) {
                if (!(docb = (DOCB *) ListNext(TXTList)))
                        return(0); /* none to distribute to */
                }

	CALLCB(TXTList, newCB, text, newData);

        return(1);
}



static int NetCOLDistribute(col,ExceptModuleName)
/* calls all the data object callbacks (DOCB) for COL*/
Col *col;
char *ExceptModuleName;	/* don't distribute to this moduleName */

{
DOCB *docb;

	if (!(docb = (DOCB *) ListHead(COLList))) {
		return(0); /* none to distribute to */
		}
	if (ExceptModuleName &&(!strcmp(docb->moduleName,ExceptModuleName))) {
		if (!(docb = (DOCB *) ListNext(COLList)))
			return(0); /* none to distribute to */
		}

	/* distribute */
	CALLCB(COLList, newCB, col, newData);

	return(1);

} /* NetCOLDistribute() */

static Col *NetMakeCOLFromDoodle(title,doodle,length,sendDiscrete)
char *title;
struct COL_TRIPLET *doodle;
int length;
int sendDiscrete;
{
#if 0
static Col col;

	col.title = title;
	col.id = UserID;
	col.func = "DOODLE";
	if (sendDiscrete) 
		col.selType = COL_DOODLE_DISC;
	else
		col.selType = COL_DOODLE_CONT;
	col.dim = length;
	col.data = doodle;
	return(&col);
#endif
}
	
static void NetCallDestroyCallback(list,d)
List list;
Data *d;
{
DOCB *docb;

	docb = (DOCB *) ListHead(list);
	while (docb) {
		if (docb->destroyCB)
			(docb->destroyCB)(d,docb->destroyData);
		docb = (DOCB *) ListNext(list);
		}
}
static int NetAnimationDistribute(dSend,ExceptModuleName)
Data **dSend;
char *ExceptModuleName;
{
#if 0
Data *d;
DOCB *docb;
static AnimMesg a;
        if (!(docb = (DOCB *) ListHead(ANIMList))) {
                return(0); /* none to distribute to */
                }
#ifdef DEBUG
	printf("bink\n");
#endif
        if (ExceptModuleName &&(!strcmp(docb->moduleName,ExceptModuleName))) {
                if (!(docb = (DOCB *) ListNext(ANIMList)))
                        return(0); /* none to distribute to */
                }
#ifdef DEBUG
	printf("boink\n");
#endif
	if (d = DataSearchByLabelAndDOT((*dSend)->label,DOT_Array)) {
		/* if this isn't the same dim or dost, could be trouble */
		if (d->dost == (*dSend)->dost)
		  if (DimensionsEqual(d->dim,d->rank,(*dSend)->dim,
				      (*dSend)->rank)){
			NetCallDestroyCallback(ANIMList,d);
			FREE(d->data);
			d->data = (*dSend)->data;
			}
		  else {
			NetCallDestroyCallback(ANIMList,d);
			FREE(d->data);
			d->data = (*dSend)->data;
			CopyDimensions(d->dim,&(d->rank),
					(*dSend)->dim,(*dSend)->rank);
		      }
		memcpy(d, (*dSend), sizeof(Data));
		FREE(*dSend);
		*dSend = d;
		}
	else {
		d = (*dSend);
		}
	a.title = d->label;
	a.id = userID;
	a.func = AF_NO_FUNC;
	a.runType = ART_NONE;
	a.data = d;
	
	/* distribute data */
	if (!DataInList(d)) {
		DataAddEntry(d);
		CALLCB(ANIMList, newCB, &a, newData);
		}
	else {
		CALLCB(ANIMList, changeCB, &a, changeData);
		}
#ifdef DEBUG
	printf("bonk\n");
#endif
	return(1);
#endif
}


static int NetArrayDistribute(dSend,ExceptModuleName)
Data **dSend;
char *ExceptModuleName;
{
#if 0
Data *d;
DOCB *docb;
        if (!(docb = (DOCB *) ListHead(SDSList))) {
                return(0); /* none to distribute to */
                }
#ifdef DEBUG
	printf("bink\n");
#endif
        if (ExceptModuleName &&(!strcmp(docb->moduleName,ExceptModuleName))) {
                if (!(docb = (DOCB *) ListNext(SDSList)))
                        return(0); /* none to distribute to */
                }
#ifdef DEBUG
	printf("boink\n");
#endif

	if (d = DataSearchByLabelAndDOTAndDOST((*dSend)->label,DOT_Array,
			(*dSend)->dost)) {
		/* if this isn't the same dost, could be trouble */
		if (d->dost == (*dSend)->dost) {
			if (! DimensionsEqual(d->dim,d->rank,
					(*dSend)->dim,(*dSend)->rank)) {
				NetCallDestroyCallback(SDSList,d);
				FREE(d->data);
				d->data = (*dSend)->data;
				CopyDimensions(d->dim,&(d->rank),
					(*dSend)->dim,(*dSend)->rank);
				}
			else {
				NetCallDestroyCallback(SDSList,d);
				FREE(d->data);
				d->data = (*dSend)->data;
				}
			}
		if (d->associated && d->associated != (*dSend)->associated)
			FREE(d->associated);
		memcpy(d, (*dSend), sizeof(Data));
		FREE(*dSend);
		*dSend = d;
		}
	else {
		d = *dSend;
		}
	/* distribute data */
	if (!DataInList(d)) {
		DataAddEntry(d);
		CALLCB(SDSList, newCB, d, newData);
		}
	else {
		CALLCB(SDSList, changeCB, d, changeData);
		}
#ifdef DEBUG
	printf("bonk\n");
#endif
	return(1);
#endif
} /* NetArrayDistribute() */


int NetRISDistribute(dSend,ExceptModuleName)
Data **dSend;
char *ExceptModuleName;
{
#if 0
	Data *d;
	DOCB *docb;

	if (!(docb = (DOCB *) ListHead(RIS8List)))
		return(0); /* none to distribute to */
	if (ExceptModuleName &&(!strcmp(docb->moduleName,ExceptModuleName))) {
		if (!(docb = (DOCB *) ListNext(RIS8List)))
			return(0); /* none to distribute to */
		}
	if (d = DataSearchByLabelAndDOTAndDOST((*dSend)->label,DOT_Array,
			DOST_Char)) {
		/* if this isn't the same dost, could be trouble */
		if (d->dost == (*dSend)->dost) {
			if (! DimensionsEqual(d->dim,d->rank,
					(*dSend)->dim,(*dSend)->rank)) {
				NetCallDestroyCallback(RIS8List,d);
				FREE(d->data);
				d->data = (*dSend)->data;
				CopyDimensions(d->dim,&(d->rank),
					(*dSend)->dim,(*dSend)->rank);
				}
			else {
				NetCallDestroyCallback(RIS8List,d);
				FREE(d->data);
				d->data = (*dSend)->data;
				}
			}
		if (d->associated && d->associated != (*dSend)->associated)
			FREE(d->associated);
		memcpy(d, (*dSend), sizeof(Data));
		FREE(*dSend);
		*dSend = d;
		}
	else {
		d = *dSend;
	      }
	
	/* distribute data */
	if (!DataInList(d)) {
		DataAddEntry(d);
		CALLCB(RIS8List, newCB, d, newData);
		}
	else {
		CALLCB(RIS8List, changeCB, d, changeData);
		}

	return(1);
#endif
} /* NetRISDistribute() */


int NetPALDistribute(title,rgb,associated,ExceptModuleName)
/* calls all the data object callbacks (DOCB) for palettes */
char *title;
unsigned char *rgb;
char *associated;
char *ExceptModuleName;	/* don't distribute to this moduleName */

{
#if 0
DOCB *docb;
register int x;
Data *d;
register char *p;

	if (!(docb = (DOCB *) ListHead(PALList))) {
		return(0); /* none to distribute to */
		}
	if (ExceptModuleName &&(!strcmp(docb->moduleName,ExceptModuleName))) {
		if (!(docb = (DOCB *) ListNext(PALList)))
			return(0); /* none to distribute to */
		}
	
	/* get data field make a new one if doesn't exist */
	if (!(d = DataSearchByLabelAndDOT(title,DOT_Palette8))) {
		if (!(d = DataNew())) {
			return(0); /* out of memory */
			}
		if (!(d->label = (char *) MALLOC(strlen(title)+1))) {
			ErrMesg("Out of Memory\n");
			return(0);
			}
		strcpy(d->label,title);
		d->entity = ENT_Internal;
		d->dot = DOT_Palette8;
		d->dost = DOST_Char;
		if (!(d->data = (char *) MALLOC(768))) {
			ErrMesg("Out of Memory\n");
			return(0);
			}
		}

	p = d->data;
	for (x=0; x < 768; x++)
		*p++ = *rgb++;

	if (d->associated)
		FREE(d->associated);
	if (associated) {
		if (!(d->associated = (char *) MALLOC(strlen(associated)+1))) {
			ErrMesg("Out of Memory\n");
			return(0);
			}
		strcpy(d->associated,associated);
		}
	else
		d->associated = associated;

	/* distribute data */
	if (!DataInList(d)) {
		DataAddEntry(d);
		CALLCB(PALList, newCB, d, newData);
		}
	else {
		CALLCB(PALList, changeCB, d, changeData);
		}
	return(1);
#endif
} /* NetPALDistribute() */
	
#undef CALLCB
#define CALLCB(List, CallB, CallData, ClientData)			\
 do {									\
	while (docb) {							\
		if (docb->CallB) {					\
			(docb->CallB)(CallData,docb->ClientData);	\
                       	}						\
                docb = (DOCB *) ListNext(List);				\
		}							\
    } while (0)




static void *
NetReadMessage(n)
NetPort *n;
{
char    header[DTM_MAX_HEADER];
int	length;
DOCB *docb;
Data 	*d = NULL;
Text	*t;
Col	*c;
Com	*com;
Server	*s;
Exec	*e;
char	*mesg;
static AnimMesg a;
AnimMesg *ap;
static char    id[DTM_STRING_SIZE];

char	buff[256];
int 	i;

#ifdef DEBUG
	printf("NetReadMessage(): I've been called.\n");
#endif
        if ((length =DTMbeginRead(n->port,header,DTM_MAX_HEADER)) == DTMERROR){
		sprintf(buff,"Error reading DTM header from port %s (%d) ret %d\nDTM error= %s\n",
				n->portName,n->port,length,
				NetDTMErrorString(DTMerrno));
		ErrMesg(buff);
#ifdef DEBUG
		printf(buff,"header= %s\n",header);
#endif
		DTMendRead(n->port);
                return(0);
                }

	if (SDScompareClass(header) && ANIMisAnimation(header,i)) {
#ifdef DEBUG
		printf("Treating SDS as an Animation\n");
#endif
		if (!(docb = (DOCB *) ListHead(ANIMList))) {
			NetReject(n->port,header);
			return(0);
			}
		if (d = NetReadSDS(n,header)) {
			a.data = d;
			a.title = d->label;
			a.func = AF_NO_FUNC;
			a.runType = ART_NONE;
			a.id = id;
			ANIMgetID(header,a.id,DTM_STRING_SIZE);
			if (!DataInList(d)) {
				DataAddEntry(d);
				CALLCB(ANIMList, newCB, &a, newData);
				}
			else {
				CALLCB(ANIMList, changeCB, &a, changeData);
				}
			}
		} /* SDS Animation*/
	else if (ANIMcompareClass(header)) {
		if (!(docb = (DOCB *) ListHead(ANIMList))) {
			NetReject(n->port,header);
			return(0);
			}
		if (ap = NetReadANIM(n,header)) {
			CALLCB(ANIMList, newCB, ap, newData);
			}
		} /* ANIM */
	else if (SDScompareClass(header)) {
#ifdef DEBUG
		printf("SDS is not an Animation\n");
#endif
		if (!(docb = (DOCB *) ListHead(SDSList))) {
			NetReject(n->port,header);
			return(0);
			}
		if (d = NetReadSDS(n,header)) {
			if (!DataInList(d)) {
				DataAddEntry(d);
				CALLCB(SDSList, newCB, d, newData);
				}
			else {
				CALLCB(SDSList, changeCB, d, changeData);
				}
			}
		} /* SDS */

	else if (PALcompareClass(header)) {
		if (!(docb = (DOCB *) ListHead(PALList))) {
			NetReject(n->port,header); 
			return(0);
			}
		if (d = NetReadPal(n,header)) {
			if (!DataInList(d)) {
				DataAddEntry(d);
				CALLCB(PALList, newCB, d, newData);
				}
			else {
				CALLCB(PALList, changeCB, d, changeData);
				}
			}
		
		}/* PAL */

	else if (RIScompareClass(header)) {
		if (!(docb = (DOCB *) ListHead(RIS8List))) {
			NetReject(n->port,header); 
			return(0);
			}
		if (d = NetReadRIS8(n,header)) {
			if (!DataInList(d)) {
				DataAddEntry(d);
				CALLCB(RIS8List, newCB, d, newData);
				}
			else {
				CALLCB(RIS8List, changeCB, d, changeData);
				}
			}
		
		}/* RIS */
	else if (TXTcompareClass(header)) {
		if (!(docb = (DOCB *) ListHead(TXTList))) {
			NetReject(n->port,header); 
			return(0);
			}
		if (t = NetReadText(n,header)) {
			CALLCB(TXTList, newCB, t, newData);
			if (t->textString)
				FREE(t->textString);
			}
		} /* TXT */
	else if (SRVcompareClass(header)) {
		docb = (DOCB *) ListHead(SRVList);
		if (s = NetReadSRV(n,header)) {
			CALLCB(SRVList, newCB, s, newData);
			}
		} /*SRV*/
	else if (COLcompareClass(header)) {
		if (!(docb = (DOCB *) ListHead(COLList))) {
			NetReject(n->port,header); 
			return(0);
			}
		if (c = NetReadCOL(n,header)) {
			CALLCB(COLList, newCB, c, newData);
			}
		} /*COL */
	else if (DTMcompareClass(header)) {
		docb = (DOCB *) ListHead(DTMList);
		if (s = NetReadDTM(n,header)) {
			CALLCB(COLList, newCB, s, newData);
			}
		} /*DTM*/
	else if (SDLcompareClass(header)) {
		docb = (DOCB *) ListHead(SDLList);
		if (d = NetReadSDL(n,header)) {
			CALLCB(SDLList, newCB, d, newData);
			}
		} /* SDL */
	else if (COMcompareClass(header)) {
		docb = (DOCB *) ListHead(COMList);
		if (com = NetReadCOM(n,header)) {
			CALLCB(COMList, newCB, com, newData);
			}
		} /* COM */
	else if (VDATAcompareClass(header)) {
		docb = (DOCB *) ListHead(VDATAList);
		if (d = NetReadVDATA(n,header)) {
			CALLCB(VDATAList, newCB, d, newData);
			}
		}
	else if (EXECcompareClass(header)) {
		docb = (DOCB *) ListHead(EXECList);
		if (e = NetReadEXEC(n,header)) {
			CALLCB(EXECList, newCB, e, newData);
			}
		} /* EXEC */
	else if (MSGcompareClass(header)) {
		docb = (DOCB *) ListHead(MSGList);
		if (mesg = NetReadMSG(n,header)) {
			CALLCB(MSGList, newCB, mesg, newData);
			}
		}
	else {
		NetReject(n->port,header); 
		}

	return(d);


} /* NetReadMessage() */

static NetPort *NetSearchListForDTMPort(netPortList,port)
List netPortList;
int port;
{
NetPort *netPort;
	
	netPort = (NetPort *) ListHead(netPortList);
	while (netPort) {
		if (netPort->port == port) {
			return(netPort);
			}
		netPort = (NetPort *) ListHead(netPortList);
		}
	return(0);
}

#if 0			/* not called in Collage */
int NetServPollAndRead()
/* this blocks until something is ready to read*/
/* return -1 on Error, 0 on nothing read, 1 on read */
{
NetPort *n;
int length;
Dtm_set s[64];
int num;
int x;
int retStatus;

	n = (NetPort *) ListHead(netInList);
	num = 0;
	while (n) {
		s[num++].port = n->port;
		n = (NetPort *) ListNext(netInList);
		}
	if (DTMERROR == DTMselectRead(s,num,0,0,1000)) {
		WriteMesg("Error checking for DTM input\n");
		return(-1);
		}
	retStatus = 0;
	for (x = 0; x < num; x++) {
		if (s[x].status) {
			n = NetSearchListForDTMPort(netInList,s[x].port);
			NetReadMessage(n);
			retStatus = 1;
			}
		}

	return(retStatus);
}
#endif				/* not used */

void NetClientPollAndRead()
/* Check all in ports, read data and make data callbacks */
/* *should* not block */
{
Data *d;
NetPort *n;

#ifdef DEBUG

/*	printf("NetClientPollAndRead(): I've been called\n");*/
#endif
	n = (NetPort *) ListHead(netInList); 
	while (n) {
		while (DTMavailRead(n->port)) {
#ifdef DEBUG
			fprintf( stderr, "Reading message from port %s\n",n->portName);
#endif
#ifdef USE_FEEDBACK_CALLS
			SetReadFeedback();
#endif
			(void) NetReadMessage(n);
#ifdef USE_FEEDBACK_CALLS
			UnsetFeedback();
#endif
			}
		n = (NetPort *) ListNext(netInList);
		}
	NetTryResend();
}


static int
NetOpenPort(netPort)
NetPort *netPort;
{
	if (!netPort->open) {
		int out;

		out = DTMmakeOutPort(netPort->portName, dtmFlowControl);
		if (out == DTMERROR)
			return DTMERROR;
		netPort->port = out;
		netPort->open = TRUE;
		netPort->queueTime = 0;
	}
	return 0;
}

static int
NetSend(netPort,header,data,num,type)
/* Attempt to send. return 0 on can't yet, -1 on error, 1 on success */
NetPort *netPort;
char	*header;
char	*data;
long	num;
DTMTYPE type;
{
char buff[1024];
int status;

#ifdef USE_AVAIL_WRITE
	if (DTMavailWrite(netPort->port)) {
#endif
#ifdef DEBUG
        printf("NetSend():Sending \"%s\" to %s\n",header,netPort->portName);
#endif


#ifdef USE_FEEDBACK_CALLS
	SetWriteFeedback();
#endif

#ifdef USE_WRITEMSG
		if (DTMERROR == DTMwriteMsg(netPort->port,
				header,strlen(header)+1,
				data,num,type)) 
#else
		status = DTMbeginWrite(netPort->port,header,strlen(header)+1);  
#ifdef DEBUG
        printf("NetSend():sent header \"%s\" to %s\n",header,netPort->portName);
#endif
		if ((status != DTMERROR) && num) {
			status = DTMwriteDataset(netPort->port,data,num,type);
			}
		if (status == DTMERROR)
#endif
			{
			sprintf(buff,"Error sending to %s\nError is %s\n",
				netPort->portName,NetDTMErrorString(DTMerrno));
			ErrMesg(buff);
#ifndef USE_WRITEMSG
			DTMendWrite(netPort->port);
#endif
#ifdef USE_FEEDBACK_CALLS
			UnsetFeedback();
#endif
			return(-1);
			}
#ifndef USE_WRITEMSG
		DTMendWrite(netPort->port);
#endif
#ifdef USE_FEEDBACK_CALLS
		UnsetFeedback();
#endif
#ifdef DEBUG
		printf("MESSAGE SENT:\"%s\" to %s\n",header,netPort->portName);
#endif
		netPort->queueTime = 0;
		return(1);
#ifdef USE_AVAIL_WRITE
		}
	else {
		if (netPort->queueTime == 0)
			netPort->queueTime = time(0);
		return(0);
		}
#endif
} /* NetSend() */



static int
DEFUN(NetClientSendMessage, (netPort,header,data,num,type,
		cb,cbData,failCB,failCBData,doQueue),
/* Attempt to send. return 0 on can't yet, -1 on error, 1 on success */
NetPort *netPort AND
char	*header AND
GenericPtr data AND
long	num AND
DTMTYPE type AND
void 	(*cb) PARAMS((GenericPtr data, caddr_t cbData)) AND
caddr_t	cbData AND
void 	(*failCB) PARAMS((GenericPtr data, caddr_t failCBData)) AND 
caddr_t	failCBData AND
int	doQueue)     /* TRUE -> Save and resend; FALSE -> let client resend*/

{
SQueue *sq;
int status;

	if (!netPort || ! netPort->open) {
		if (!(netPort = (NetPort *) ListHead(netOutList))) {
#ifdef DEBUG
			printf("no out port: discarding %s\n",header);
#endif
			if (failCB) 
				failCB(data,failCBData);
			return(-1);
			}
		}
#ifdef DEBUG
	printf("Attempting to send \"%s\" to netPort=%x %x (%s)\n",header,
			netPort,netPort->port,netPort->portName);
#endif

	/*
	 * Before sending this data, first we must flush any pending data
	 * on this port, to preserve sending order.
	 * If we can't flush all pending data, we must queue this data
	 * for later sending.
	 */
	status = NetFlushPort(netPort);
	if (status == 0) {
		if (doQueue) {
			if (!(sq = (SQueue *) MALLOC(sizeof(SQueue)))) {
				ErrMesg("Out of Memory\n");
				return(-1);
				}
			if (!(sq->header = (char *) MALLOC(strlen(header)+1))) {
				ErrMesg("Out of Memory when putting on send queue\n");
				return(-1);
				}
			strcpy(sq->header,header);
			sq->netPort = netPort;
			sq->data = data;
			sq->num = num;
			sq->type = type;
			sq->cb = cb;
			sq->cbData = cbData;
			sq->failCB = failCB;
			sq->failCBData = failCBData;
			sq->numTries = 1;
			ListAddEntry(sendQueue,(char *)sq);
#ifdef DEBUG
			printf(
			    "message queued for later sending on netPort %x\n",
			    sq->netPort);
#endif
			}
		else {
			if (cb) 
				cb(data,cbData);
			}
		return(0);
		}

	if (!(status = NetSend(netPort,header,data,num,type))) {
	    if (doQueue) {
		if (!(sq = (SQueue *) MALLOC(sizeof(SQueue)))) {
			ErrMesg("Out of Memory\n");
			return(-1);
			}
		if (!(sq->header = (char *) MALLOC(strlen(header)+1))) {
			ErrMesg("Out of Memory when putting on send queue\n");
			return(-1);
			}
		strcpy(sq->header,header);
		sq->netPort = netPort;
/*EJB
		if (!(sq->netPort = (NetPort *) MALLOC(sizeof(netPort)))){
			ErrMesg("Out of memory when putting on send queue\n");
			return(0);
			}
		bcopy((char *) netPort,(char *) sq->netPort,sizeof(NetPort));
*/
		sq->data = data;
		sq->num = num;
		sq->type = type;
		sq->cb = cb;
		sq->failCB = failCB;
		sq->cbData = cbData;
		sq->failCBData = failCBData;
		sq->numTries = 1;
		ListAddEntry(sendQueue,sq);
#ifdef DEBUG
		printf("message queued for later sending on netPort %x\n",
			    sq->netPort);
#endif
		}
	    else {
                /* couldn't send now and no queuing, so call failCB()*/
		if (failCB) 
			failCB(data,failCBData);
		}
		return(0);
	    }
	else if (status == 1) {
		if (cb) 
			cb(data,cbData);
		return(1);
		}
	else if (status == -1 ) {
		if (failCB)
			failCB(data,failCBData);
		return(-1);
		}
	return(-1);
		
} /* NetClientSendMessage() */


static int NetFlushPort(port)
NetPort *port;
{
SQueue *sq;
char buff[DTM_MAX_HEADER+80];
int status;
time_t t;


	sq = (SQueue *) ListHead(sendQueue);
#ifdef DEBUG
	if (sq)  {
/*
		fprintf(stderr,"Flushing queue for port %s\n",port->portName);
*/
		fprintf(stderr,"Want to send %s to %s\n",
				sq->header,sq->netPort->portName);
		fprintf(stderr,"port = %x (%s) ,sq->netPort=%x (%s)\n",
				port,
				port->portName,
				sq->netPort,
				sq->netPort->portName);
		fprintf(stderr,"*"); 
		}
#endif
	while (sq) {
		if (sq->netPort == port) {
#ifdef DEBUG
			fprintf(stderr,".");
#endif
			if (status = NetSend(sq->netPort,sq->header,
					sq->data,sq->num,sq->type)){
				if ((status == 1) &&(sq->cb))
					(sq->cb)(sq->data,sq->cbData);
				ListDeleteEntry(sendQueue,sq);
				FREE(sq->header);
				FREE(sq);
				}
			else {
                                (sq->numTries)++;
				t = time(0);
/*
	                        if (sq->numTries > netMaxAttemptsToSend) {
*/
				if (t >(sq->netPort->queueTime + netTimeOut)){
					sprintf(buff,
					    "Couldn't send %s :DISCARDING\nafter %d tries and %d seconds",
					    sq->header,sq->numTries,
					    t - sq->netPort->queueTime);
                                	ErrMesg(buff);
					DTMdestroyPort(sq->netPort->port);
					sq->netPort->open = FALSE;
                                	ListDeleteEntry(sendQueue,sq);
                                	FREE(sq->header);
					if (sq->failCB)
					    (sq->failCB)(sq->data,sq->failCBData);
                                	FREE(sq);
                                	}

				return(0);
				}
			sq = (SQueue *) ListCurrent(sendQueue);
			}
		else {
			sq = (SQueue *) ListNext(sendQueue);
			}
		}
	return(1);
} /* NetFlushPort()*/


void NetTryResend()
{
int status;
NetPort *n;

#ifdef DEBUG
/*
	if (ListHead(sendQueue))
		printf("NetTryResend(): There is something on the SendQueue\n");
	else
		printf("NetTryResend(): Nothing on the SendQueue\n");
*/
#endif
	n = (NetPort *) ListHead(netOutList);
	while (n) {
		status = NetFlushPort(n);
		ListMakeEntryCurrent(netOutList,n);
		n = (NetPort *) ListNext(netOutList);
		}
} /* NetTryResend()*/

int NetSendDisconnect(netPort,cb,failCB)
NetPort *netPort;
void 	(*cb)();
void 	(*failCB)();
{
char header[DTM_MAX_HEADER];
NetPort *myInPort;

#ifdef DEBUG
	printf("NetSendDisconnect(): I've been called\n");
#endif
        SRVsetClass(header);
        SRVsetID(header,userID);
        SRVsetFunction(header,SRV_FUNC_DISCONNECT);
	myInPort = (NetPort *) ListHead(netInList);
	if (myInPort) {
		/**** assumes only one inport */
		SRVsetInPort(header,myInPort->portName);
		}
        return(NetClientSendMessage(netPort,header,0,0,0,cb,0,failCB,0,1));
}

static int NetSendConnect(myInPort,to,cb)
NetPort *myInPort;
NetPort *to;
void 	(*cb)();
{
char header[DTM_MAX_HEADER];

#ifdef DEBUG
	printf("NetSendConnect(): I've been called \n");
#endif
	if (!myInPort)
		return(-1); /* Error, no InPort to send */
	SRVsetClass(header);
	SRVsetID(header,userID);
	SRVsetFunction(header,SRV_FUNC_CONNECT);
	SRVsetInPort(header,myInPort->portName);
	SRVsetVersionString(header,VERSION_STRING);
	SRVsetVersionNumber(header,VERSION_NUMBER);
	return(NetClientSendMessage(to,header,0,0,0,cb,0,0,0,1));
}

static void
DEFUN(NetFreeDataCB,(p, client_data),
/* Free space after it has been sent */
GenericPtr p AND
caddr_t client_data)
{
	FREE(p);
}


int NetSendDoodle(netPort,title,length,width,doodle,color,sendDiscrete,doQueue,
		  distributeInternally,moduleName)
NetPort *netPort;
char *title;    /* title of doodle (name of data set applied to */
long length;
int width;	/* the linewidth of the doodle */
POINT *doodle;
DColor *color;
int sendDiscrete;	/* TRUE -> COL_DOODLE_DISC; FALSE -> COL_DOODLE_CONT */
int doQueue;       /* TRUE -> Save and resend; FALSE -> let client resend */
int distributeInternally; /* boolean */
char *moduleName; /* Send internally to all DOCB except this one */
                  /* in most cases this would be the calling module's name*/
{
#if 0
char header[DTM_MAX_HEADER];
struct COL_TRIPLET *a;
int     status;
register long i;

        COLsetClass(header);
        COLsetTitle(header,title);
        COLsetID(header,UserID);
	if (sendDiscrete) {
	        COLsetFunc(header,"DOODLE",COL_DOODLE_DISC);
		}
	else {
	        COLsetFunc(header,"DOODLE",COL_DOODLE_CONT);
		}
        COLsetWidth(header, width);
        COLsetDimension(header,(length + 1));

        if (!(a = (struct COL_TRIPLET *)
                        MALLOC(sizeof(struct COL_TRIPLET)*(length + 1)))) {
                ErrMesg("Out of Memory.  Can't send doodle\n");
                return(0);
                }

        a[0].x = (float) color->red;
        a[0].y = (float) color->green;
        a[0].z = (float) color->blue;
        a[0].tag = DTM_FLOAT;  /*?*/
        for (i = 1; i < (length + 1); i++ ) {
                a[i].x = (float) doodle[i - 1].x;
                a[i].y = (float) doodle[i - 1].y;
                a[i].z = 0.0;
                a[i].tag = DTM_FLOAT;  /*?*/
                }

	if (distributeInternally) {
		NetCOLDistribute(
			NetMakeCOLFromDoodle(title,a,length+1,sendDiscrete),
			moduleName);
		}
		

	status = NetClientSendMessage(netPort,header,a,(length + 1),
			COL_TRIPLET, NetFreeDataCB, 0,0,0,doQueue);

	if (status == -1)
	{
		NetFreeDataCB(a, (caddr_t)NULL);
	}

       	return(status);
#endif
}

int NetSendPointSelect(netPort,title,func,x,y)
NetPort *netPort;
char *title;
char *func;
int x,y;
{
#if 0
char header[DTM_MAX_HEADER];
int     status;
struct COL_TRIPLET *p;

        COLsetClass(header);
        COLsetTitle(header,title);
        COLsetID(header,UserID);
        COLsetFunc(header,func,COL_POINT);
        COLsetDimension(header,1);
        if (!(p = (struct COL_TRIPLET *)
                        MALLOC(sizeof(struct COL_TRIPLET)))) {
                ErrMesg("Out of Memory.  Can't send point select\n");
                return(0);
                }
        p->x = (float) x;
        p->y = (float) y;
	status = NetClientSendMessage(netPort,header,p,1,COL_TRIPLET,
			NetFreeDataCB,0,NetFreeDataCB,0,1);

	return(status);
#endif
}

int NetSendLineSelect(netPort,title,func,x1,y1,x2,y2)
NetPort *netPort;
char *title;
char *func;
int x1,y1,x2,y2;
{
#if 0
char header[DTM_MAX_HEADER];
int     status;
register long i;
struct COL_TRIPLET *a;


        COLsetClass(header);
        COLsetTitle(header,title);
        COLsetID(header,UserID);
        COLsetFunc(header,"NONE",COL_LINE);
        COLsetDimension(header,2);
        if (!(a = (struct COL_TRIPLET *)
                        MALLOC(sizeof(struct COL_TRIPLET)*2))) {
                ErrMesg("Out of Memory.  Can't send line select\n");
                return(0);
                }
        a[0].x = (float) x1;
        a[0].y = (float) y1;
        a[1].x = (float) x2;
        a[1].y = (float) y2;
        a[0].z = a[1].z = 0.0;
        a[0].tag = a[1].tag = DTM_FLOAT;
	status = NetClientSendMessage(netPort,header,a,2,COL_TRIPLET,
			NetFreeDataCB,0,NetFreeDataCB,0,1);

	return(status);
#endif
}

int NetSendAreaSelect(netPort,title,func,x1,y1,x2,y2)
NetPort *netPort;
char *title;
char *func;
int x1,y1,x2,y2;
{
#if 0
char header[DTM_MAX_HEADER];
int     status;
register long i;
struct COL_TRIPLET *a;


        COLsetClass(header);
        COLsetTitle(header,title);
        COLsetID(header,UserID);
        COLsetFunc(header,"HISTOGRAM",COL_AREA);
        COLsetDimension(header,3);
        if (!(a = (struct COL_TRIPLET *)
                        MALLOC(sizeof(struct COL_TRIPLET)*3))) {
                ErrMesg("Out of Memory.  Can't send area select \n");
                return(0);
                }
        a[0].x = (float) x1;
        a[0].y = (float) y1;
        a[1].x = (float) x2;
        a[1].y = (float) y2;
        a[0].z = a[1].z = a[2].x = a[2].y = a[2].z = 0.0;
        a[0].tag = a[1].tag = a[2].tag = DTM_FLOAT;
	status = NetClientSendMessage(netPort,header,a,3,COL_TRIPLET,
			NetFreeDataCB,0,NetFreeDataCB,0,1);

        return(status);
#endif
}

int NetSendClearDoodle(netPort,title)
NetPort *netPort;
char *title;
{
#if 0
char header[DTM_MAX_HEADER];
int     status;
register long i;

        COLsetClass(header);
        COLsetTitle(header,title);
        COLsetID(header,UserID);
        COLsetFunc(header,"CLEAR",COL_DOODLE_DISC);
        COLsetDimension(header,0);
	status = NetClientSendMessage(netPort,header,0,0,0,0,0,0,0,1);
        return(status);
#endif
}


#if 0			/* not used in Collage */
int NetSendSetDoodle(netPort,title)
NetPort *netPort;
char *title;
{
        char header[DTM_MAX_HEADER];
        struct COL_TRIPLET *a;
        int status;
        register long i;

        COLsetClass(header);
        COLsetTitle(header,title);
        COLsetID(header,userID);
        COLsetFunc(header,"SET",COL_DOODLE_DISC);
        COLsetDimension(header, (long)1);

        if (!(a = (struct COL_TRIPLET *)
                MALLOC(sizeof(struct COL_TRIPLET))))
        {
                ErrMesg("Out of Memory.  Can't send set doodle\n");
                return(0);
        }

        a[0].x = 0.0;
        a[0].y = 0.0;
        a[0].z = 0.0;

        status = NetClientSendMessage(netPort,header,a,(long) 1,COL_TRIPLET,
				NetFreeDataCB,0,NetFreeDataCB,0,1);

        return(status);
}


int NetSendDoodleText(netPort,title, length, doodle, buf)
NetPort *netPort;
char *title;    /* title of doodle (name of data set applied to */
long length;
POINT *doodle;
char *buf;
{
        char header[DTM_MAX_HEADER];
        struct COL_TRIPLET *a;
        int     status;
        register long i;

        COLsetClass(header);
        COLsetTitle(header,title);
        COLsetID(header,userID);
        COLsetFunc(header,"TEXT",COL_DOODLE_DISC);
        COLsetDimension(header,length);

        if (!(a = (struct COL_TRIPLET *)
                        MALLOC(sizeof(struct COL_TRIPLET)*length))) {
                ErrMesg("Out of Memory.  Can't send doodle text\n");
                return(0);
                }

        for (i = 0; i < length; i++ ) {
                a[i].x = (float) doodle[i].x;
                a[i].y = (float) doodle[i].y;
                a[i].z = (float) buf[i];
                a[i].tag = DTM_FLOAT;  /*?*/
                }

        status = NetClientSendMessage(netPort,header,a,length,COL_TRIPLET,
				NetFreeDataCB,0,NetFreeDataCB,0,1);

        return(status);
}
#endif				/* not used in Collage */


int NetSendEraseDoodle(netPort,title,length,doodle,doQueue)
/* return 0 on failure */
NetPort *netPort;
char *title;    /* title of doodle (name of data set applied to */
long length;
POINT *doodle;
int doQueue;       /* TRUE -> Save and resend; FALSE -> let client resend */
{
#if 0
char header[DTM_MAX_HEADER];
struct COL_TRIPLET *a;
int     status;
register long i;

        COLsetClass(header);
        COLsetTitle(header,title);
        COLsetID(header,UserID);
        COLsetFunc(header,"ERASE",COL_DOODLE_DISC);
        COLsetDimension(header,length);

        if (!(a = (struct COL_TRIPLET *)
                        MALLOC(sizeof(struct COL_TRIPLET)*length))) {
                ErrMesg("Out of Memory.  Can't send erase doodle\n");
                return(0);
                }

        for (i = 0; i < length; i++ ) {
                a[i].x = (float) doodle[i].x;
                a[i].y = (float) doodle[i].y;
                a[i].z = 0.0;
                a[i].tag = DTM_FLOAT;  /*?*/
                }

        status = NetClientSendMessage(netPort,header,a,length,COL_TRIPLET,
				NetFreeDataCB,0,0,0,doQueue);

	if (status == -1)
	{
		NetFreeDataCB(a);
	}

        return(status);
#endif
}

int NetSendEraseBlockDoodle(netPort,title,length,doodle)
/* return 0 on failure */
NetPort *netPort;
char *title;    /* title of doodle (name of data set applied to */
long length;
POINT *doodle;
{
#if 0
char header[DTM_MAX_HEADER];
struct COL_TRIPLET *a;
int     status;
register long i;

        COLsetClass(header);
        COLsetTitle(header,title);
        COLsetID(header,UserID);
        COLsetFunc(header,"BERASE",COL_DOODLE_DISC);
        COLsetDimension(header,length);

        if (!(a = (struct COL_TRIPLET *)
                        MALLOC(sizeof(struct COL_TRIPLET)*length))) {
                ErrMesg("Out of Memory.  Can't send erase doodle\n");
                return(0);
                }

        for (i = 0; i < length; i++ ) {
                a[i].x = (float) doodle[i].x;
                a[i].y = (float) doodle[i].y;
                a[i].z = 0.0;
                a[i].tag = DTM_FLOAT;  /*?*/
                }

	status = NetClientSendMessage(netPort,header,a,length,COL_TRIPLET,
			NetFreeDataCB,0,NetFreeDataCB,0,1);

        return(status);
#endif
}


int NetSendTextSelection(netPort,title,left,right)
NetPort *netPort;
char *title;
int left,right;
{
#if 0
char header[DTM_MAX_HEADER];
int status;

        TXTsetClass(header);
        TXTsetTitle(header,title);
        TXTsetID(header,userID);
        TXTsetSelectionLeft(header,left);
        TXTsetSelectionRight(header,right);
	status = NetClientSendMessage(netPort,header,0,0,0,0,0,0,0,1);
	return(status);
#endif
}

int NetSendText(netPort,t,distributeInternally,moduleName)
NetPort *netPort;
Text *t;
int distributeInternally;
char *moduleName;
{
char header[DTM_MAX_HEADER];
int status;
char *buff;

	t->id = userID;
	if (distributeInternally) {
		NetTextDistribute(t,moduleName);
		}
        TXTsetClass(header);
        TXTsetTitle(header,t->title);
        TXTsetID(header,userID);
	if (t->selLeft || t->selRight) {
	        TXTsetSelectionLeft(header,t->selLeft);
		TXTsetSelectionRight(header,t->selRight);
		}
	TXTsetInsertionPt(header,t->insertPt);
	TXTsetNumReplace(header,t->numReplace);
	if (t->replaceAll)
		TXTsetReplaceAll(header);
	TXTsetDimension(header,t->dim);
#ifdef DEBUG
	printf("NetSendText(): sending textString \"%s\" dim = %d\n",
				t->textString,t->dim);
#endif
	if (!(buff = (char *) MALLOC(t->dim +1))) {
		ErrMesg("Out of Memory sending text\n");
		return(-1);
		}
	strncpy(buff,t->textString,t->dim);
	buff[t->dim] = '\0';
	status = NetClientSendMessage(netPort,header,buff,t->dim,
				DTM_CHAR,NetFreeDataCB,0,NetFreeDataCB,0,1);
	return(status);
}


int NetSendPalette8(netPort,title,rgb,associated,distributeInternally,
		    moduleName)
NetPort *netPort;
char *title;
unsigned char *rgb;
char *associated;
int	distributeInternally; /* boolean */
char *moduleName; /* Send internally to all DOCB except this one */
		  /* in most cases this would be the calling module's name*/
{
unsigned char *p;
char header[DTM_MAX_HEADER];
register unsigned char *t;
register int x;
int status;

	if (distributeInternally)
		NetPALDistribute(title,rgb,associated,moduleName);

	if (!(p = (unsigned char *) MALLOC(768))) {
		ErrMesg("Out of Memory trying to send Palette\n");
		return(-1);
		}
	t = p;
	for (x = 0; x < 768; x++)
		*t++ = *rgb++;

        PALsetClass(header);
        PALsetTitle(header,title);
	COLsetID(header,userID);
	PALsetSize(header,768/3);
	if (associated)
		COLsetAssoc(header, associated);
	status = NetClientSendMessage(netPort,header,p,768,DTM_CHAR,
			NetFreeDataCB,0,NetFreeDataCB,0,1);

	return(status);
}


int NetSendArray(netPort,d,shouldCopy,distributeInternally,moduleName,
		 isAnimation)
NetPort *netPort;
Data	*d;
int	shouldCopy;
int	distributeInternally;
char	*moduleName;
char	isAnimation;
{
char header[DTM_MAX_HEADER];
DTMTYPE dtmType;
long size,buffSize;
char *buff;
register int x;
register char *pbuff;
register char *pdata;
int status;
int	elementSize;


	SDSsetClass(header);
        SDSsetTitle(header,d->label);
	ANIMsetID(header,userID);
        if (d->expandX != 0. && d->expandY != 0.)
		ANIMsetExpansion(header,d->expandX, d->expandY);
        switch (d->dost) {
                case DOST_Float:
                        SDSsetType(header,DTM_FLOAT);
                        dtmType = DTM_FLOAT;
			elementSize = sizeof(float);
                        break;
                case DOST_Char:
                        SDSsetType(header,DTM_CHAR);
                        dtmType = DTM_CHAR;
			elementSize = sizeof(char);
                        break;
                case DOST_Int16:
                        SDSsetType(header,DTM_SHORT);
                        dtmType = DTM_SHORT;
			elementSize = sizeof(short);
                        break;
                case DOST_Int32:
                        SDSsetType(header,DTM_INT);
                        dtmType = DTM_INT;
			elementSize = sizeof(int);
                        break;
                case DOST_Double:
                        SDSsetType(header,DTM_DOUBLE);
                        dtmType = DTM_DOUBLE;
			elementSize = sizeof(double);
                        break;
                }
	if (d->dost == DOST_Float || d->dost == DOST_Double)
	  {
		if (d->min.f != d->max.f)
		  SDSsetMinMax(header, d->min.f, d->max.f);
	  }
	else
	  {
		if (d->min.i != d->max.i)
		  SDSsetMinMax(header, (float)d->min.i, (float)d->max.i);
	  }
	SDSsetDimensions(header,d->rank,d->dim);

	if (isAnimation) {
		ANIMmarkAnimation(header);
		}
#if 0
	else {
		if (d->view_type)
			COLsetView(header, d->view_type);
		}
#endif

#if 0
	if (d->associated)
		COLsetAssoc(header, d->associated);
#endif

        for (x=1,size = d->dim[0]; x < d->rank; x++)
                size *= d->dim[x];

	buffSize = size * elementSize;

	if (distributeInternally) {
		if (isAnimation)
			NetAnimationDistribute(&d,moduleName);
		else
			NetArrayDistribute(&d,moduleName);
		}

	if (shouldCopy) {
		if (!(buff = (char *) MALLOC(buffSize))) {
			ErrMesg("Couldn't allocate memory to send image\n");
			return(-1);
			}
		for (x = 0, pbuff = buff, pdata = d->data; x < buffSize; x++)
			*pbuff++ = *pdata++;
#ifdef DEBUG
	if (dtmType == DTM_FLOAT) {
		printf("\n\n\n\n###########\nThe first number is %f\n\n\n",
				*((float*) buff));
		}
#endif
		status = NetClientSendMessage(netPort,header,
				buff,size,dtmType, NetFreeDataCB,0,
					      NetFreeDataCB,0,1);
		}
	else {
		status = NetClientSendMessage(netPort,header,
				d->data,size,dtmType, 0,0,0,0,1);
#ifdef DEBUG
	if (dtmType == DTM_FLOAT) {
		printf("\n\n\n\n###########\nThe first number is %f\n\n\n",
				*((float*) buff));
		}
#endif
		}

	return(status);

} /* NetSendArray()*/


static int
DEFUN(_NetSendRaster8,(netPort,d,shouldCopy,
		       distributeInternally,moduleName,isAnimation),
      NetPort *netPort AND
      Data    *d AND
      int     shouldCopy AND
      int     distributeInternally AND
      char    *moduleName AND
      char    isAnimation)
{
	char header[DTM_MAX_HEADER];
	char *buff;
	int status;

	RISsetClass(header);
	RISsetTitle(header, d->label);
	RISsetDimensions(header, d->dim[0], d->dim[1]);
	COLsetID(header, userID);

        if (d->expandX != 0. && d->expandY != 0.)
		ANIMsetExpansion(header,d->expandX, d->expandY);
#if 0
	if (d->view_type)
		COLsetView(header, d->view_type);
#endif
#if 0
	if (d->associated)
		COLsetAssoc(header, d->associated);
#endif
	if (d->min.f != d->max.f)
		SDSsetMinMax(header, d->min.f, d->max.f);

	if (isAnimation)
		ANIMmarkAnimation(header);

	if (distributeInternally)
		if (isAnimation)
			NetAnimationDistribute(&d,moduleName);
		else
			NetRISDistribute(&d, moduleName);

	
	if (shouldCopy) {
		int buffSize = d->dim[0] * d->dim[1] * sizeof(char);
		register int x;
		register char *pbuff, *pdata;

		if (!(buff = (char *) MALLOC(buffSize))) {
			ErrMesg("Couldn't allocate memory to send image\n");
			return(-1);
			}
		for (x = 0, pbuff = buff, pdata = d->data; x < buffSize; x++)
			*pbuff++ = *pdata++;
		status = NetClientSendMessage(netPort,header,
				buff,buffSize,DTM_CHAR,NetFreeDataCB,0,
					      NetFreeDataCB,0,1);
		}
	else
		status = NetClientSendMessage(netPort,header,
				d->data,d->dim[0]*d->dim[1],DTM_CHAR,
					      0,0,0,0,1);

	return(status);
}

int NetSendAnimation(netPort,d,shouldCopy,distributeInternally,moduleName)
NetPort *netPort;
Data    *d;
int     shouldCopy;
int     distributeInternally;
char    *moduleName;
{
	int status;
	char *dName = d->label;
	Data *next = d->group;

	if (d->dot == DOT_Array)
		status = NetSendArray(netPort,d,shouldCopy,
				      distributeInternally,moduleName,
				      TRUE);
	else
		status = _NetSendRaster8(netPort,d,shouldCopy,
					 distributeInternally,moduleName,
					 TRUE);

	if (next && next->dot == DOT_Palette8)
		NetSendPalette8(netPort,next->label,next->data,dName,
				distributeInternally,moduleName);

	return(status);
}

int NetSendAnimationCommand(netPort,title,command,runType,frameNumber)
NetPort *netPort;
char *title;
AnimFunc command;
AnimRunType runType;
int frameNumber;
{
#if 0
char header[DTM_MAX_HEADER];
int status;

	ANIMsetClass(header);
	ANIMsetTitle(header,title);
	ANIMsetID(header,userID);
	
	switch (command) {
	    case AF_STOP:
			ANIMsetFunc(header,ANIM_FUNC_STOP);
			break;
	    case AF_FPLAY:
			ANIMsetFunc(header,ANIM_FUNC_FPLAY);
			break;
	    case AF_RPLAY:
			ANIMsetFunc(header,ANIM_FUNC_RPLAY);
			break;
	    case AF_NO_FUNC:
	    default:
			break;
	    };
	switch (runType) {
	    case ART_SINGLE:
			ANIMsetRunType(header,ANIM_RUN_TYPE_SINGLE);
			break;
	    case ART_CONT:
			ANIMsetRunType(header,ANIM_RUN_TYPE_CONT);
			break;
	    case ART_BOUNCE:
			ANIMsetRunType(header,ANIM_RUN_TYPE_BOUNCE);
			break;
	    case ART_NONE:
	    default:
			break;
	    };

	ANIMsetFrame(header,frameNumber);
	status = NetClientSendMessage(netPort,header,0,0,0,0,0,0,0,1);
	return(status);
#endif
}


int NetSendRaster8(netPort,d,shouldCopy,
		   distributeInternally,moduleName)
NetPort *netPort;
Data    *d;
int shouldCopy;     /* should this data be copied before returning? */
		    /* if not, charData and palette8 must not be freed or */
		    /* changed until data is sent */ 
int distributeInternally; /* boolean */
char *moduleName; /* Send internally to all DOCB except this one */
                  /* in most cases this would be the calling module's name*/
{
	return _NetSendRaster8(netPort,d,shouldCopy,
			       distributeInternally,moduleName,
			       FALSE);
}


int NetSendRaster8Group(netPort,title,charData,xdim,ydim,palette8,shouldCopy,
			distributeInternally,moduleName)
NetPort *netPort;
char *title;
unsigned char *charData;
int xdim;
int ydim;
unsigned char *palette8;
int 	shouldCopy; /* should this data be copied before returning? */
		    /* if not, charData and palette8 must not be freed or */
		    /* changed until data is sent */
int distributeInternally; /* boolean */
char *moduleName; /* Send internally to all DOCB except this one */
                  /* in most cases this would be the calling module's name*/
{
int palStatus;
int rasStatus;
Data *d;

	d = DataNew();
	d->label = title;
	d->dot = DOT_Image;
	d->dost = DOST_Char;
	d->dim[0] = xdim;
	d->dim[1] = ydim;
	d->rank = 2;
	d->data = (char *) charData;
	rasStatus = NetSendRaster8(netPort,d,shouldCopy,
				distributeInternally,moduleName);
	palStatus = NetSendPalette8(netPort,title,palette8,(char *)NULL,
				distributeInternally,moduleName);

	if ((rasStatus == -1) || (palStatus == -1)) {
		return(-1); /* error */
		}
	if ((!rasStatus) || (!palStatus)) {
		return(0); /* delayed send */
		}
	return(1); /* no prob */


}

int NetSendVData(netPort,label,magicPath,pathLength,nodeID,nodeName,field,
			numRecords, numElements,type,vdata,
			shouldCopy,distributeInternally,moduleName)
/* returns 1 on success, 0 on delayed send, -1 on error */
char	*label;
NetPort *netPort;
VdataPathElement **magicPath;
int	pathLength;
int	nodeID;
char	*nodeName;
char	*field;
int	numRecords;
int	numElements;
int	type;	
char	*vdata;
int	shouldCopy; /* copy Vdata before returning in case of delayed send */
int	distributeInternally; 
char	*moduleName; /* distribute Internally to all except */
{
#if 0
char header[DTM_MAX_HEADER];
int elementSize;
char *copyData;
DTMTYPE dtmType;
int status;
int size;

	VDATAsetClass(header);
	VDATAsetTitle(header,label);
	VDATAsetID(header,userID);
	VDATAsetPath(header,magicPath,pathLength);
	VDATAsetNodeID(header,nodeID);
	VDATAsetNodeName(header,nodeName);
	VDATAsetField(header,field);
	VDATAsetNumRecords(header,numRecords);
	VDATAsetNumElements(header,numElements);
        switch (type) {
                case DOST_Float:
                        VDATAsetType(header,DTM_FLOAT);
                        dtmType = DTM_FLOAT;
			elementSize = sizeof(float);
                        break;
                case DOST_Char:
                        VDATAsetType(header,DTM_CHAR);
                        dtmType = DTM_CHAR;
			elementSize = sizeof(char);
                        break;
                case DOST_Int16:
                        VDATAsetType(header,DTM_SHORT);
                        dtmType = DTM_SHORT;
			elementSize = sizeof(short);
                        break;
                case DOST_Int32:
                        VDATAsetType(header,DTM_INT);
                        dtmType = DTM_INT;
			elementSize = sizeof(int);
                        break;
                case DOST_Double:
                        VDATAsetType(header,DTM_DOUBLE);
                        dtmType = DTM_DOUBLE;
			elementSize = sizeof(double);
                        break;
		default:
			ErrMesg("BAD TYPE in SendVDATA\n");
			break;
                };

	if (shouldCopy)  {
		size = numRecords * numElements * elementSize;
		if (!(copyData = (char *) MALLOC(size))) {
			ErrMesg("Out of Memory\n");
			return(-1);
			}
		memcpy(copyData,vdata,size);
		status = NetClientSendMessage(netPort,header,copyData,
					numRecords * numElements,
					dtmType,NetFreeDataCB,0,0,0,1);
		}
	else {
		status = NetClientSendMessage(netPort,header,vdata,
					numRecords * numElements,
					dtmType,0,0,0,0,1);
		}
	
	return(status);
#endif
}


int NetSendDataObject(netPort,d,shouldCopy,distributeInternally,moduleName)
NetPort *netPort;
Data *d;
int shouldCopy;
int distributeInternally; /* boolean */
char *moduleName; /* Send internally to all DOCB except this one */
                  /* in most cases this would be the calling module's name*/
{
int status;
static Text t;
Data *nextD;

    while (d) {
	nextD = d->group;
	switch(d->dot) {
		case DOT_Palette8:
			status = NetSendPalette8(netPort,d->label,d->data,
						 d->associated,
					distributeInternally,moduleName);
			break;
		case DOT_Array:
			if (d->rank == 1) {
				d->rank = 2;
				d->dim[1] = 1;
				}
			if (d->rank > 3) {
				WarningMesg(
			"Collage doesn't support greater than 3D data\n");
					/* should free data here?*/
				}
			status = NetSendArray(netPort,d,
					      shouldCopy,
					      distributeInternally,
					      moduleName,FALSE);
			break;
		case DOT_Image:
			if ( (nextD) 
			    && (nextD->dot == DOT_Palette8)
			    && !nextD->associated )
				nextD->associated = d->label;
			status = NetSendRaster8(netPort,d,
						shouldCopy,
						distributeInternally,
						moduleName);
			break;
		case DOT_Text:
			t.title = d->label;
			t.id = userID;
			t.selLeft = 0;
			t.selRight= 0;
			t.numReplace = 0;
			t.replaceAll = TRUE;
			t.dim = d->dim[0];
			t.textString = d->data;
			status = NetSendText(netPort,&t,distributeInternally,
				moduleName);
			break;
		case DOT_VData:
			status = NetSendVData(netPort,d->label,d->magicPath,
					d->pathLength,d->nodeID,d->nodeName,
					d->fields, d->dim[0],d->dim[1],
					d->dost, d->data,
					shouldCopy,distributeInternally,
					moduleName);
			break;

		default:
#ifdef DEBUG
		printf("*******NetSendDataObject() doesn't know this type\n");
#endif
			status = -1;
			break;
		
		};
	    d = nextD;
	    }

	return(status);
}

int NetSendCommand(netPort,domain,message,cb,failCB)
NetPort *netPort;
char *domain;
char *message;
void 	(*cb)();	/* callback when sent */
void 	(*failCB)();	/* callback if send failed */
{
char header[DTM_MAX_HEADER];

	COMsetClass(header);
	COMsetID(header,userID);
	COMsetDomain(header,domain);
	COMsetMesg(header,message);
	
	return(NetClientSendMessage(netPort,header,0,0,0,cb,0,failCB,0,1));
	
}

#if 0				/* not used */
int NetSendExecuteProc(netPort,retAddress,argc,argv,cb,cbData,failCB,failCBData)
NetPort netPort;
char *retAddress;
int argc;
char **argv;
void (*cb)();
caddr_t cbData;
void (*failCB)();
caddr_t failCBData;
{

}

static NetExecCB(data,client_data)
caddr_t data;
caddr_t client_data;
{
ExecCBData *ecbd;

	ecbd = (ExecCBData *) client_data;

	NetDestroyPort((NetPort *) ecbd->internal);
	if (ecbd->cb) {
		ecbd->cb(data,ecbd->cbData);
		}
	FREE(ecbd);
}

static NetExecFailCB(data,client_data)
caddr_t data;
caddr_t client_data;
{
ExecCBData *ecbd;

	ecbd = (ExecCBData *) client_data;
	NetDestroyPort((NetPort *) ecbd->internal);

	if (ecbd->failCB) {
		ecbd->failCB(data,ecbd->failCBData);
		}
	FREE(ecbd);
}

int NetSendHostStatusRequest(outPortAddr,retAddress,cb,cbData,failCB,failCBData)
char *outPortAddr;
char *retAddress;
void (*cb)();
caddr_t cbData;
void (*failCB)();
caddr_t failCBData;
{
char header[DTM_MAX_HEADER];
NetPort *netPort;
NetPort *inPort;
ExecCBData *ecbd;
time_t	now;

	if (!(netPort = NetInternalCreateOutPort(outPortAddr,FALSE))) {
		return(-1);
		}
	if (!( ecbd = (ExecCBData *) MALLOC(sizeof(ExecCBData)))) {
		ErrMesg("Out of Memory\n");
		return(-1);
		}
	ecbd->internal = (caddr_t) netPort;
	ecbd->cb = cb;
	ecbd->cbData = cbData;
	ecbd->failCB = failCB;
	ecbd->failCBData = failCBData;
	

	EXECsetClass(header);
	EXECsetID(header,userID);

	if (retAddress) {
		EXECsetAddress(header,retAddress);
		}
	else {
		/* if retAddress == 0, use first in port */
		if (!(inPort = (NetPort *) ListHead(netInList))) {
			/* no in port created */
			return(-1);
			}
		EXECsetAddress(header,inPort->portName);
		}
	now = time(0);
	EXECsetTimeStamp(header,ctime(&now));
	EXECsetType(header,EXEC_HOST_STATUS_QUERY);
	return(NetClientSendMessage(netPort,header,0,0,0,NetExecCB,ecbd,
						NetExecFailCB,ecbd,1));
}


int NetSendHostStatus(outPortAddr,retAddress,timeStamp,load1,load5,load15,
			numUsers,cb,cbData,failCB,failCBData)
char *outPortAddr;
char *retAddress;
char *timeStamp;
float load1,load5,load15;
int numUsers;
void (*cb)();
caddr_t cbData;
void (*failCB)();
caddr_t failCBData;
{
char header[DTM_MAX_HEADER];
NetPort *netPort;
NetPort *inPort;
ExecCBData *ecbd;

	if (!(netPort = NetInternalCreateOutPort(outPortAddr,FALSE))) {
		return(-1);
		}
	if (!( ecbd = (ExecCBData *) MALLOC(sizeof(ExecCBData)))) {
		ErrMesg("Out of Memory\n");
		return(-1);
		}
	ecbd->internal = (caddr_t) netPort;
	ecbd->cb = cb;
	ecbd->cbData = cbData;
	ecbd->failCB = failCB;
	ecbd->failCBData = failCBData;

	EXECsetClass(header);
	EXECsetID(header,userID);

	if (retAddress) {
		EXECsetAddress(header,retAddress);
		}
	else {
		/* if retAddress == 0, use first in port */
		if (!(inPort = (NetPort *) ListHead(netInList))) {
			/* no in port created */
			return(-1);
			}
		EXECsetAddress(header,inPort->portName);
		}

	EXECsetTimeStamp(header,timeStamp);
	EXECsetType(header,EXEC_HOST_STATUS_RETURN);
	EXECsetLoad1(header,load1);
	EXECsetLoad5(header,load5);
	EXECsetLoad15(header,load15);
	EXECsetNumUsers(header,numUsers);
	return(NetClientSendMessage(netPort,header,0,0,0,NetExecCB,ecbd,
					NetExecFailCB,ecbd,1));
}
#endif				/* not used */




int NetSendMessage(netPort,message,cb,cbData,failCB,failCBData)
NetPort *netPort;
char *message;
void (*cb)();
caddr_t cbData;
void (*failCB)();
caddr_t failCBData;
{
char header[DTM_MAX_HEADER];
char tmp[DTM_STRING_SIZE];
	
	MSGsetClass(header);
	COLsetID(header, userID);
	strncpy(tmp, message, DTM_STRING_SIZE - 1);
	tmp[DTM_STRING_SIZE - 1] = 0;
	MSGsetString(header,tmp);
	return(NetClientSendMessage(netPort,header,0,0,0,cb,cbData,
					failCB,failCBData,1));
}

