/*ScianSockets.c
  Eric Pepke
  April 25, 1990
  Stuff for socket interface in Scian

  Taken over by John R. Murray
  June 29, 1992
*/

#include "Scian.h"
#include "ScianTypes.h"
#include "ScianSockets.h"
#include "ScianArrays.h"
#include "ScianLists.h"
#include "ScianControls.h"
#include "ScianButtons.h"
#include "ScianNetDaemon.h"
#include "ScianWindows.h"
#include "ScianObjWindows.h"
#include "ScianVisWindows.h"
#include "ScianTextBoxes.h"
#include "ScianDialogs.h"
#include "ScianIDs.h"
#include "ScianScripts.h"
#include "ScianErrors.h"
#include "ScianColors.h"
#include "ScianDialogs.h"
#include "ScianEvents.h"
#include "ScianTimers.h"
#include "ScianStyle.h"
#include "ScianDatasets.h"
#include "ScianDraw.h"
#include "ScianIcons.h"
#include "ScianObjFunctions.h"
#include "ScianGarbageMan.h"
#include "ScianNetObjects.h"
#include "ScianHelp.h"

/* temporary? */
#include <errno.h>

/* NEWSTYLEUPDATES enables auto-transmission of netids and/or objects after an UPDATEVAR */
#define NEWSTYLEUPDATES
/* HAK */

/* #define MESGSTATS */
/*Line for mkmk*/
/*$cc -I/usr/include/bsd ScianSockets.c -c $(CFLAGS)
*/

/*Public objects*/
ObjPtr advertiseableClass = NULLOBJ;
ObjPtr allAdvertised = NULLOBJ;	/*All objects advertised from
					  this process (list)*/

/* this should probably move to Scian.h and ScianMain.c */
int libRunning = 0;

#ifdef SOCKETS

#define SHORT_TIMEOUT	5.0		/*Short timeout on socket*/
#define LONG_TIMEOUT	10.0		/*Long timeout*/	

/*Kinds of connection*/
#define CK_REMOTE_DAEMON	1	/*Connection to a remote network daemon*/
#define CK_LOCAL_DAEMON		2	/*Connection to a local daemon*/
#define CK_LISTENING_SCIAN	3	/*Listening connection on a local SciAn*/
#define CK_REMOTE_SCIAN		4	/*Connection to a remote SciAn (not implemented)*/
#define CK_LOCAL_SCIAN		5	/*Connection from a local SciAn (not implemented)*/

/*Values for connection state*/
#define CS_NAME_ONLY		0	/*Just have the name of target, nothing else*/
#define CS_WAIT_FOR_VERSION	1	/*Waiting for a version reply*/
#define CS_MORIBUND		2	/*Moribund connection, ready to die*/
#define CS_LIMBO		3	/*Connection in limbo, doing nothing*/
#define CS_IDLING		4	/*Idling a connection.*/
#define CS_FILL_CMD		5	/*Fill a command*/
#define CS_WAIT_FOR_CMD_END	6	/*Wait for the end of a command*/
#define CS_READ_PROC_NAME	7	/*Read a process name*/
#define CS_NOT_LISTENING	8	/*Accepting socket, not listening yet*/
#define CS_LISTENING		9	/*Accepting socket, listening*/
#define CS_WAIT_FOR_PASSWORD	10	/*Of a recieving socket, wait for a password*/
#define CS_READ_OBJECT		11	/*Read an Object */
#define CS_READ_NUMBER_ONE	12	/*Read a number*/
#define CS_READ_NUMBER_TWO	13	/*Read the second number*/
#define CS_READ_NUMBER_THREE	14	/*Read the third number*/
#define CS_READ_NUMBER_FOUR	15	/*Read the fourth number*/

/*Local variables*/
static char *stateName[] = 
	{
	    "name only",
	    "waiting for version",
	    "moribund",
	    "limbo",
	    "idle connection",
	    "fill cmd",
	    "wait for cmd end",
	    "read proc name",
	    "not listening",
	    "listening",
	    "wait for password",
	    "read object",
	    "read number one",
	    "read number two",
	    "read number three",
	    "read number four"
	};
static char *lastComputer = 0;		/*Address of last computer to be dialed*/
static int lastSocketTried = ND_BASE_SOCKET_NUMBER;
					/*Last process # tried*/
static ObjPtr connectionClass;		/*Class of connections*/
static ObjPtr processClass;		/*Class of processes*/
static ObjPtr allConnections;		/*List of all the connections*/
static ObjPtr processIcon;			/*Icon that represents the process*/
static ObjPtr publicationConnection;	/*Connection for publications*/

/*Local prototypes*/
#ifdef PROTO
static ObjPtr NewConnection(char *, int);
static void SetConnectionState(ObjPtr connection, int state);
static int GetConnectionState(ObjPtr connection);
static void DeleteProcess(ObjPtr connection, int procNum);
static ObjPtr NewProcess(ObjPtr connection, char *procName, int procSocket);
static int SocketPort(void);
#else
static ObjPtr NewConnection();
static void SetConnectionState();
static int GetConnectionState();
static void DeleteProcess();
static ObjPtr NewProcess();
static int SocketPort();
#endif

/*Local methods*/
static ObjPtr ConnectToComputerAgain();
static ObjPtr ContinueWaiting();
static ObjPtr ShowConnectionControls();
static ObjPtr ConnectToProcess();

int writen(fd, cptr, nbytes)
int fd;
char *cptr;
long nbytes;
{
    int nleft, nwritten;

    nleft = nbytes;
    while (nleft > 0)
    {
	nwritten = write(fd, cptr, nleft);
	if (nwritten < 0)
	{
	    if (errno != EWOULDBLOCK && errno != EAGAIN)
	    {
		perror("writen");
		return;
	    }
	}
	else
	{
	    nleft -= nwritten;
	    cptr += nwritten;
	}
    }
}

int readn(sock, cptr, num)
int sock;
char *cptr;
long num;
{
    double lastData;
    long oldnum;
    int olderrno = 0;
    int notTimeOut = 1;

    oldnum = num;
    lastData = WallClock();
    while(num > 0 && (notTimeOut = lastData + JOHNSTIMEOUT > WallClock()))
    {
	long tmp;

	tmp = read(sock, cptr, num);
	if (tmp > 0)
	{
	    num -= tmp;
	    cptr += tmp;
	    lastData = WallClock();
	}
	if (tmp < 0 && olderrno != errno)
	{
	    if (errno != EWOULDBLOCK && errno != EAGAIN)
	    {
		perror("readn");
	    }
	    olderrno = errno;
	}
    }
    if (!notTimeOut)
	ReportError("readn", "timeout");

    return 0;
}

#ifdef PROTO
static int SocketPort(void)
#else
static int SocketPort()
#endif
/*Return a socket port which may or may not work*/
{
    return lastSocketTried++;
}

static ObjPtr ConnectToComputerAgain(dummy, whichButton)
ObjPtr dummy;
int whichButton;
/*Asks for a connect again depending on whichButton*/
{
    if (whichButton == 1) DoConnectToComputer();
    return ObjTrue;
}

static ObjPtr ContinueWaiting(connection, whichButton)
ObjPtr connection;
int whichButton;
/*Has a connection continue waiting or not*/
{
    if (whichButton == 1)
    {
	/*Continue trying*/

	/*Set the state*/
	SetVar(connection, CONNECTIONSTATE, 
		GetVar(connection, OLDCONNSTATE));

	/*Set a timeout*/
	SetTimeout(connection, TIMEOUT, WallClock() + LONG_TIMEOUT);
    }
    else
    {
	/*Set the state*/
	SetConnectionState(connection, CS_MORIBUND);
    }
    return ObjTrue;
}

#ifdef PROTO
static void SetConnectionState(ObjPtr connection, int state)
#else
static void SetConnectionState(connection, state)
ObjPtr connection;
int state;
#endif
/*Changes the state of connection to connection*/
{
    ObjPtr oldState;

    /*Get the old state*/
    oldState = GetVar(connection, CONNECTIONSTATE);

    /*If they're not the same, save the old one*/
    if (!oldState || (GetInt(oldState) != state))
    {
	SetVar(connection, OLDCONNSTATE, oldState);
#ifdef DEBUG
	printf("Scian (%ld) connection %ld transition to %s\n",
	    getpid(), connection, stateName[state]);
#endif
    }

    /*Only some states have timeouts, disable them now*/
    DoNotDisturb(connection, TIMEOUT);

    SetVar(connection, CONNECTIONSTATE, NewInt(state));

    /*Determine actions on the transition, such as doing a timeout*/
    switch (state)
    {
	case CS_NAME_ONLY:
	    /*Set a timeout*/
	    SetTimeout(connection, TIMEOUT, WallClock() + SHORT_TIMEOUT);
	    break;
	case CS_READ_NUMBER_ONE:
	    /*Must null the socket num first*/
	    SetVar(connection, READNUMBERONE, NULLOBJ);
	    break;
	case CS_READ_NUMBER_TWO:
	    /*Must null the socket num first*/
	    SetVar(connection, READNUMBERTWO, NULLOBJ);
	    break;
	case CS_READ_NUMBER_THREE:
	    /*Must null the socket num first*/
	    SetVar(connection, READNUMBERTHREE, NULLOBJ);
	    break;
	case CS_READ_NUMBER_FOUR:
	    /*Must null the socket num first*/
	    SetVar(connection, READNUMBERFOUR, NULLOBJ);
	    break;
	case CS_READ_PROC_NAME:
	    /*Must null the proc name first*/
	    SetVar(connection, CMDPROCNAME, NULLOBJ);
	    break;
	case CS_IDLING:
	    /*Null the command name*/
	    SetVar(connection, CURCMD, NULLOBJ);
	    break;
	case CS_WAIT_FOR_VERSION:
	    /*Null the command name*/
	    SetVar(connection, CURCMD, NULLOBJ);
	    SetTimeout(connection, TIMEOUT, WallClock() + SHORT_TIMEOUT);
	    break;
	case CS_WAIT_FOR_PASSWORD:
	    /*Null the command name*/
	    SetTimeout(connection, TIMEOUT, WallClock() + SHORT_TIMEOUT);
	    SetVar(connection, CURCMD, NULLOBJ);
	    break;
	default:
	    break;
    }
}

#ifdef PROTO
static int GetConnectionState(ObjPtr connection)
#else
static int GetConnectionState(connection)
ObjPtr connection;
#endif
/*Returns the state of the connection*/
{
    int state;
    ObjPtr var;
    var = GetIntVar("GetConnectionState", connection, CONNECTIONSTATE);
    if (var)
    {
	state = GetInt(var);
    }
    else
    {
	state = CS_LIMBO;
    }
    return state;
}

static ObjPtr TimeoutConnection(connection, elapsedTime)
ObjPtr connection;
double elapsedTime;
/*Connection timeout, performing an action based on the state*/
{
#ifdef INTERACTIVE
    WinInfoPtr alertWindow;
    int state;
    ObjPtr var;
    char alertMessage[400];

    var = GetIntVar("TimeoutConnection", connection, CONNECTIONSTATE);
    state = GetInt(var);

    switch (state)
    {
	case CS_NAME_ONLY:
	    var = GetIntVar("TimeoutConnection", connection, CONNECTIONKIND);
	    sprintf(alertMessage, "The SciAn %s did not respond during the time out period.  You can cancel \
the connection or keep waiting.", GetInt(var) == CK_REMOTE_SCIAN ? "process" : "daemon");
	    alertWindow = AlertUser(UISEVEREALERT, (WinInfoPtr) connection, 
		alertMessage, ContinueWaiting, 
		2, "Cancel", "Keep Waiting");
		    if (alertWindow) SetVar((ObjPtr) alertWindow, HELPSTRING,
    NewString("Something responded to the socket connection on the remote host, \
but it did not identify itself as a SciAn process within the timeout period.  It \
is possible but unlikely that some other process is running in the daemon's place \
It is more likely that the process is just busy or slow, or there is a temporary \
slowdown in the network."));
	    SetConnectionState(connection, CS_LIMBO);
	    break;
	case CS_WAIT_FOR_VERSION:
	    var = GetIntVar("TimeoutConnection", connection, CONNECTIONKIND);
	    sprintf(alertMessage, "The SciAn %s did not respond during the time out period.  You can cancel \
the connection or keep waiting.", GetInt(var) == CK_REMOTE_SCIAN ? "process" : "daemon");
	    alertWindow = AlertUser(UISEVEREALERT, (WinInfoPtr) connection, 
		alertMessage, ContinueWaiting, 
		2, "Cancel", "Keep Waiting");
		    if (alertWindow) SetVar((ObjPtr) alertWindow, HELPSTRING,
    NewString("Something responded to the socket connection on the remote host, \
but it did not identify itself as a SciAn process within the timeout period.  It \
is possible but unlikely that some other process is running in the daemon's place \
It is more likely that the process is just busy or slow, or there is a temporary \
slowdown in the network."));
	    SetConnectionState(connection, CS_LIMBO);
	    break;
	case CS_WAIT_FOR_PASSWORD:
	    sprintf(alertMessage, "An incoming connection has not validated itself as a SciAn process during the time out period.  You can cancel \
the connection or keep waiting.");
	    alertWindow = AlertUser(UISEVEREALERT, (WinInfoPtr) connection, 
		alertMessage, ContinueWaiting, 
		2, "Cancel", "Keep Waiting");
		    if (alertWindow) SetVar((ObjPtr) alertWindow, HELPSTRING,
    NewString("Something attempted to connect to this process, but, \
but it did not identify itself as a SciAn process within the timeout period.  It \
is possible but unlikely that some other process is running in the daemon's place \
It is more likely that the process is just busy or slow, or there is a temporary \
slowdown in the network."));
	    SetConnectionState(connection, CS_LIMBO);
	    break;
	default:
	    ReportError("TimeoutConnection", "Internal State Error");
	    break;
    }

    return ObjTrue;
#endif
}

static void HostNameErrorAlert()
/*Creates a host name error*/
{
    WinInfoPtr alertWindow;
    alertWindow = AlertUser(UIERRORALERT, (WinInfoPtr) 0, 
	"A host by that name or address could not be found.", ConnectToComputerAgain, 
	2, "Cancel", "Re-enter");
    if (alertWindow) SetVar((ObjPtr) alertWindow, HELPSTRING,
	NewString("A valid host could not be found for that name or address.  \
This may be it is not in the name server or the name server is not available.  You can check the \
address to make sure that it is valid and try again."));
}

#ifdef PROTO
static void NoDaemonAlert(ObjPtr connection)
#else
static void NoDaemonAlert(connection)
ObjPtr connection;
#endif
/*Creates a "no daemon" error*/
{
    WinInfoPtr alertWindow;
    ObjPtr var;
    char *message = "";

    var = GetIntVar("NoDaemonAlert", connection, CONNECTIONKIND);
    if (var)
    {
	switch(GetInt(var))
	{
	    case CK_REMOTE_DAEMON:
		message = "There is no SciAn network daemon running on that host.";
		break;
	    case CK_LOCAL_DAEMON:
		message = "There is no SciAn network daemon running on your local computer.";
		break;
	    default:
		message = "Internal State Error in NoDaemonAlert";
		break;
	}
    }

    alertWindow = AlertUser(UISEVEREALERT, (WinInfoPtr) 0, 
	message, ConnectToComputerAgain, 
	2, "Cancel", "Re-enter");
    if (alertWindow) SetVar((ObjPtr) alertWindow, HELPSTRING,
	NewString("In order for you to be able to connect \
to SciAn processes running on a computer, the SciAn network daemon must be running \
on the computer.  The SciAn network daemon can by started by entering 'snd &' on \
the command line of the host computer."));
}

static ObjPtr KillConnection(connection)
ObjPtr connection;
/*Kills a connection*/
{
    DeleteFromList(allConnections, connection);
    return ObjTrue;
}

void FindCommand(connection)
ObjPtr connection;
/*Finds a command corresponding to CURCMD in connection and changes the
connection's state appropriately to deal with it.*/
{
    ObjPtr command;
    command = GetVar(connection, CURCMD);

    if (command)
    {
	char *cmd;
	cmd = GetString(command);

#ifdef DEBUG
fprintf(stderr, "received header of %s command\n", cmd);
#endif

	if (strlen(cmd) == 4)
	{
	    long cmdNum;

	    cmdNum = CMD(cmd);

	    if (cmdNum == CMD(ND_INFO_LIVE_PROCESS) ||
		cmdNum == CMD(ND_INFO_DEAD_PROCESS))
	    {
		/*Live or dead process*/
		SetConnectionState(connection, CS_READ_NUMBER_ONE);
	    }
	    else if (cmdNum == CMD(ND_CLOSE))
	    {
		SetConnectionState(connection, CS_MORIBUND);
	    }
	    /* This may be temporary. provides an exit for comp process */
	    else if (libRunning && cmdNum == CMD(ND_EXIT))
	    {
		SetConnectionState(connection, CS_MORIBUND);
		fprintf(stderr, "Got exit command, turning off library\n");
		libRunning = 0;
	    }
	    else if (cmdNum == CMD(ND_LIST_OBJECTS))
	    {
		ThingListPtr runner;

		runner = LISTOF(allAdvertised);
		while (runner)
		{
		    sprintf (tempStr, "%s %ld\n", ND_REG_OBJECT, GetInt(GetIntVar("FindCommand", runner -> thing, NETWORKID)));
		    SendSocketCommandLater(connection,tempStr);
		    SendObjectLater(connection, runner -> thing);
		    runner = runner -> next;
		}
		SetConnectionState(connection, CS_WAIT_FOR_CMD_END);
	    }
	    else if (cmdNum == CMD(ND_REG_OBJECT))
	    {
		SetConnectionState(connection, CS_READ_NUMBER_ONE);
	    }
	    else if (cmdNum == CMD(NO_MESG_OBJECT))
	    {
		SetConnectionState(connection, CS_READ_OBJECT);
	    }
	    else if (cmdNum == CMD(NO_MESG_SEND_OBJECT))
	    {
		SetConnectionState(connection, CS_READ_NUMBER_ONE);
	    }
	    else if (cmdNum == CMD(NO_MESG_GETVAR))
	    {
		SetConnectionState(connection, CS_READ_NUMBER_ONE);
	    }
	    else if (cmdNum == CMD(NO_MESG_GETELEMENT))
	    {
		SetConnectionState(connection, CS_READ_NUMBER_ONE);
	    }
	    else if (cmdNum == CMD(NO_MESG_GOTVAR))
	    {
		SetConnectionState(connection, CS_READ_NUMBER_ONE);
	    }
	    else if (cmdNum == CMD(NO_MESG_GOTELEMENT))
	    {
		SetConnectionState(connection, CS_READ_NUMBER_ONE);
	    }
#ifndef NEWSTYLEUPDATES
	    else if (cmdNum == CMD(NO_MESG_UPDATEVAR))
	    {
		SetConnectionState(connection, CS_READ_NUMBER_ONE);
	    }
#else
	    else if (cmdNum == CMD(NO_MESG_UPDATEVAR_ID))
	    {
		SetConnectionState(connection, CS_READ_NUMBER_ONE);
	    }
	    else if (cmdNum == CMD(NO_MESG_UPDATEVAR_SEND))
	    {
		SetConnectionState(connection, CS_READ_NUMBER_ONE);
	    }
#endif
	    else if (cmdNum == CMD(NO_MESG_MACHINE_IRIS4D))
	    {
#if MACHINE == IRIS4D
fprintf(stderr, "fast socket\n");
		SetVar(connection, FASTSOCKET, ObjTrue);
#endif
		SetConnectionState(connection, CS_WAIT_FOR_CMD_END);
	    }
	    else if (cmdNum == CMD(NO_MESG_MACHINE_RS6000))
	    {
#if MACHINE == RS6000
#endif
		SetConnectionState(connection, CS_WAIT_FOR_CMD_END);
	    }
	    else if (cmdNum == CMD(NO_MESG_MACHINE_UNKNOWN))
	    {
		SetConnectionState(connection, CS_WAIT_FOR_CMD_END);
	    }
	    else
	    {
		sprintf(tempStr, "Bad socket command: %s", cmd);
		ReportError("FindCommand", tempStr);
		SetConnectionState(connection, CS_WAIT_FOR_CMD_END);
	    }
	}
	else
	{
	    SetConnectionState(connection, CS_WAIT_FOR_CMD_END);
	    ReportError("FindCommand", "Socket command wrong size");
	}
    }
    else
    {
	SetConnectionState(connection, CS_WAIT_FOR_CMD_END);
	ReportError("FindCommand", "No command specified");
    }
}

#ifdef PROTO
Bool ThisWouldBeAReallyBadThing(NameTyp var)
#else
Bool ThisWouldBeAReallyBadThing(var)
NameTyp var;
#endif
{
    switch(var)
    {
	case SELECTED:
	    return true;
	case NETWORKID:
	    return true;
	case OWNERCONNECTION:
	    return true;
/*	case CPALETTE:
	    return true; */	/* HAK temporary kludge */
	case CURDATA:
	    return true;	/* force CURDATA to be made locally */
	case PREFVIS:
	    return true;	/* can't handle vis classes (only datasets) */
	default:
	    return false;
    }
}

#ifdef PROTO
void SendGetVarMessage(ObjPtr obj, NameTyp var)
#else
void SendGetVarMessage(obj, var)
ObjPtr obj;
NameTyp var;
#endif
{
    ObjPtr connection;
    int netid;

    /* object had better be a Remote object */
    connection = GetVar(obj, OWNERCONNECTION);
    if (!connection)
    {
	ReportError("SendGetVarMessage", "object has no OWNERCONNECTION");
    }
    netid = GetInt(GetVar(obj, NETWORKID));
    if (!netid)
    {
	ReportError("SendGetVarMessage", "object has no NETWORKID");
    }
    if (connection && netid)
    {
	sprintf(tempStr, "%s %d %d\n", NO_MESG_GETVAR, netid, var);
	SendSocketCommandLater(connection, tempStr);
    }
}

void SendSendObjectMessage(ObjPtr connection, unsigned long netid)
{
    ObjPtr process;

    if (!netid)
    {
	ReportError("SendSendObjectMessage", "netid = 0!");
    }
    else if (!connection)
    {
	ReportError("SendSendObjectMessage", "connection nil");
    }
    else if (!(process = GetVar(connection, OWNERPROCESS)))
    {
	ReportError("SendSendObjectMessage", "connection has no OWNERPROCESS");
    }
    else
    {
	sprintf(tempStr, "%s %ld\n", NO_MESG_SEND_OBJECT, netid);
	SendSocketCommandLater(connection, tempStr);
	NetworkIDIsWaiting(process, netid, true);
    }
}

#ifdef PROTO
void SendGetElementMessage(ObjPtr obj, NameTyp el)
#else
void SendGetElementMessage(obj, var)
ObjPtr obj;
NameTyp var;
#endif
{
    ObjPtr connection;
    int netid;

    /* object had better be a Remote object */
    connection = GetVar(obj, OWNERCONNECTION);
    netid = GetInt(GetVar(obj, NETWORKID));
    if (connection && netid)
    {
	sprintf(tempStr, "%s %d %d\n", NO_MESG_GETELEMENT, netid, el);
	SendSocketCommandLater(connection, tempStr);
    }
    else
    {
	ReportError("SendGetElementMessage", "object has no OWNERCONNECTION or NETWORKID");
    }
}

#ifdef PROTO
void SendVarUpdateNotice(ObjPtr obj, NameTyp var)
#else
void SendVarUpdateNotice(obj, var)
ObjPtr obj;
NameTyp var;
#endif
{
    ThingListPtr runner;
    int netid;
    VarsPtr vptr;

    netid = GetInt(GetVar(obj, NETWORKID));
    if (!netid)
    {
	ReportError("SendVarUpdateNotice", "object has no NETWORKID");
	return;
    }
    runner = LISTOF(allConnections);
    while (runner)
    {
	/* any non-daemon connection */
	if (CK_LOCAL_SCIAN == GetInt(GetIntVar("SendVarUpdateNotice", runner -> thing, CONNECTIONKIND)))
	{
#ifndef NEWSTYLEUPDATES
	    sprintf(tempStr, "%s %ld %ld\n", NO_MESG_UPDATEVAR, netid, var);
	    SendSocketCommandLater(runner -> thing, tempStr);
#else
	    vptr = GetVarNode(obj, var);
	    if (!vptr)
	    {
fprintf(stderr, "something funny happened\n");
		sprintf(tempStr, "%s %ld %ld %ld %ld \n", NO_MESG_UPDATEVAR, netid, var, 0, 0);
		SendSocketCommandLater(runner -> thing, tempStr);
	    }
/* HAK */
#if 0
	    else if ( 0 && ObjectWasSent(vptr -> value, runner -> thing))
#else
	    else if (ObjectWasSent(vptr -> value, runner -> thing))
#endif
	    {
		unsigned long valNetID;

		valNetID = PublishObject(vptr -> value);
		sprintf(tempStr, "%s %ld %ld %ld %ld \n", NO_MESG_UPDATEVAR_ID, netid, var, valNetID, vptr -> chCount);
		SendSocketCommandLater(runner -> thing, tempStr);
	    }
	    else
	    {
		unsigned long valNetID;

		valNetID = PublishObject(vptr -> value);
		sprintf(tempStr, "%s %ld %ld %ld %ld \n", NO_MESG_UPDATEVAR_SEND, netid, var, valNetID, vptr -> chCount);
		SendSocketCommandLater(runner -> thing, tempStr);
		if (vptr -> value)
		    SendObjectLater(runner -> thing, vptr -> value);
	    }
#endif
	}
	runner = runner -> next;
    }
}

ObjPtr AdvertiseObject(object)
ObjPtr object;
/*Advertises an object*/
{
    ObjPtr networkID;
    int netid;
    ThingListPtr runner; /* runs down connection list */

    if (GetPredicate(object, ADVERTISED))
    {
	/* already advertised */
	return ObjFalse;
    }

    SetVar (object, ADVERTISED, ObjTrue);

    netid = PublishObject(object);

    PrefixList (allAdvertised, object);

    runner = LISTOF(allConnections);
    while (runner)
    {
	/* any non-daemon connection */
	if (CK_LOCAL_SCIAN == GetInt(GetIntVar("AdvertiseObject", runner -> thing, CONNECTIONKIND)))
	{
	    sprintf(tempStr, "%s %ld\n", ND_REG_OBJECT, netid);
	    SendSocketCommandLater(runner -> thing, tempStr);
	    SendObjectLater(runner -> thing, object);
	}
	runner = runner -> next;
    }
    
    return ObjTrue;
}

ObjPtr UnadvertiseObject(object)
ObjPtr object;
/*Unadvertises an object*/
{
    ThingListPtr runner;
    if (!GetPredicate(object, ADVERTISED))
    {
	/* never advertised, or already unadvertised */
	return ObjFalse;
    }

    SetVar (object, ADVERTISED, ObjFalse);

    DeleteFromList(allAdvertised, object);

    runner = LISTOF(allConnections);
    while (runner)
    {
	if (CK_LOCAL_SCIAN == GetInt(GetIntVar("AdvertiseObject", runner -> thing, CONNECTIONKIND)))
	{
	    sprintf(tempStr, "%s %ld %ld\n", ND_UNREG_OBJECT, GetIntVar("AdvertiseObject", runner -> thing, NETWORKID));
	    SendSocketCommandLater(runner -> thing, tempStr);
	}
	runner = runner -> next;
    }

    return ObjTrue;
}

#ifdef PROTO
static ObjPtr NewProcess(ObjPtr connection, char *procName, int procSocket)
#else
static ObjPtr NewProcess(connection, procName, procSocket)
ObjPtr connection;
char *procName;
int procSocket;
#endif
/*Registers a process with the given name and num from connection*/
{
    WinInfoPtr dialogExists;
    ObjPtr allProcesses;
    ObjPtr process;

    /*Make a new process*/
    process = NewObject(processClass, 0);
    SetVar(process, NAME, NewString(procName));
    SetVar(process, SOCKETID, NewInt(procSocket));
    SetVar(process, OWNERCONNECTION, connection);
    SetVar(process, ALLOBJECTS, NewList());
    SetVar(process, ALLADVERTISED, NewList());

    /*Add to the list of all processes*/
    allProcesses = GetListVar("NewProcess", connection, ALLPROCESSES);
    if (allProcesses)
    {
	PrefixList(allProcesses, process);
    }

    dialogExists = DialogExists((WinInfoPtr) connection, NewString("Controls"));
    if (dialogExists)
    {
	ObjPtr icon, corral;

	/*Make an icon and put it in the corral*/
	corral = GetObjectVar("NewProcess", (ObjPtr) dialogExists, CORRAL);
	if (!corral) return NULLOBJ;

	icon = NewObject(processIcon, 0L);
	SetVar(icon, ICONLOC, NULLOBJ);

	SetVar(icon, NAME, NewString(procName));
	SetVar(icon, REPOBJ, process);
	SetVar(icon, CORRAL, corral);
	DropIconInCorral(corral, icon);
    }

    return process;
}

#ifdef PROTO
Bool IsNetworkIDWaiting(ObjPtr process, unsigned long netid)
#else
Bool IsNetworkIDWaiting(process, netid)
ObjPtr process;
unsigned long netid;
#endif
{
    ObjPtr netWaiting;
    ThingListPtr runner;

    if (netid == NETSTUBFLAG)
    {
	return true;
    }

#ifdef DEBUG
fprintf(stderr, "IsNetworkIDWaiting: process %lx, looking for netid %d\n", process, netid);
#endif

    netWaiting = GetVar(process, NETWAITINGOBJECTS);
    if (!netWaiting || !IsList(netWaiting))
    {
	netWaiting = NewList();
	SetVar (process, NETWAITINGOBJECTS, netWaiting);
    }

    runner = LISTOF(netWaiting);
    while (runner)
    {
	if (netid == GetInt(runner -> thing))
	{
	    return true;
	}
	runner = runner -> next;
    }
    return false;
}

#ifdef PROTO
void NetworkIDIsWaiting(ObjPtr process, unsigned long netid, Bool flag)
#else
void NetworkIDIsWaiting(process, netid, flag)
ObjPtr process;
unsigned long netid;
Bool flag;
#endif
{
    ObjPtr netWaiting;

#ifdef DEBUG
fprintf(stderr, "making id # %ld waiting %s\n", netid, flag ? "true" : "false");
#endif

    netWaiting = GetVar(process, NETWAITINGOBJECTS);
    if (!netWaiting || !IsList(netWaiting))
    {
	netWaiting = NewList();
	if (flag)
	    PrefixList(netWaiting, NewInt(netid));
	SetVar (process, NETWAITINGOBJECTS, netWaiting);
    }
    else if (flag)
    {
	if (!IsNetworkIDWaiting(process, netid))
	{
	    PrefixList(netWaiting, NewInt(netid));
	    SetVar (process, NETWAITINGOBJECTS, netWaiting);
	}
    }
    else
    {
	if (IsNetworkIDWaiting(process, netid))
	{
	    ThingListPtr *runner;
	    runner = &LISTOF(netWaiting);
	    /* delete all occurences of netid from list */
	    while (*runner)
	    {
		if (netid == GetInt((*runner) -> thing))
		{
		    ThingListPtr next;
		    next = (*runner) -> next;
		    free(*runner);
		    *runner = next;
		}
		else
		{
		    runner = &((*runner) -> next);
		}
	    }
	}
    }
}

Bool ObjectWasSent(obj, connection)
ObjPtr obj, connection;
{
    ThingListPtr runner;
    ObjPtr netidObj;
    unsigned long netid;
    ObjPtr allSentList;

    if (!(netidObj = GetVar(obj, NETWORKID)))
    {
	return false;
    }
    netid = GetInt(netidObj);

    allSentList = GetVar(connection, ALLSENTLIST);
    if (!allSentList)
    {
	return false;
    }
/* fprintf(stderr, "looking for %ld... ", netid); */
    runner = LISTOF(allSentList);
    while (runner)
    {
/* fprintf(stderr, "%ld ", GetInt(GetVar(runner -> thing, NETWORKID))); */
	if (netid == GetInt(GetVar(runner -> thing, NETWORKID)))
	{
/* fprintf(stderr, "Found it!\n"); */
	    return true;
	}
	runner = runner -> next;
    }
/* fprintf(stderr, "didn't find it..\n"); */
    return false;
}

#ifdef PROTO
ObjPtr FindRemoteObject(ObjPtr process, int netid, Bool getFlag)
#else
ObjPtr FindRemoteObject(process, netid, getFlag)
ObjPtr process;
int netid;
Bool getFlag
#endif
{
    ObjPtr allObjects;
    ThingListPtr runner;


    if (netid == NETSTUBFLAG)
    {
	return NULLOBJ;
    }

    allObjects = GetVar(process, ALLOBJECTS);
    if (!allObjects || !IsList(allObjects))
    {
	allObjects = NewList();
	SetVar (process, ALLOBJECTS, allObjects);
    }

    runner = LISTOF(allObjects);
    while(runner)
    {
	if (netid == GetInt(GetVar(runner -> thing, NETWORKID)))
	{
	    break;
	}
	runner = runner -> next;
    }

    if (runner)
    {
	return runner -> thing;
    }
    else
    {
	if (getFlag)
	{
	    ThingListPtr runner;
	    ObjPtr connection = NULLOBJ;

	    runner = LISTOF(allConnections);
	    while (runner)
	    {
		if (GetVar(runner -> thing, OWNERPROCESS) == process)
		{
		    connection = runner -> thing;
		    break;
		}
		runner = runner -> next;
	    }
	    if (connection)
	    {
		ObjPtr retVal;

		if (!IsNetworkIDWaiting(process, netid))
		{
		    SendSendObjectMessage(connection, netid);
		}

		WaitForNetObject(connection, netid);
		if (retVal = FindRemoteObject(process, netid, false))
		{
		    return retVal;
		}
		else
		{
		    ReportError("FindRemoteObject", "Internal error (probably timeout)");
		}
	    }
#ifdef PARANOID
	    else
	    {
		ReportError("FindRemoteObject", "No connection found for process");
	    }
#endif
	}
    }
    return NULLOBJ; /* only if not found ( && flag == false) or was an error */
}

#ifdef PROTO
static void DeleteProcess(ObjPtr connection, int socketNum)
#else
static void DeleteProcess(connection, socketNum)
ObjPtr connection;
int socketNum;
#endif
/*Deletes a process with the given name and num from connection*/
{
    WinInfoPtr dialogExists;
    ObjPtr allProcesses;
    ObjPtr process = NULLOBJ;
    ThingListPtr runner;
    /*Find the process*/
    allProcesses = GetListVar("DeleteProcess", connection, ALLPROCESSES);
    if (allProcesses)
    {
	runner = LISTOF(allProcesses);
	while (runner)
	{
	    ObjPtr var;
	    var = GetVar(runner -> thing, SOCKETID);
	    if (var && socketNum == GetInt(var))
	    {
		process = runner -> thing;
	    }
	    runner = runner -> next;
	}
    }

    if (process)
    {
	DeleteFromList(allProcesses, process);

        dialogExists = DialogExists((WinInfoPtr) connection, NewString("Controls"));
	if (dialogExists)
	{
	    ObjPtr contents, corral, delIcon = NULLOBJ;

	    /*Delete the icon from the corral*/
	    corral = GetObjectVar("DeleteProcess", (ObjPtr) dialogExists, CORRAL);
	    if (!corral) return;

	    contents = GetListVar("DeleteProcess", corral, CONTENTS);
	    if (!contents) return;
	    runner = LISTOF(contents);
	    while (runner)
	    {
		if (GetVar(runner -> thing, REPOBJ) == process)
		{
		    delIcon = runner -> thing;
		}
		runner = runner -> next;
	    }
	    DeleteFromList(contents, delIcon);
	    ImInvalid(corral);
	}
    }
}

#if 0
#ifdef PROTO
void FlagObjectAsChanging(ObjPtr obj)
#else
void FlagObjectAsChanging(obj)
ObjPtr obj;
#endif
{
    HAK!
}
#endif

static ObjPtr NewRemoteObject(filledObj)
ObjPtr filledObj;
/*Method to register a new remote object and add its icon to the process window*/
{
    WinInfoPtr dialogExists;
    ObjPtr connection, process;

    connection = GetVar(filledObj, OWNERCONNECTION);
    if (!connection)
    {
	ReportError("NewRemoteObject", "Object has no connection");
	return ObjFalse;
    }

    process = GetVar(connection, OWNERPROCESS);
    if (!process)
    {
	ReportError("NewRemoteObject", "John lied");
	return ObjFalse;
    }

    dialogExists = DialogExists((WinInfoPtr) process, NewString("Controls"));
    if (dialogExists)
    {
	ObjPtr icon, corral, name;

	/*Make an icon and put it in the corral*/
	corral = GetObjectVar("NewRemoteObject", (ObjPtr) dialogExists, CORRAL);
	MakeVar(filledObj, DEFAULTICON);
	icon = GetVar(filledObj, DEFAULTICON);
	if (icon)
	{
	    icon = NewObject(icon, 0L);
	}
	else
	{
	    icon = NewIcon(ICONQUESTION, 0, 0, "?");
	}
        SetVar(icon, ICONLOC, NULLOBJ);
	MakeVar(filledObj, NAME);
	name = GetVar(filledObj, NAME);
        SetVar(icon, NAME, name);
	SetVar(icon, FORMAT, GetVar(process, NAME)); 
        SetVar(icon, REPOBJ, filledObj);
        SetVar(icon, CORRAL, corral);
        DropIconInCorral(corral, icon);
    }
    return ObjTrue;
}

#ifdef PARANOID
int ICCount = 0;
#endif

#ifdef PROTO
Bool IdleConnection(ObjPtr connection)
#else
void IdleConnection(connection)
ObjPtr connection;
#endif
/*Idles a connection*/
{
    int state;
    Bool retVal;

    retVal = false;

#ifdef PARANOID
    if (++ICCount > 1)
    ReportError("IdleConnection", "HEY! Multiple calls are active! Very bad!\n");
#endif

    state = GetConnectionState(connection);

    switch (state)
    {
	case CS_NOT_LISTENING:		/*Accepting socket, not listening*/
	    {
		struct sockaddr_in templateAddr;
		int template;
		int proto;
		int port;

		/*Initialize internet socket template to internet, any machine*/
		port = SocketPort();
		templateAddr . sin_port = port;
		templateAddr . sin_family = PF_INET;
		templateAddr . sin_addr.s_addr = INADDR_ANY;

		proto = getprotobyname("tcp") -> p_proto;
		template = socket(PF_INET, SOCK_STREAM, proto);
		if (template >= 0)
		{
		    int status;

		    status = bind(template, &templateAddr, sizeof(templateAddr));
		    if (status >= 0)
		    {
			ObjPtr localConnection;
			/*Name was bound OK*/

			/*Set socket to be non blocking*/
#if MACHINE == IRIS4D
			fcntl(template, F_SETFL, 
				fcntl(template, F_GETFL) | FNDELAY);
#else
			fcntl(template, F_SETFL, 
				fcntl(template, F_GETFL) | O_NDELAY);
			fcntl(template, F_SETFL,
				fcntl(template, F_GETFL) | O_NONBLOCK);
#endif

			/*Listen for a maximum of 5 connections*/
			listen(template, 5);
			((ConnectionPtr) connection) -> template = template;
			SetVar(connection, SOCKETID, NewInt(port));

			/*Send out a newp, now that we know who we are*/
			localConnection = NewLocalDaemonConnection();
			sprintf(tempStr, "%s %d %d %s_(%d)\n", 
			    ND_REG_PROCESS, getpid(), port, whatsMyName, getpid());
			SendSocketCommandLater(localConnection, tempStr);

			SetConnectionState(connection, CS_LISTENING);
		    }
		    else
		    {
			/*Connection failed, try again later.*/
		    }
		}
		else
		{
		    ReportError("IdleSockets", "socket call failed!");
		}
	    }
	    break;
	case CS_LISTENING:		/*Accepting socket, listening*/
	    {
		int sock, template;

		template = ((ConnectionPtr) connection) -> template;
		if (template >= 0)
		{
		    sock = accept(template, (struct sockaddr *) 0, (int *) 0);
		    if (sock != -1)
		    {
			ObjPtr newConnection;

			/*Wow, a connection*/ printf("Wow, a connection\n");
			newConnection = NewLocalScianConnection(sock);
		    }
		    else
		    {
			/*No connection yet*/
			break;
		    }
		}
		else
		{
		    ReportError("IdleSocket", "Bad listener template");
		}
	    }
	    break;
	case CS_NAME_ONLY:		/*We've got the name only*/
	    {
		struct hostent *host;
		ObjPtr name;
		char *s;
		struct sockaddr_in inSocketAddr;
		WinInfoPtr alertWindow;

		name = GetStringVar("IdleConnection", connection, NAME);
		if (!name)
		{
		    break;
		}

		/*Try to get the host information based on the address or name*/
		host = gethostbyname(GetString(name));

		if (host)
		{
		    ObjPtr var;
		    int k, inSocket, port;
		    struct protoent *proto;

		    /*Now try to connect*/
		    for (k = 0; k < sizeof(inSocketAddr); ++k)
		    {
			*(k + (char *) &inSocketAddr) = 0;
		    }
		    for (k = 0; k < host -> h_length; ++k)
		    {
			*(k + (char *) &inSocketAddr . sin_addr) =
			    *(k + (char *) host -> h_addr);
        	    }
		    var = GetVar(connection, SOCKETID);
		    if (var)
		    {
			port = GetInt(var);
		    }
		    else
		    {
			port = ND_BASE_SOCKET_NUMBER;
		    }
		    inSocketAddr . sin_port = htons((u_short) port);
		    inSocketAddr . sin_family = PF_INET;	/*Internet protocol*/

		    proto = getprotobyname("tcp");

		    inSocket = socket(PF_INET, SOCK_STREAM, proto -> p_proto);
		    if (inSocket >= 0)
		    {
			int status;

		 	status = connect(inSocket, &inSocketAddr, sizeof(inSocketAddr));
			if (status >= 0)
			{
			    /*Set socket to be non blocking*/
			    fcntl(inSocket, F_SETFL, 
				fcntl(inSocket, F_GETFL) | FNDELAY);

			    /*Send a validator*/
			    sprintf(tempStr, "%s\n", ND_PASSWORD);
			    writen(inSocket, tempStr, strlen(tempStr));

			    /*Put the fd into the connection*/
			    ((ConnectionPtr) connection) -> sock = inSocket;

			    /*Set the state*/
			    SetConnectionState(connection, CS_WAIT_FOR_VERSION);
			}
			else
			{
			    NoDaemonAlert(connection);
			    SetConnectionState(connection, CS_MORIBUND);
			}
		    }
		    else
		    {
			ReportError("IdleSocket", "Problem making socket.");
			SetConnectionState(connection, CS_MORIBUND);
		    }
		}
		else
		{
		    /*Error with the host name.*/
		    switch (h_errno)
		    {
			case HOST_NOT_FOUND:		/*No host by that name*/
			default:			/*Any other fatal error*/
			    HostNameErrorAlert();
			    SetConnectionState(connection, CS_MORIBUND);
			    break;
			case TRY_AGAIN:			/*Temporary error*/
			    break;
		    }
		}
	    }
	    break;
	case CS_WAIT_FOR_VERSION:			/*Waiting for a VERS to come back*/
	    {
		int sock;
		int nChars;
		ObjPtr command;
		ObjPtr curCmd;

		curCmd = GetVar(connection, CURCMD);
		if (!curCmd) curCmd = NewString(""); 

		sock = ((ConnectionPtr) connection) -> sock;
		if (sock >= 0)
		{
		    Bool anything;
		    char inChar;
		    anything = false;

		    /*See if there are characters waiting*/
		    while ((1 == read(sock, &inChar, 1)) &&
			   inChar &&
			   !isspace(inChar))
		    {
			char cmd[2];
			/*There's a command of some sort*/
			cmd[0] = inChar;
			cmd[1] = 0;

			curCmd = ConcatStrings(curCmd, NewString(cmd));
			anything = true;
		    }
		    if (anything)
		    {
			SetVar(connection, CURCMD, curCmd);
			if (inChar == '\n' || inChar == 0)
			{
			    ((ConnectionPtr) connection) -> gotCmdEnd = true;
			}
			if (inChar == 0 || isspace(inChar))
			{
			    char *cmd;

			    /*The command is done.  Find it*/
			    cmd = GetString(curCmd);
			    if (strlen(cmd) == 4 && CMD(cmd) == CMD(ND_INFO_VERSION))
			    {
				ObjPtr var;
				/*Got a version.  Either register our process
				  or ask for updates*/

				var = GetIntVar("IdleSocket", connection, CONNECTIONKIND);
				if (var)
				{
				    switch (GetInt(var))
				    {
					case CK_REMOTE_DAEMON:
					    sprintf(tempStr, "%s\n", ND_UPDATE_PROCESSES);
					    SendSocketCommandLater(connection, tempStr);
					    ShowConnectionControls(connection, NULLOBJ, GetString(GetVar(connection, NAME)));
					    break;
					case CK_LOCAL_DAEMON:
					    {
						/*Make a local listener*/
						NewListenerConnection();
					    }
					    break;
					case CK_REMOTE_SCIAN:
					    /* say what kind of machine I am */
#if MACHINE == IRIS4D
					    sprintf(tempStr, "%s\n", NO_MESG_MACHINE_IRIS4D);
#else
#if MACHINE == RS6000
					    sprintf(tempStr, "%s\n", NO_MESG_MACHINE_RS6000);
#else
					    sprintf(tempStr, "%s\n", NO_MESG_MACHINE_UNKNOWN);
#endif
#endif
					    SendSocketCommandLater(connection, tempStr);
					    sprintf(tempStr, "%s\n", ND_LIST_OBJECTS);
					    SendSocketCommandLater(connection, tempStr);
					    break;
					default:
					    ReportError("IdleConnection", "Internal Error: bad connection type");
					    break;
				    }
				}
				SetConnectionState(connection, CS_WAIT_FOR_CMD_END);
			    }
			    else
			    {
				/*DIKEO make user alert*/
				ReportError("IdleSockets", "Funny return");
				SetConnectionState(connection, CS_MORIBUND);
				++TrashDayFlag;
			    }
			}
		    }
		}
		else
		{
		    ReportError("IdleSockets", "No socket file descriptor");
		    SetConnectionState(connection, CS_MORIBUND);
		    ++TrashDayFlag;
		}
	    }
	    break;
	case CS_WAIT_FOR_PASSWORD:			/*Waiting for a RUN? to come back*/
	    {
		int sock;
		int nChars;
		ObjPtr command;
		ObjPtr curCmd;

		curCmd = GetVar(connection, CURCMD);
		if (!curCmd) curCmd = NewString(""); 

		sock = ((ConnectionPtr) connection) -> sock;
		if (sock >= 0)
		{
		    Bool anything;
		    char inChar;
		    anything = false;

		    /*See if there are characters waiting*/
		    while ((1 == read(sock, &inChar, 1)) &&
			   inChar &&
			   !isspace(inChar))
		    {
			char cmd[2];
			/*There's a command of some sort*/
			cmd[0] = inChar;
			cmd[1] = 0;

			curCmd = ConcatStrings(curCmd, NewString(cmd));
			anything = true;
		    }
		    if (anything)
		    {
			SetVar(connection, CURCMD, curCmd);
			if (inChar == '\n' || inChar == 0)
			{
			    /* ignore it */
			    ((ConnectionPtr) connection) -> gotCmdEnd = true;
			}
			if (inChar == 0 || isspace(inChar))
			{
			    char *cmd;

			    /*The command is done.  Find it*/
			    cmd = GetString(curCmd);
			    if (strlen(cmd) == 4 && CMD(cmd) == CMD(ND_PASSWORD))
			    {
				char *s;
				ObjPtr var;
				/*Got a password.  Return a version.*/

				sprintf(tempStr, "%s Scian %s\n", ND_INFO_VERSION, SCIANVERSION);
				s = tempStr + 5;
				while (*s)
				{
				    if (*s == ' ') *s = '_';
				    ++s;
				}
				SendSocketCommandLater(connection, tempStr);
				/* say what kind of machine I am */
#if MACHINE == IRIS4D
				sprintf(tempStr, "%s\n", NO_MESG_MACHINE_IRIS4D);
#else
#if MACHINE == RS6000
				sprintf(tempStr, "%s\n", NO_MESG_MACHINE_RS6000);
#else
				sprintf(tempStr, "%s\n", NO_MESG_MACHINE_UNKNOWN);
#endif
#endif
				SendSocketCommandLater(connection, tempStr);
				SetConnectionState(connection, CS_WAIT_FOR_CMD_END);
			    }
			    else
			    {
				/*DIKEO make user alert*/
				ReportError("IdleSockets", "Funny return");
				SetConnectionState(connection, CS_MORIBUND);
			    }
			}
		    }
		}
		else
		{
		    ReportError("IdleSockets", "No socket file");
		    SetConnectionState(connection, CS_MORIBUND);
		}
	    }
	    break;
	case CS_IDLING:			/*At connection idle*/
	    {
		int sock; /* socket file descriptor */
		sock = ((ConnectionPtr) connection) -> sock;
		if (sock >= 0)
		{
		    char inChar;
		    /*See if there's a character waiting*/
		    if ((1 == read(sock, &inChar, 1)) &&
			isalpha(inChar))
		    {
			char cmd[2];
			/*There's a command of some sort*/
			cmd[0] = inChar;
			cmd[1] = 0;

			SetVar(connection, CURCMD, NewString(cmd));
			SetConnectionState(connection, CS_FILL_CMD);
			retVal = true;
		    }
		    else
		    {
			/*See if there are commands to be sent*/
			ObjPtr cmdQueue;
			cmdQueue = GetVar(connection, OUTPUTQUEUE);
			if (cmdQueue)
			{
			    ThingListPtr runner;
			    runner = LISTOF(cmdQueue);
			    while (runner)
			    {
				if (!IsPublished(runner -> thing))
				{
				    char *str;
				    str = GetString(runner -> thing);
				    if (str)
				    {
#ifdef DEBUG
					fprintf(stderr, "Sending string: %s\n", str);
#endif
	     		        	writen(sock, str, strlen(str));
				    }
				    else
				    {
#ifdef DEBUG
					fprintf(stderr, "ERROR! null string in OUTPUTQUEUE\n");
#endif
				    }
				}
				else
				{
#ifdef DEBUG
				    fprintf(stderr, "Sending object: 0x%x\n", runner -> thing);
#endif
				    TransmitObject(runner -> thing, connection, true);
				}
				runner = runner -> next;
			    }
			    SetVar(connection, OUTPUTQUEUE, NewList());
			}
		    }
		}
	    }
	    break;
	case CS_FILL_CMD:			/*Filling command*/
	    {
		int sock;
		int nChars;
		ObjPtr command;
		ObjPtr curCmd;

		curCmd = GetVar(connection, CURCMD);
		if (!curCmd) curCmd = NewString(""); 

		sock = ((ConnectionPtr) connection) -> sock;
		if (sock >= 0)
		{
		    Bool anything;
		    char inChar = 'x';
		    anything = false;

		    /*See if there are characters waiting*/
		    while ((1 == read(sock, &inChar, 1)) &&
			   inChar &&
			   !isspace(inChar))
		    {
			char cmd[2];
			/*There's a command of some sort*/
			cmd[0] = inChar;
			cmd[1] = 0;

			curCmd = ConcatStrings(curCmd, NewString(cmd));
			anything = true;
		    }
		    SetVar(connection, CURCMD, curCmd);
		    if (inChar == '\n' || inChar == 0)
		    {
			((ConnectionPtr) connection) -> gotCmdEnd = true;
		    }
		    if (inChar == 0 || isspace(inChar))
		    {
			/*The command is done.  Find it*/
			FindCommand(connection);
		    }
		}
	    }
	    retVal = true;
	    break;
	case CS_READ_NUMBER_ONE:		/*Read first number*/
	case CS_READ_NUMBER_TWO:		/*Read second number*/
	case CS_READ_NUMBER_THREE:		/*Read third number*/
	case CS_READ_NUMBER_FOUR:		/*Read fourth number*/
	    {
		int sock;
		int oldState;

		oldState = state;

		sock = ((ConnectionPtr) connection) -> sock;
		if (sock >= 0)
		{
		    unsigned long num;
		    Bool someDigits = false;
		    char inChar;
		    ObjPtr var;

		    /*Get process num.  If null, it means that one hasn't been
		      set yet, and leading spaces should be tolerated.*/

		    switch (oldState)
		    {
			case CS_READ_NUMBER_ONE:
			    var = GetVar(connection, READNUMBERONE);
			    break;
			case CS_READ_NUMBER_TWO:
			    var = GetVar(connection, READNUMBERTWO);
			    break;
			case CS_READ_NUMBER_THREE:
			    var = GetVar(connection, READNUMBERTHREE);
			    break;
			case CS_READ_NUMBER_FOUR:
			    var = GetVar(connection, READNUMBERFOUR);
			    break;
			default:
			    ReportError("IdleConnection", "Internal state error");
			    break;
		    }
		    if (var)
		    {
			num = GetInt(var);
			someDigits = true;
		    }
		    else
		    {
			num = 0;
		    }

		    /*See if there's a character waiting*/
		    while (1 == read(sock, &inChar, 1))
		    {
			if (isspace(inChar) && (inChar != '\n') && !someDigits)
			{
			    
			    /*Just skip the blanks*/
			}
			else if (inChar >= '0' && inChar <= '9')
			{
			    someDigits = true;
			    num *= 10;
			    num += inChar - '0';
			}
			else
			{
			    /*This is the end of the number*/
			    ObjPtr cmdObj;

			    if (inChar == '\n' || inChar == '\0')
			    {
				((ConnectionPtr) connection) -> gotCmdEnd = true;
			    }

			    cmdObj = GetStringVar("IdleConnection", connection, CURCMD);
			    if (cmdObj)
			    {
				char *cmd;

				cmd = GetString(cmdObj);
				if (CMD(cmd) == CMD(ND_INFO_LIVE_PROCESS))
				{
				    SetVar(connection, CMDPROCSOCKET, NewInt(num));
				    SetConnectionState(connection, CS_READ_PROC_NAME);
				}
				else if (CMD(cmd) == CMD(ND_INFO_DEAD_PROCESS))
				{
				    SetVar(connection, CMDPROCSOCKET, NewInt(num));
				    SetConnectionState(connection, CS_READ_PROC_NAME);
				}
				else if (CMD(cmd) == CMD(NO_MESG_SEND_OBJECT))
				{
				    ObjPtr sendObject;
				    sendObject = FindPublishedObject(num);
				    if (sendObject)
				    {
					SendObjectLater(connection, sendObject);
				    }
				    SetConnectionState(connection, CS_WAIT_FOR_CMD_END);
				}
				else if (CMD(cmd) == CMD(ND_REG_OBJECT))
				{
				    ObjPtr process;

				    process = GetVar(connection, OWNERPROCESS);
				    if (process && num)
				    {
					NetworkIDIsWaiting(process, num, true);
					SetVar(process, REMOTECHCOUNT, NewInt(0));
				    }
				    SetConnectionState(connection, CS_WAIT_FOR_CMD_END);
				}
				else if (CMD(cmd) == CMD(NO_MESG_GETVAR))
				{
				    /* HAK! don't know how to deal with this yet!*/
				    switch (oldState)
				    {
					case CS_READ_NUMBER_ONE:
					    SetConnectionState(connection, CS_READ_NUMBER_TWO);
					    break;
					case CS_READ_NUMBER_TWO:
					    {
						ObjPtr obj, val;
						int netid, valNetid;

						netid = GetInt(GetIntVar("IdleConnection", connection, READNUMBERONE));
						obj = FindPublishedObject(netid);
						if (obj)
						{
						    VarsPtr vPtr;
						    unsigned long chCt;

						    if (!ThisWouldBeAReallyBadThing) /* should not be necessary! */
							MakeVar(obj, num);
						    val = GetVar(obj, num);
						    if (val)
						    {
							valNetid = PublishObject(val);
						    }
						    else
						    {
							valNetid = 0;
						    }

						    vPtr = GetVarNode(obj, num);
						    if (vPtr)
							chCt = vPtr -> chCount;
						    else
							chCt = 0;

						    sprintf(tempStr, "%s %ld %ld %ld %ld\n", NO_MESG_GOTVAR, netid, num, valNetid, chCt);
						    SendSocketCommandLater(connection, tempStr);
						}
						else
						{
#if 1
						    sprintf(tempStr, "%s %ld %ld %ld %ld\n", NO_MESG_GOTVAR, netid, num, NETSTUBFLAG, 0);
#else
						    sprintf(tempStr, "%s %ld %ld %ld %ld\n", NO_MESG_GOTVAR, netid, num, 0, 0);
#endif
						    SendSocketCommandLater(connection, tempStr);
						}
						SetConnectionState(connection, CS_WAIT_FOR_CMD_END);
					    }
					    break;
					default:
					    ReportError("IdleConnection", "Internal State Error");
					    break;
				    }
				}
				else if (CMD(cmd) == CMD(NO_MESG_GETELEMENT))
				{
				    switch (oldState)
				    {
					case CS_READ_NUMBER_ONE:
					    SetConnectionState(connection, CS_READ_NUMBER_TWO);
					    break;
					case CS_READ_NUMBER_TWO:
					    {
						ObjPtr obj, el, *elements;
						int netid, elNetid;

						netid = GetInt(GetIntVar("IdleConnection", connection, READNUMBERONE));
						obj = FindPublishedObject(netid);
						if (obj)
						{
						    elements = (ObjPtr *) ELEMENTS(obj);
						    el = elements[num];
						    if (el)
						    {
							elNetid = PublishObject(el);
						    }
						    else
						    {
							elNetid = 0;
						    }
						    sprintf(tempStr, "%s %d %d %d\n", NO_MESG_GOTELEMENT, netid, num, elNetid);
						    SendSocketCommandLater(connection, tempStr);
						}
						else
						{
						    sprintf(tempStr, "%s %d %d %d\n", NO_MESG_GOTELEMENT, netid, num, 0);
						    SendSocketCommandLater(connection, tempStr);
						}
						SetConnectionState(connection, CS_WAIT_FOR_CMD_END);
					    }
					    break;
					default:
					    ReportError("IdleConnection", "Internal State Error");
					    break;
				    }
				}
				else if (CMD(cmd) == CMD(NO_MESG_GOTVAR))
				{
				    switch (oldState)
				    {
					case CS_READ_NUMBER_ONE:
					    SetConnectionState(connection, CS_READ_NUMBER_TWO);
					    break;
					case CS_READ_NUMBER_TWO:
					    SetConnectionState(connection, CS_READ_NUMBER_THREE);
					    break;
					case CS_READ_NUMBER_THREE:
					    SetConnectionState(connection, CS_READ_NUMBER_FOUR);
					    break;
					case CS_READ_NUMBER_FOUR:
					    if (GetInt(GetVar(connection, READNUMBERTHREE))) /* num3 = 0 means it was a null object */
					    {
						ObjPtr process, target, value;

						process = GetObjectVar("IdleConnection", connection, OWNERPROCESS);
						target = FindRemoteObject(process, GetInt(GetVar(connection, READNUMBERONE)), false); /* MUST be false! */
if (!target)
    fprintf(stderr, "No Target\n");

#if 1 /* HAS to be this way to prevent overlaid calls to WaitForNetObject[Var] and IdleConnection */
						{
						    VarsPtr vptr;

						    if (NETSTUBFLAG == GetInt(GetVar(connection, READNUMBERTHREE)))
						    {
fprintf(stderr, "Very bad!\n");
							/* other guy couldn't find this target! */
							target -> flags = target -> flags | ISDEFUNCT;
						    }

/* HAK shouldn't have to do this */		    /* SendSendObjectMessage(connection, GetInt(GetVar(connection, READNUMBERTHREE))); */
/* correct? */
						    vptr = GetVarNode(target, GetInt(GetVar(connection, READNUMBERTWO)));
						    if (!vptr)
						    {
							SetVar(target, GetInt(GetIntVar("IdleConnection", connection, READNUMBERTWO)), value);
							vptr = GetVarNode(target, GetInt(GetVar(connection, READNUMBERTWO)));
						    }
						    vptr -> remoteNetID = GetInt(GetVar(connection, READNUMBERTHREE));
						    vptr -> remoteChCount = num;
						    vptr -> value = NULLOBJ;
						}
#else
						value = FindRemoteObject(process, GetInt(GetVar(connection, READNUMBERTHREE)), false);
						if (!value)
						{
						    /* ReportError("IdleConnection", "Something smells bad in Sioux City\n"); */
						}
						else
						{
						    VarsPtr vPtr;
						    SetVar(target, GetInt(GetIntVar("IdleConnection", connection, READNUMBERTWO)), value);
						    vPtr = GetVarNode(target, GetInt(GetVar(connection, READNUMBERTWO)));
						    vPtr -> remoteNetID = GetInt(GetVar(connection, READNUMBERTHREE));
						    vPtr -> remoteChCount = num;
						    vPtr -> value = value;
						}
#endif
					    }
					    else
					    {
						ObjPtr process, target;
						process = GetObjectVar("IdleConnection", connection, OWNERPROCESS);
						target = FindRemoteObject(process, GetInt(GetVar(connection, READNUMBERONE)), false); /* MUST be false! */
if (!target)
    fprintf(stderr, "No Target\n");
						SetVar(target, GetInt(GetVar(connection, READNUMBERTWO)), NULLOBJ);
					    }
					    SetConnectionState(connection, CS_WAIT_FOR_CMD_END);
					    break;
					default:
					    ReportError("IdleConnection", "Internal State Error");
					    break;
				    }
				}
				else if (CMD(cmd) == CMD(NO_MESG_GOTELEMENT))
				{
				    switch (oldState)
				    {
					case CS_READ_NUMBER_ONE:
					    SetConnectionState(connection, CS_READ_NUMBER_TWO);
					    break;
					case CS_READ_NUMBER_TWO:
					    SetConnectionState(connection, CS_READ_NUMBER_THREE);
					    break;
					case CS_READ_NUMBER_THREE:
#if 0
					    /* old code */
					    if (num) /* num = 0 means it was a null object */
					    {
						sprintf(tempStr, "%s %d\n", NO_MESG_SEND_OBJECT, num);
						SendSocketCommandLater(connection, tempStr);
						If we do this, MUST do a NetworkIDISWaiting!
					    }
#else
					    /* SPECIAL CASE: if obj netid == element's netid, there's no object coming */
					    {
						ObjPtr process;

						process = GetVar(connection, OWNERPROCESS);
						if (process)
						{
						    ObjPtr target, *elements;
						    unsigned long offset;

						    /* these are always integers, I hope */
						    target = FindRemoteObject(process, GetInt(GetVar(connection, READNUMBERONE)), false); /* MUST be false! */
if (!target)
    fprintf(stderr, "No Target!\n");
if (!IsObjArray(target))
    fprintf(stderr, "Not an ObjArray!\n");
						    elements = (ObjPtr *) ELEMENTS(target);
						    offset = GetInt(GetVar(connection, READNUMBERTWO));
						    elements[offset] = (ObjPtr) num;
						}
					    }
#endif
					    SetConnectionState(connection, CS_WAIT_FOR_CMD_END);
					    break;
					default:
					    ReportError("IdleConnection", "Internal State Error");
					    break;
				    }
				}
#ifndef NEWSTYLEUPDATES
				else if (CMD(cmd) == CMD(NO_MESG_UPDATEVAR))
				{
				    switch(oldState)
				    {
					case CS_READ_NUMBER_ONE:
					    SetConnectionState(connection, CS_READ_NUMBER_TWO);
					    break;
					case CS_READ_NUMBER_TWO:
					    {
						ObjPtr process;
						process = GetVar(connection, OWNERPROCESS);
						if (process)
						{
						    ObjPtr object;
						    object = FindRemoteObject(process, GetInt(GetVar(connection, READNUMBERONE)), false);
						    if (object)
						    {
							VarsPtr vptr;

							SetVar(object, num, NULLOBJ);
							vptr = GetVarNode(object, num);
							vptr -> process = process;
							vptr -> remoteNetID = NETSTUBFLAG;
							vptr -> remoteChCount = anotherDamnReferenceCount;
/* NOT!							SendGetVarMessage(object, num);
*/
						    }
						    if (num == DATA)
						    {
							DatasetChanged(object);
						    }

						}
					    }
					    SetConnectionState(connection, CS_WAIT_FOR_CMD_END);
					    break;
					default:
					    ReportError("IdleConnection", "Internal State Error");
					    break;
				    }
				}
#else
				/* "UPV2 netid varname valuenetid varchct " */
				else if (CMD(cmd) == CMD(NO_MESG_UPDATEVAR_ID))
				{
				    switch(oldState)
				    {
					case CS_READ_NUMBER_ONE:
					    SetConnectionState(connection, CS_READ_NUMBER_TWO);
					    break;
					case CS_READ_NUMBER_TWO:
					    SetConnectionState(connection, CS_READ_NUMBER_THREE);
					    break;
					case CS_READ_NUMBER_THREE:
					    SetConnectionState(connection, CS_READ_NUMBER_FOUR);
					    break;
					case CS_READ_NUMBER_FOUR:
					    {
						ObjPtr process;
						process = GetVar(connection, OWNERPROCESS);
						if (process)
						{
						    ObjPtr object;
						    object = FindRemoteObject(process, GetInt(GetVar(connection, READNUMBERONE)), false);
						    if (object)
						    {
							VarsPtr vptr;
							ObjPtr value;

							value = FindRemoteObject(process, GetInt(GetVar(connection, READNUMBERTHREE)), false);
							if (value)
							{
							    fprintf(stderr, "not good.. got UPDATEVAR_ID but I don't have netid %ld\n",  GetInt(GetVar(connection, READNUMBERTHREE)));
							    SetVar(object, GetInt(GetVar(connection, READNUMBERTWO)), value);
							}
							vptr = GetVarNode(object, GetInt(GetVar(connection, READNUMBERTWO)));
							if (!vptr)
							{
							    SetVar(object, GetInt(GetVar(connection, READNUMBERTWO)), NULLOBJ);
							    vptr = GetVarNode(object, GetInt(GetVar(connection, READNUMBERTWO)));
							}
							vptr -> process = process;
							vptr -> remoteNetID = GetInt(GetVar(connection, READNUMBERTHREE));
							vptr -> remoteChCount = num;
/* NOT!							SendGetVarMessage(object, GetInt(GetVar(connection, READNUMBERTWO)));
*/
						    }
						    if (GetInt(GetVar(connection, READNUMBERTWO)) == DATA)
						    {
							DatasetChanged(object);
						    }

						}
					    }
					    SetConnectionState(connection, CS_WAIT_FOR_CMD_END);
					    break;
					default:
					    ReportError("IdleConnection", "Internal State Error");
					    break;
				    }
				}
				/* "UPV3 netid varname valnetid varchct ... (obj follows) (currently in separate msg) */
				else if (CMD(cmd) == CMD(NO_MESG_UPDATEVAR_SEND))
				{
				    switch(oldState)
				    {
					case CS_READ_NUMBER_ONE:
					    SetConnectionState(connection, CS_READ_NUMBER_TWO);
					    break;
					case CS_READ_NUMBER_TWO:
					    SetConnectionState(connection, CS_READ_NUMBER_THREE);
					    break;
					case CS_READ_NUMBER_THREE:
					    SetConnectionState(connection, CS_READ_NUMBER_FOUR);
					    break;
					case CS_READ_NUMBER_FOUR:
					    {
						ObjPtr process;
						process = GetVar(connection, OWNERPROCESS);
						if (process)
						{
						    ObjPtr object;
						    object = FindRemoteObject(process, GetInt(GetVar(connection, READNUMBERONE)), false);
						    if (object)
						    {
							VarsPtr vptr;

							SetVar(object, GetInt(GetVar(connection, READNUMBERTWO)), NULLOBJ);
							vptr = GetVarNode(object, GetInt(GetVar(connection, READNUMBERTWO)));
							vptr -> process = process;
							vptr -> remoteNetID = NETSTUBFLAG;
							vptr -> remoteChCount = anotherDamnReferenceCount;
						    }
						    if (GetInt(GetVar(connection, READNUMBERTWO)) == DATA)
						    {
							DatasetChanged(object);
						    }
						    NetworkIDIsWaiting(process, GetInt(GetVar(connection, READNUMBERTHREE)), true);
						}
					    }
					    SetConnectionState(connection, CS_WAIT_FOR_CMD_END);
					    break;
					default:
					    ReportError("IdleConnection", "Internal State Error");
					    break;
				    }
				}
#endif
				else
				{
				    ReportError("IdleConnection", "Funny command in CS_READ_NUMBER_num");
				    SetConnectionState(connection, CS_WAIT_FOR_CMD_END);
				}
			    }
			    break; /* out of while loop */
			}
		    }
		    if (someDigits)
		    {
			switch (oldState)
			{
			    case CS_READ_NUMBER_ONE:
				var = SetVar(connection, READNUMBERONE, NewInt(num));
				break;
			    case CS_READ_NUMBER_TWO:
				var = SetVar(connection, READNUMBERTWO, NewInt(num));
				break;
			    case CS_READ_NUMBER_THREE:
				var = SetVar(connection, READNUMBERTHREE, NewInt(num));
				break;
			    case CS_READ_NUMBER_FOUR:
				var = SetVar(connection, READNUMBERFOUR, NewInt(num));
				break;
			    default:
				ReportError("IdleConnection", "Internal state error");
				break;
			}
		    }
		}
	    }
	    retVal = true;
	    break;
	case CS_READ_PROC_NAME:			/*Read the proc name*/
	    {
		int sock;

		sock = ((ConnectionPtr) connection) -> sock;
		if (sock)
		{
		    Bool someAlpha = false;		/*True iff there's some alpha*/
		    char inChar;
		    Bool escaped;
		    ObjPtr var;

		    var = GetVar(connection, CMDPROCNAME);
		    if (var)
		    {
			someAlpha = true;
		    }

		    escaped = GetPredicate(connection, ESCAPED);

		    /*See if there's a character waiting*/
		    while ((1 == read(sock, &inChar, 1)))
		    {
			if (!escaped && !someAlpha && (inChar != '\n') && isspace(inChar))
			{
			    /*Just skip the blanks*/
			}
			else if (!escaped && (inChar == 0 || inChar == '\n'))
			{
			    ObjPtr cmdObj;
			 
			    /*This is the end of the name*/
			    cmdObj = GetStringVar("IdleConnection", connection, CURCMD);
			    if (cmdObj)
			    {
				char *cmd;
				cmd = GetString(cmdObj);
				if (CMD(cmd) == CMD(ND_INFO_LIVE_PROCESS))
				{
				    NewProcess(connection, GetString(var), GetInt(GetVar(connection, CMDPROCSOCKET)));
				}
				else if (CMD(cmd) == CMD(ND_INFO_DEAD_PROCESS))
				{
				    DeleteProcess(connection, GetInt(GetVar(connection, CMDPROCSOCKET)));
				}
			    }
			    SetConnectionState(connection, CS_IDLING);
			    break;
			}
			else if (!escaped && (inChar == '\\'))
			{
			    SetVar(connection, ESCAPED, ObjTrue);
			    escaped = true;
			}
			else
			{
			    char s[2];

			    if (escaped)
			    {
				SetVar(connection, ESCAPED, ObjFalse);
			    }
			    else if (inChar == '_')
			    {
				inChar = ' ';
			    }

			    s[0] = inChar;
			    s[1] = 0;

			    if (var)
			    {
				var = ConcatStrings(var, NewString(s));
			    }
			    else
			    {
				var = NewString(s);
				someAlpha = true;
			    }
			}
		    }
		    if (someAlpha)
		    {
			SetVar(connection, CMDPROCNAME, var);
		    }
		}
	    }
	    break;
	case CS_READ_OBJECT:
	    {
		ObjPtr filledObj, process;
		unsigned long chCt;

		process = GetObjectVar ("IdleConnection", connection, OWNERPROCESS);
		chCt = GetInt(GetIntVar("IdleConnection", process, REMOTECHCOUNT));
		filledObj = ReceiveObject(connection, chCt); /* currently, wait until obj is filled */
#ifdef DEBUG
if (filledObj)
   fprintf(stderr, "well, it returned something\n");
else
   fprintf(stderr, "ERROR! ReceiveObject didn't return anything!\n");
#endif
		if (filledObj)
		{
		    ObjPtr allObjects;
		    int varnum;

		    allObjects = GetVar(process, ALLOBJECTS);
		    if (!allObjects)
		    {
			allObjects = NewList();
		    }
		    PrefixList(allObjects, filledObj);

		    SetVar(process, ALLOBJECTS, allObjects);

		    if (IsRemoteAdvertised(filledObj))
		    {
			/* must be a top-level advertised object */
			ObjPtr allAdvertised;

			allAdvertised = GetVar(process, ALLADVERTISED);
			if (!allAdvertised)
			{
			    allAdvertised = NewList();
			}
			PrefixList(allAdvertised, filledObj);
			SetVar(process, ALLADVERTISED, allAdvertised);
			SetMethod(filledObj, NEWREMOTEOBJ, NewRemoteObject);
			DeferMessage(filledObj, NEWREMOTEOBJ);
		    }
		    else
		    {
			/* Do nothing. Really! */
		    }
		}
		else
		{
fprintf(stderr, "I guess the connection didn't have an owner process\n");
		}

		SetConnectionState(connection, CS_WAIT_FOR_CMD_END);
	    }
	    retVal = true;
	    break;
	case CS_WAIT_FOR_CMD_END:		/*Wait for the end of the command*/
	    {
		int sock;

		sock = ((ConnectionPtr) connection) -> sock;
		if (((ConnectionPtr) connection) -> gotCmdEnd)
		{
		    SetVar(connection, CURCMD, NULLOBJ);
		    SetConnectionState(connection, CS_IDLING);
		}
		else if (sock)
		{
		    char inChar = 'x';
		    /*See if there's a character waiting*/
		    while ((1 == read(sock, &inChar, 1)))
		    {
			if (inChar == '\n' || inChar == 0)
			{
			    SetVar(connection, CURCMD, NULLOBJ);
			    SetConnectionState(connection, CS_IDLING);
			    break;
			}
		    }
		}
		((ConnectionPtr) connection) -> gotCmdEnd = false;
	    }
	    retVal = true;
	    break;
	case CS_MORIBUND:			/*Moribund.  Kill connection*/
	    KillConnection(connection);
	    break;
	case CS_LIMBO:				/*Process is in limbo.  Do nothing and let idle expire.*/
	    break;
	default:
	    fprintf(stderr, "Funny state in IdleConnection\n");
	    break;
    }
#ifdef PARANOID
    --ICCount;
#endif
    return retVal;
}

/* returns true IFF all connections are in idle state, and have no queued
 * output waiting. If flag == true, prints the reason it's returning false
 */
#ifdef PROTO
Bool AllConnectionsIdle(Bool flag)
#else
Bool AllConnectionsIdle(flag)
Bool flag;
#endif
{
    ThingListPtr runner;
    int state;
    ObjPtr cmdQ;

    runner = LISTOF(allConnections);
    while (runner)
    {
	state = GetConnectionState(runner -> thing);
	if (state != CS_IDLING && state != CS_MORIBUND && state != CS_LIMBO && state != CS_LISTENING)
	{
	    if (flag)
		fprintf(stderr, "found connection in state %s\n", stateName[state]);
	    return false;
	}
	else if (state == CS_IDLING || state == CS_LIMBO)
	{
	    cmdQ = GetVar(runner -> thing, OUTPUTQUEUE);
	    if (cmdQ)
	    {
		/* if there's something waiting in the queue, it's not idle */
		if (LISTOF(cmdQ))
		{
		    if (flag)
			fprintf(stderr, "found pending output in socket in state %s\n", stateName[state]);
		    return false;
		}
	    }
	}
	runner = runner -> next;
    }
    return true;
}

#if 0
#ifdef PROTO
Bool IdleAllConnections(void)
#else
void IdleAllConnections()
#endif
/*Idles all the connections*/
{
    ThingListPtr runner;
    Bool retVal;

    retVal = false;
    runner = LISTOF(allConnections);
    while (runner)
    {
	retVal = retVal || IdleConnection(runner -> thing);
	runner = runner -> next;
    }
    return retVal;
}
#else
#ifdef PROTO
Bool IdleAllConnections(void)
#else
void IdleAllConnections()
#endif
/*Idles all the connections*/
{
    ThingListPtr runner;
    Bool retVal;

    retVal = true;
    while (retVal) /* keep going until all connections return false */
    {
    retVal = false;
    runner = LISTOF(allConnections);
    while (runner)
    {
	retVal = retVal || IdleConnection(runner -> thing);
	runner = runner -> next;
    }
    }
    return retVal;
}
#endif

#ifdef PROTO
static ObjPtr NewConnection(char *machineName, int connectionKind)
#else
static ObjPtr NewConnection(machineName, connectionKind)
char *machineName;
Bool connectionKind;
#endif
/*Makes a new connection to machine machineName.  connectionKind
  is the kind of connection:
	CK_REMOTE_DAEMON	Connection to a remote network daemon
	CK_LOCAL_DAEMON		Connection to a local daemon
	CK_LISTENING_SCIAN	Listening connection on a local SciAn
	CK_REMOTE_SCIAN		Connection to a remote SciAn (not implemented)
	CK_LOCAL_SCIAN		Connection from a local SciAn (not implemented)

  Does not do kind-specific initialization for the connection, such
  as socket ID, and leaves the connection in limbo.
*/
{
    char *s;
    ObjPtr connection;

    /*Stuff this in lastComputer*/
    if (lastComputer)
    {
	free(lastComputer);
    }
    lastComputer = malloc(strlen(machineName) + 1);
    strcpy(lastComputer, machineName);

    /*Create the new connection*/
    connection = NewObject(connectionClass, sizeof(Connection) - sizeof(Thing));
    SetVar(connection, CONNECTIONKIND, NewInt(connectionKind));
    SETOBJTYPE(connection -> flags, CONNECTION);
    ((ConnectionPtr) connection) -> sock = -1;
    ((ConnectionPtr) connection) -> gotCmdEnd = false;
    ((ConnectionPtr) connection) -> template = -1;

    PrefixList(allConnections, connection);

    SetVar(connection, NAME, NewString(machineName));
    SetVar(connection, OUTPUTQUEUE, NewList());
    SetVar(connection, ALLPROCESSES, NewList());

    SetVar(connection, CONNECTIONSTATE, NewInt(CS_LIMBO));

    return connection;
}

#ifdef PROTO
ObjPtr NewLocalDaemonConnection(void)
#else
ObjPtr NewLocalDaemonConnection()
#endif
/*Make a new connection to the local daemon*/
{
    char hostName[200];
    ThingListPtr runner;
    ObjPtr retVal;

    /*See if there's connection to the daemon already*/
    runner = LISTOF(allConnections);
    while (runner)
    {
	ObjPtr var;
	var = GetIntVar("NewLocalDaemonConnection", runner -> thing, CONNECTIONKIND);
	if (var && GetInt(var) == CK_LOCAL_DAEMON)
	{
	    return runner -> thing;
	}
	runner = runner -> next;
    }

    /*There must not be.*/
    gethostname(hostName, 199);
    hostName[199] = 0;
    retVal = NewConnection(hostName, CK_LOCAL_DAEMON);
    SetConnectionState(retVal, CS_NAME_ONLY);
    return retVal;
}

#ifdef PROTO
ObjPtr NewRemoteDaemonConnection(char *hostName)
#else
ObjPtr NewRemoteDaemonConnection(hostName)
char *hostName;
#endif
/*Make a new connection to the daemon on hostName*/
{
    ObjPtr retVal;

    retVal = NewConnection(hostName, CK_REMOTE_DAEMON);
    SetVar(retVal, SOCKETID, NewInt(ND_BASE_SOCKET_NUMBER));
    SetConnectionState(retVal, CS_NAME_ONLY);
    return retVal;
}

#ifdef PROTO
ObjPtr NewRemoteScianConnection(char *hostName, int port, ObjPtr process)
#else
ObjPtr NewRemoteScianConnection(hostName, port, process)
char *hostName;
int port
ObjPtr process;
#endif
/*Make a new connection to the SciAn on hostName on socket port*/
{
    ObjPtr retVal;

    retVal = NewConnection(hostName, CK_REMOTE_SCIAN);
    SetVar(retVal, SOCKETID, NewInt(port));
    SetConnectionState(retVal, CS_NAME_ONLY);
    SetVar(retVal, OWNERPROCESS, process); /* JRM connection needs assoc. proc. */
    return retVal;
}


#ifdef PROTO
static ObjPtr NewListenerConnection(void)
#else
static ObjPtr NewListenerConnection();
#endif
{
    ObjPtr retVal;
    ThingListPtr runner;

    /*See if there's connection to the daemon already*/
    runner = LISTOF(allConnections);
    while (runner)
    {
	ObjPtr var;
	var = GetIntVar("NewListenerConnection", runner -> thing, CONNECTIONKIND);
	if (var && GetInt(var) == CK_LISTENING_SCIAN)
	{
	    return runner -> thing;
	}
	runner = runner -> next;
    }

    retVal = NewConnection("", CK_LISTENING_SCIAN);

    SetConnectionState(retVal, CS_NOT_LISTENING);
    return retVal;
}

#ifdef PROTO
static ObjPtr NewLocalScianConnection(int file)
#else
static ObjPtr NewLocalScianConnection(file)
int file;
#endif
/*Opens a new local Scian connection using file as descriptor*/
{
    ObjPtr retVal = NULLOBJ;

    if (file >= 0)
    {
	retVal = NewConnection("", CK_LOCAL_SCIAN);
	((ConnectionPtr) retVal) -> sock = file;
	SetConnectionState(retVal, CS_WAIT_FOR_PASSWORD);
    }
    else
    {
	ReportError("NewLocalScianConnection", "bad file descriptor");
	retVal = NULLOBJ;
    }
    return retVal;
}

static ObjPtr ConnectToProcess(process)
ObjPtr process;
/*Method to connect to a process*/
{
    ThingListPtr runner;
    ObjPtr ownerConnection;
    ObjPtr connection = NULLOBJ;
    ObjPtr var;
    char *name;
    int port;

    /*First see if there is already a process in connection*/
    runner = LISTOF(allConnections);
    while (runner)
    {
	if (GetVar(runner -> thing, OWNERPROCESS) == process)
	{
	    connection = runner -> thing;
	    break;
	}
	runner = runner -> next;
    }

    if (!connection)
    {
	/*Have to make the connection.  Get machine name from owner connection*/
	ownerConnection = GetVar(process, OWNERCONNECTION);
	if (!ownerConnection)
	{
	    return ObjFalse;
	}

	var = GetStringVar("ConnectToProcess", ownerConnection, NAME);
	if (!var)
	{
	    return ObjFalse;
	}
	name = GetString(var);

	var = GetIntVar("ConnectToProcess", process, SOCKETID);
	if (!var)
	{
	    return ObjFalse;
	}
	port = GetInt(var);

	connection = NewRemoteScianConnection(name, port, process);
	SetConnectionState(connection, CS_NAME_ONLY);
    }
    NewControlWindow(process);

    return ObjTrue;
}

#ifdef PROTO
void DoEnablePublication(void)
#else
void DoEnablePublication()
#endif
/*Enables publication*/
{
    NewLocalDaemonConnection();
}

static ObjPtr ReplyConnection(dummy, replyText)
ObjPtr dummy;
char *replyText;
/*Makes a new remote daemon connection to machine replyText*/
{
    NewRemoteDaemonConnection(replyText);
}

void DoConnectToComputer(void)
/*Initiates a connection to another computer by asking the user for an
  address and then trying to connect.*/
{
    WinInfoPtr askWindow;
    char hostName[200];

    gethostname(hostName, 199);
    hostName[199] = 0;
    askWindow = AskUser((WinInfoPtr) NULLOBJ, "Enter the name or the IP address of the computer to connect:",
		 (FuncTyp) ReplyConnection, 
		lastComputer ? lastComputer : hostName);
    if (askWindow) SetVar((ObjPtr) askWindow, HELPSTRING,
	NewString("In order to connect to a computer, you must first enter \
its internet name (e.g. foo.bar.moo.cow.edu) or its internet address (e.g. \
128.182.2.255).  Enter the name or address here and press OK to connect to \
the computer.  Press Cancel to cancel the connection."));
}

#ifdef PROTO
void CloseConnection(ObjPtr connection)
#else
void CloseConnection(connection)
ObjPtr connection;
#endif
{
    if (OBJTYPE(connection -> flags) == CONNECTION)
    {
	ObjPtr var;
	int connectionKind;
	int sock;

	sock = (((ConnectionPtr) connection) -> sock);
	var = GetIntVar("CleanupConnection", connection, CONNECTIONKIND);

	connectionKind = GetInt(var);

	if (connectionKind == CK_LOCAL_DAEMON)
	{
	    if (sock >= 0)
	    {
	            /*I am not here*/
	            sprintf(tempStr, "%s %d\n", ND_UNREG_PROCESS, getpid());
		    writen(sock, tempStr, strlen(tempStr));
		    sleep(1);
	    }
	}

	/*Say bye*/
	if (sock >= 0 && (connectionKind == CK_LOCAL_DAEMON ||
			   connectionKind == CK_REMOTE_DAEMON))
	{
	    /*Send a close*/
	    sprintf(tempStr, "%s\n", ND_CLOSE);
	    writen(sock, tempStr, strlen(tempStr));
	    sleep(1);
	}

	if (sock >= 0)
	{
	    close(sock);
	}
	else if (((ConnectionPtr) connection) -> template >= 0)
	{
	    close(((ConnectionPtr) connection) -> template);
	}
	((ConnectionPtr) connection) -> sock = -1;
	((ConnectionPtr) connection) -> template = -1;
    }
}

ObjPtr CleanupConnection(connection)
ObjPtr connection;
/*Clean up a connection*/
{
    if (OBJTYPE(connection -> flags) == CONNECTION)
    {
	CloseConnection(connection);
    }

    return ObjTrue;
}

#ifdef MESGSTATS
#define NUMMESGSTATS 17

static struct {
    int inCount;
    int outCount;
    char *cmd;
    char *cmdName;
} mesgStats[NUMMESGSTATS] = {{0, 0, NO_MESG_OBJECT, "object"},
/*  2 */		   {0, 0, NO_MESG_SEND_OBJECT, "send object"},
/*  3 */		   {0, 0, NO_MESG_DELETE_OBJECT, "delete object"},
/*  4 */		   {0, 0, NO_MESG_MAKEVAR, "makevar"},
/*  5 */		   {0, 0, NO_MESG_DIDMAKEVAR, "did makevar"},
/*  6 */		   {0, 0, NO_MESG_DIDNTMAKEVAR, "didn\'t makevar"},
/*  7 */		   {0, 0, NO_MESG_GETVAR, "get var"},
/*  8 */		   {0, 0, NO_MESG_GETELEMENT, "get element"},
/*  9 */		   {0, 0, NO_MESG_GOTVAR, "got var"},
/* 10 */		   {0, 0, NO_MESG_GOTELEMENT, "got element"},
/* 11 */		   {0, 0, NO_MESG_UPDATEVAR, "update var"},
/* 12 */		   {0, 0, NO_MESG_UPDATEVAR_ID, "update var + id"},
/* 13 */		   {0, 0, NO_MESG_UPDATEVAR_SEND, "update var + send"},
/* 14 */		   {0, 0, NO_MESG_MACHINE_IRIS4D, "machine iris 4d"},
/* 15 */		   {0, 0, NO_MESG_MACHINE_RS6000, "machine rs6000"},
/* 16 */		   {0, 0, NO_MESG_MACHINE_UNKNOWN, "machine unknown"},
/* 17 */		   {0, 0, "OTHR", "other"}};

#endif

#ifdef PROTO
void SendSocketCommandLater(ObjPtr connection, char *command)
#else
void SendSocketCommandLater(connection, command)
ObjPtr connection;
char *command;
#endif
/*Sends command to connection later*/
{
    ObjPtr cmdQueue;
    int i;

    cmdQueue = GetVar(connection, OUTPUTQUEUE);
    if (!cmdQueue)
    {
	cmdQueue = NewList();
    }

    PostfixList(cmdQueue, NewString(command));
    SetVar(connection, OUTPUTQUEUE, cmdQueue);

#ifdef MESGSTATS
    for (i = 0; i < NUMMESGSTATS; ++i)
    {
	if (i == NUMMESGSTATS - 1 || CMD(command) == CMD(mesgStats[i].cmd))
	{
	    ++mesgStats[i].outCount;
	    break;
	}
    }
#endif
}

void SendObjectLater(connection, obj)
ObjPtr connection, obj;
{
    ObjPtr cmdQueue, sentList;

    cmdQueue = GetVar(connection, OUTPUTQUEUE);
    if (!cmdQueue)
    {
	cmdQueue = NewList();
    }

    PostfixList(cmdQueue, obj);
    SetVar(connection, OUTPUTQUEUE, cmdQueue);

#ifdef NEWSTYLEUPDATES
    if (!ObjectWasSent(obj, connection))
    {
	sentList = GetVar(connection, ALLSENTLIST);
	if (!sentList || !IsList(sentList))
	{
	    sentList = NewList();
	}
	PrefixList(sentList, obj);
	SetVar(connection, ALLSENTLIST, sentList);
    }
    else
    {
	fprintf(stderr, "hmm.. resending something..\n");
    }
#endif
}

static ObjPtr ShowProcessControls(process, windowName)
ObjPtr process;
char *windowName;
/*Makes a new control window to control a process */
{
    WinInfoPtr controlWindow;
    ObjPtr var;
    ObjPtr panel;
    ObjPtr corral;
    ObjPtr contents;
    WinInfoPtr dialogExists;

    dialogExists = DialogExists((WinInfoPtr) process, NewString("Controls"));
    controlWindow = GetDialog((WinInfoPtr) process, NewString("Controls"), windowName, 
	PXWINWIDTH, PXWINHEIGHT, SCRWIDTH, SCRHEIGHT, WINDBUF + WINFIXEDSIZE);
    
    if (!dialogExists)
    {
	ObjPtr connection, textBox, checkBox, icon, palette, name, button, sw;
	int l, r, b, t, bw;
	int left, right, bottom, top;
	char *s;
	ObjPtr allObjects;
	ThingListPtr runner;

	SetVar((ObjPtr) controlWindow, REPOBJ, process);

	/*Set help string*/
	SetVar((ObjPtr) controlWindow, HELPSTRING, NewString("This window \
shows status and running processes for a remote network connection."));
	ContentsExpectWindowSize(controlWindow, PXWINWIDTH, PXWINHEIGHT);

	l = 0;
	r = PXWINWIDTH;
	b = 0;
	t = PXWINHEIGHT;

	/*Add in a panel*/
	panel = NewPanel(greyPanelClass, 0, PXWINWIDTH, 0, PXWINHEIGHT);
	if (!panel)
	{
	    return ObjFalse;
	}
	contents = GetVar((ObjPtr) controlWindow, CONTENTS);
	PrefixList(contents, panel);
	SetVar(panel, PARENT, (ObjPtr) controlWindow);
	SetVar(panel, STICKINESS, NewInt(STICKYLEFT + STICKYRIGHT +
				     STICKYBOTTOM + STICKYTOP));
	contents = GetVar(panel, CONTENTS);

	/*Make an icon corral*/
	corral = NewIconCorral(NULLOBJ, l + MINORBORDER, r - MINORBORDER, b + 3 * MINORBORDER + 2 * BUTTONHEIGHT, t - MINORBORDER, BARRIGHT + BARBOTTOM);
	SetVar(corral, STICKINESS, NewInt(STICKYLEFT + STICKYRIGHT +
				     STICKYBOTTOM + STICKYTOP));
	SetVar(corral, TOPDOWN, ObjTrue);
	SetVar((ObjPtr) controlWindow, CORRAL, corral);
	SetVar(corral, NAME, NewString("Process Objects Corral"));
	SetVar(corral, HELPSTRING,
	NewString("This corral contains icons for all the objects available through \
a process connection.  You can visualize, show the controls of, or modify the datasets by selecting \
some of them and pressing the buttons at the bottom of the window.  You can delete \
datasets by choosing Delete from the Object menu.  You can also open new datasets by dragging icons from \
the file window into this corral."));
	PrefixList(contents, corral);
	SetVar(corral, PARENT, panel);

	/*Drop the objects in it*/
	allObjects = GetVar(process, ALLADVERTISED);
	if (!allObjects)
	{
	    allObjects = NewList();
	    SetVar(process, ALLADVERTISED, allObjects);
	}

	runner = LISTOF(allObjects);
	while (runner)
	{
	    icon = GetVar(runner -> thing, DEFAULTICON);
	    if (icon)
	    {
		icon = NewObject(icon, 0L);
	    }
	    else
	    {
		icon = NewIcon(0, 0,ICONQUESTION, "?");
	    }
	    SetVar(icon, REPOBJ, runner -> thing);
	    SetVar(icon, ICONLOC, NULLOBJ);
	    SetVar(icon, CORRAL, corral);
	    DropIconInCorral(corral, icon);
	    runner = runner -> next;
	}

	l += MINORBORDER;
	r -= MINORBORDER;
	b += 2 * MINORBORDER + BUTTONHEIGHT;
	t = b + BUTTONHEIGHT;
	bw = (r - l - MINORBORDER) / 2;

	/*Make a visualize button*/
	button = NewFunctionButton(controlWindow,
		    l, l + bw,
		    b, b + BUTTONHEIGHT, OF_VISUALIZE); 
	if (button)
	{
	    SetVar(button, PARENT, panel);
	    SetVar(button, STICKINESS, NewInt(STICKYBOTTOM + STICKYLEFT + FLOATINGRIGHT));
	    PrefixList(contents, button);
	}
	    
	/*Make a visualize as... button*/
	button = NewFunctionButton(controlWindow,
		    r - bw, r,
		    b, b + BUTTONHEIGHT, OF_VISUALIZE_AS); 
	if (button)
	{
	    SetVar(button, PARENT, panel);
	    SetVar(button, STICKINESS, NewInt(STICKYBOTTOM + FLOATINGLEFT + STICKYRIGHT));
	    PrefixList(contents, button);
	}

	t = b - MINORBORDER;
	b = t - BUTTONHEIGHT;
	/*Make a show info button*/
	button = NewFunctionButton(controlWindow,
		    l, l + bw, 
		    b, b + BUTTONHEIGHT, OF_SHOW_CONTROLS); 
	if (button)
	{
	    SetVar(button, PARENT, panel);
	    SetVar(button, STICKINESS, NewInt(STICKYBOTTOM + STICKYLEFT + FLOATINGRIGHT));
	    PrefixList(contents, button);
	}

	/*Make a modify button*/
	button = NewFunctionButton(controlWindow, 
		    r - bw, r,
		    b, b + BUTTONHEIGHT, OF_MODIFY);
	if (button)
	{
	    SetVar(button, PARENT, panel);
	    SetVar(button, STICKINESS, NewInt(STICKYBOTTOM + FLOATINGLEFT + STICKYRIGHT));
	    PrefixList(contents, button);
	}
    }
    return ObjTrue;
}

static ObjPtr ShowConnectionControls(connection, ownerWindow, windowName)
ObjPtr connection;
WinInfoPtr ownerWindow;
char *windowName;
/*Makes a new control window to control a connection*/
{
    WinInfoPtr controlWindow;
    ObjPtr var;
    ObjPtr panel;
    ObjPtr corral;
    ObjPtr contents;
    WinInfoPtr dialogExists;
    char hostName[200];

    dialogExists = DialogExists((WinInfoPtr) connection, NewString("Controls"));
    controlWindow = GetDialog((WinInfoPtr) connection, NewString("Controls"), windowName, 
	SCWINWIDTH, SCWINHEIGHT, SCRWIDTH, SCRHEIGHT, WINDBUF + WINFIXEDSIZE);
    
    if (!dialogExists)
    {
	ObjPtr textBox, checkBox, icon, palette, name, button, sw;
	int l, r, b, t;
	int left, right, bottom, top;
	char *s;
	SetVar((ObjPtr) controlWindow, REPOBJ, connection);

	/*Set help string*/
	SetVar((ObjPtr) controlWindow, HELPSTRING, NewString("This window \
shows status and running processes for a remote network connection."));
	ContentsExpectWindowSize(controlWindow, SCWINWIDTH, SCWINHEIGHT);
	l = 0;
	r = SCWINWIDTH;
	b = 0;
	t = SCWINHEIGHT;

	/*Add in a panel*/
	panel = NewPanel(greyPanelClass, 0, SCWINWIDTH, 0, SCWINHEIGHT);
	if (!panel)
	{
	    return ObjFalse;
	}
	contents = GetVar((ObjPtr) controlWindow, CONTENTS);
	PrefixList(contents, panel);
	SetVar(panel, PARENT, (ObjPtr) controlWindow);
	SetVar(panel, STICKINESS, NewInt(STICKYLEFT + STICKYRIGHT +
				     STICKYBOTTOM + STICKYTOP));

	contents = GetVar(panel, CONTENTS);
	
	/*Add in the icons*/

	left = l + MAJORBORDER;
	right = l + MAJORBORDER + SCGRAPHICWIDTH;
	top = t - MAJORBORDER;
	bottom = top - ICONSIZE;

	gethostname(hostName, 199);
	hostName[199] = 0;
	icon = NewIcon(left + ICONSIZE / 2, top - ICONSIZE / 2, ICONWORKSTATION,
		"SciAn");
	PrefixList(contents, icon);
	SetVar(icon, PARENT, (ObjPtr) controlWindow);
	SetVar(icon, STICKINESS, NewInt(STICKYLEFT + STICKYTOP));
	SetVar(icon, HELPSTRING, NewString("This icon represents the workstation running \
SciAn that you are using right now.  It's part of a graphic that shows that the window \
is a network connection window.  Other than that, it doesn't really do anything."));

	icon = NewIcon(right - ICONSIZE / 2, top - ICONSIZE / 2, ICONCOMPUTER,
		"Host");
	PrefixList(contents, icon);
	SetVar(icon, PARENT, (ObjPtr) controlWindow);
	SetVar(icon, STICKINESS, NewInt(STICKYLEFT + STICKYTOP));
	SetVar(icon, HELPSTRING, NewString("This icon represents the remote computer \
that you are connected to.  It's part of a graphic that shows that the window \
is a network connection window.  Other than that, it doesn't really do anything."));

	/*Put a switch between the icons*/
	sw = NewFlowLine(left + ICONSIZE + MINORBORDER, right - ICONSIZE - MINORBORDER, bottom, top, "Graphic Flow Line");
	PrefixList(contents, sw);
	SetVar(sw, PARENT, (ObjPtr) controlWindow);
	SetVar(sw, STICKINESS, NewInt(STICKYLEFT + STICKYTOP));

	/*Add the info description*/
	left = l + 2 * MAJORBORDER + SCGRAPHICWIDTH;
	right = r - MINORBORDER;
	top = t - MINORBORDER;
	bottom = top - SCTEXTHEIGHT;

	s = tempStr;
	sprintf(s, "Network Connection\nHost: ");
	while (*s) ++s;

	var = GetVar(connection, NAME);
	if (var)
	{
	    sprintf(s, "%s\n", GetString(var));
	}
	else
	{
	    sprintf(s, "Unknown\n");
	}
	while (*s) ++s;

	/*Create the text box*/
	textBox = NewTextBox(left, right, 
			 bottom, top,
			 0, "Info Text", tempStr);
	PrefixList(contents, textBox);
	SetVar(textBox, PARENT, panel);
	SetVar(textBox, TEXTFONT, NewString(SCFONT));
	SetVar(textBox, TEXTSIZE, NewInt(SCFONTSIZE));
	SetVar(textBox, STICKINESS, NewInt(STICKYLEFT + STICKYRIGHT + STICKYTOP));

	/*Make an icon corral*/
	top = bottom - MAJORBORDER;
	left = MINORBORDER;
	right = r - MINORBORDER;
	bottom = b + 2 * MINORBORDER + BUTTONHEIGHT;
	corral = NewIconCorral(NULLOBJ, left, right, bottom, top, BARRIGHT + BARBOTTOM);
	SetVar(corral, STICKINESS, NewInt(STICKYLEFT + STICKYRIGHT +
				     STICKYBOTTOM + STICKYTOP));
	SetVar(corral, TOPDOWN, ObjTrue);
	SetVar((ObjPtr) controlWindow, CORRAL, corral);
	SetVar(corral, NAME, NewString("Processes Corral"));
	SetVar(corral, HELPSTRING,
	    NewString("This corral contains icons for all the processes running on the \
host machine.  You can show the controls of a process by selecting it and pressing \
the Show Controls button."));
	PrefixList(contents, corral);
	SetVar(corral, PARENT, panel);

	bottom = b + MINORBORDER;
	top = bottom + BUTTONHEIGHT;
	right = (l + r) / 2;

	/*Make a show controls button*/
	button = NewFunctionButton(controlWindow,
		left, right, bottom, top, OF_CONNECT_TO_PROCESS); 
	if (button)
	{
	    SetVar(button, PARENT, panel);
	    SetVar(button, STICKINESS, NewInt(STICKYBOTTOM + STICKYLEFT + FLOATINGRIGHT));
	    PrefixList(contents, button);
	}
    }

    return (ObjPtr) controlWindow;
}
#endif

#ifdef SOCKETS
#ifdef PARANOID
int WFNOVCount = 0;
#endif
#endif

/* any vars of object that are gotten in here or in underlying calls **MUST**
 * have been set locally!
 */
Bool WaitForNetObjectElement(object, offset)
ObjPtr object;
unsigned long offset;
{
#ifdef SOCKETS
    ObjPtr connection, process, allObjects;
    ThingListPtr thing;
    ObjPtr oldThing;
    double time;
    Bool wasNetStub;

#ifdef DEBUG
fprintf(stderr, "waiting for object %lx, element %ld\n", object, offset);
#endif

#ifdef PARANOID
    if (++WFNOVCount > 1)
    {
	ReportError("WaitForNetObjectElement", "HEY! Multiple calls to WaitForNetObject* are active! Very Bad!");
    }
#endif

    if (!IsObjArray(object))
    {
	ReportError("WaitForNetObjectElement", "not an ObjArray");
    }
    else if (!(connection = GetVar(object, OWNERCONNECTION)))
    {
	ReportError("WaitForNetObjectElement", "network object has no OWNERCONNECTION");
    }
    else if(!(process = GetVar(connection, OWNERPROCESS)))
    {
	ReportError("WaitForNetObjectElement", "network object has no OWNERPROCESS");
    }
    else
    {
	ObjPtr *elements;

	elements = (ObjPtr *) ELEMENTS(object);

/* Always send it for now */
	    /* ReportError("WaitForNetObjectElement", "Object not already waiting!"); */
	    SendGetElementMessage(object, offset);

	time = WallClock();
	/* wasNetStub = NETSTUBFLAG == varNode -> remoteNetID; */
	wasNetStub = true;
	while (WallClock() < time + LONG_TIMEOUT && IsNetworkIDWaiting(process, (unsigned long) elements[offset]))
	{
	    while (IdleAllConnections())
		;
	    if (wasNetStub && (elements[offset] != (ObjPtr) NETSTUBFLAG))
	    {
		/* got results of GETVAR message, send get object msg if we don't already have it */
		if (elements[offset] && !FindRemoteObject(process, (unsigned long) elements[offset], false))
		{
		    SendSendObjectMessage(connection, (unsigned long) elements[offset]);
		    wasNetStub = false;
		}
	    }
	}
	if (WallClock() >= time + LONG_TIMEOUT)
	{
	    sprintf(tempStr, "timed out waiting for element %ld\n", offset);
	    ReportError("WaitForNetObjectElement", tempStr);
	    /* if (elements[offset] == (ObjPtr) NETSTUBFLAG) */
		elements[offset] == NULLOBJ;
/* exit(0); */
	}
	else
	{
#ifdef PARANOID
	    --WFNOVCount;
#endif

#ifdef DEBUG
	    fprintf(stderr, "done waiting for object %lx, element %ld (remoteNetID %ld), trying to find it now\n", object, offset, (unsigned long) elements[offset]);
#endif
	    elements[offset] = FindRemoteObject(process, (unsigned long) elements[offset], false);
	    return true;
	}
    }

#ifdef DEBUG
    fprintf(stderr, "Abnormal exit waiting for object %lx, offset %ld\n", object, offset);
#endif

#ifdef PARANOID
    --WFNOVCount;
#endif

#endif
    return false;
}

/* any vars of object that are gotten in here or in underlying calls **MUST**
 * have been set locally!
 */
Bool WaitForNetObjectVar(object, varNode)
ObjPtr object;
VarsPtr varNode;
{
#ifdef SOCKETS
    ObjPtr connection, process, allObjects;
    ThingListPtr thing;
    ObjPtr oldThing;
    double time;
    Bool wasNetStub;

#ifdef DEBUG
fprintf(stderr, "waiting for object %lx, var %ld (remoteNetID %ld)\n", object, varNode -> name, varNode -> remoteNetID);
#endif

#ifdef PARANOID
    if (++WFNOVCount > 1)
    {
	ReportError("WaitForNetObjectVar", "HEY! Multiple calls are active! Very Bad!");
    }
#endif

    connection = GetVar(object, OWNERCONNECTION);
    if (!(connection = GetVar(object, OWNERCONNECTION)))
    {
	ReportError("WaitForNetObjectVar", "network object has no OWNERCONNECTION");
    }
    else if(!(process = GetVar(connection, OWNERPROCESS)))
    {
	ReportError("WaitForNetObjectVar", "network object has no OWNERPROCESS");
    }
    else if (!varNode)
    {
	ReportError("WaitForNetObjectVar", "waiting for nothing!");
    }
    else
    {
	int firstTime = 1;

	if (!IsNetworkIDWaiting(process, varNode -> remoteNetID))
	{
	    ReportError("WaitForNetObjectVar", "Object not already waiting!");
	    SendGetVarMessage(object, varNode -> name);
	}

	time = WallClock();
	wasNetStub = NETSTUBFLAG == varNode -> remoteNetID;
	while (!IsDefunct(object) && WallClock() < time + LONG_TIMEOUT && IsNetworkIDWaiting(process, varNode -> remoteNetID))
	{
	    varNode = GetVarNode(object, varNode -> name);
	    while (IdleAllConnections())
		;
	    if (wasNetStub && varNode -> remoteNetID != NETSTUBFLAG)
	    {
		/* got results of GETVAR message, send get object msg if we don't already have it */
		if (varNode -> remoteNetID && !FindRemoteObject(process, varNode -> remoteNetID, false))
		{
		    SendSendObjectMessage(connection, varNode -> remoteNetID);
		    wasNetStub = false;
		}
	    }
	}

	if (IsDefunct(object))
	{
	    sprintf(tempStr, "target %lx was marked ISDEFUNCT while waiting for %ld", object, varNode -> name);
	    ReportError("WaitForNetObjectVar", tempStr);
	}
	else if (WallClock() >= time + LONG_TIMEOUT)
	{
	    sprintf(tempStr, "timed out waiting for var %ld", varNode -> name);
	    ReportError("WaitForNetObjectVar", tempStr);
/* exit(0); */
	}
	else
	{
#ifdef PARANOID
	    --WFNOVCount;
#endif

#ifdef DEBUG
	    fprintf(stderr, "done waiting for object %lx, var %ld (remoteNetID %ld)\n", object, varNode -> name, varNode -> remoteNetID);
#endif

	    return true;
	}
    }

#ifdef DEBUG
    fprintf(stderr, "Abnormal exit waiting for object %lx, var %ld (remoteNetID %ld)\n", object, varNode -> name, varNode -> remoteNetID);
#endif

#ifdef PARANOID
    --WFNOVCount;
#endif

#endif
    return false;
}

Bool WaitForNetObject(ObjPtr connection, unsigned long netid)
{
#ifdef SOCKETS
ObjPtr process;
double time;

#ifdef DEBUG
fprintf(stderr, "waiting for remoteNetID %ld\n", netid);
#endif

#ifdef PARANOID
    if (++WFNOVCount > 1)
    {
	ReportError("WaitForNetObject", "multiple calls are active! Very Bad!");
    }

    if (!netid)
    {
	ReportError("WaitForNetObject", "waiting for netid 0!");
    }
#endif

    process = GetVar(connection, OWNERPROCESS);

    if (!process)
    {
	ReportError("WaitForNetObject", "connection has no OWNERPROCESS");
    }
    else
    {
	if (!IsNetworkIDWaiting(process, netid))
	{
	    ReportError("WaitForNetObject", "Object not already waiting!");
	    SendSendObjectMessage(connection, netid);
	}

	time = WallClock();
	while (WallClock() < time + LONG_TIMEOUT && IsNetworkIDWaiting(process, netid))
	{
	    while (IdleAllConnections())
		;
	}
	if (WallClock() >= time + LONG_TIMEOUT)
	{
	    sprintf(tempStr, "timed out waiting for object, netid %ld\n", netid);
	    ReportError("WaitForNetObject", tempStr);
/* exit(0); */
	}
	else
	{
#ifdef PARANOID
	    --WFNOVCount;
#endif

#ifdef DEBUG
fprintf(stderr, "done waiting for remoteNetID %ld\n", netid);
#endif
	    return true;
	}
    }
#ifdef PARANOID
    --WFNOVCount;
#endif

#ifdef DEBUG
fprintf(stderr, "Abnormal exit waiting for remoteNetID %ld\n", netid);
#endif


#endif
    return false;
}

void InitSockets(void)
/*Initializes the socket system*/
{
#ifdef SOCKETS
    advertiseableClass = NewObject(NULLOBJ, 0L);
    SetMethod(advertiseableClass, ADVERTISE, AdvertiseObject);
    SetMethod(advertiseableClass, UNADVERTISE, UnadvertiseObject);
    allAdvertised = NewList();
    AddToReferenceList(advertiseableClass);
    AddToReferenceList(allAdvertised);

    connectionClass = NewObject(NULLOBJ, 0L);
    AddToReferenceList(connectionClass);
    SetMethod(connectionClass, TIMEOUT, TimeoutConnection);
    SetMethod(connectionClass, CLEANUP, CleanupConnection);
    SetMethod(connectionClass, NEWCTLWINDOW, ShowConnectionControls);

    /*Make class for processes*/
    processClass = NewObject(NULLOBJ, 0L);
    AddToReferenceList(processClass);
    SetMethod(processClass, NEWCTLWINDOW, ShowProcessControls);
    SetMethod(processClass, CONNECTTOPROCESS, ConnectToProcess);

    /*Make icons for process*/
    processIcon = NewIcon(10, 10, ICONPROCESS, "Process");
    AddToReferenceList(processIcon);

    allConnections = NewList();
    AddToReferenceList(allConnections);
#endif
}

void KillSockets(void)
/*Kills the socket system*/
{
#ifdef SOCKETS
    int i;

    RemoveFromReferenceList(allConnections);
    RemoveFromReferenceList(processIcon);
    RemoveFromReferenceList(processClass);
    RemoveFromReferenceList(connectionClass);
    if (lastComputer)
    {
	free(lastComputer);
	lastComputer = 0;
    }
    RemoveFromReferenceList(allAdvertised);
    RemoveFromReferenceList(advertiseableClass);

#ifdef MESGSTATS
    for (i = 0; i < NUMMESGSTATS; ++i)
    {
	fprintf(stderr, "%5d %s\n",mesgStats[i].outCount,mesgStats[i].cmdName);
    }
#endif
#endif
}
