/*****************************************************************************
*   Sockets routines to handle socket io of an objects.			     *
*									     *
* Written by:  Gershon Elber				Ver 0.1, June 1993.  *
*****************************************************************************/

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

#ifdef __UNIX__
#if (defined(ultrix) && defined(mips)) || defined(_AIX) || defined(sgi)
#    include <fcntl.h>
#else
#    include <sys/fcntl.h>
#endif /* (ultrix && mips) || _AIX || sgi */
#include <signal.h>
#include <sys/socket.h>
#if defined(__hpux) || defined(sun)
#    include <sys/file.h>
#endif /* __hpux || sun */
#include <netinet/in.h>
#include <netdb.h>
#endif /* __UNIX__ */

#ifdef __WINNT__
#include <stdlib.h>
#include <windows.h>
#include <winsock.h>
#include <io.h>
#endif /* __WINNT__ */

#ifdef AMIGA
#ifdef __SASC
#include <dos.h>
#endif
#include <string.h>
#include <exec/types.h>
#include <exec/ports.h>
#include <exec/memory.h>
#include <dos/dostags.h>
#include <proto/dos.h>
#include <proto/exec.h>
#endif /* AMIGA */

#include "irit_sm.h"
#include "prsr_loc.h"
#include "irit_soc.h"

#ifdef OS2GCC
#define INCL_DOSPROCESS
#include <os2.h>
#endif /* OS2GCC */

static void SocketPrintError(char *Str);
#ifdef __UNIX__
#if defined(_AIX) || defined(__hpux) || defined(sun)
typedef void (*SignalFuncType)(int); 
#else
typedef void (*SignalFuncType)(void); 
#endif /* _AIX || __hpux || sun */
static void SocNoConnectionHandler(void);
static void SocNoConnectionDummyHandler(void);
#endif /* __UNIX__ */

/*****************************************************************************
* DESCRIPTION:                                                               *
* IO error print routine.						     *
*                                                                            *
* PARAMETERS:                                                                *
*   Str:      To output to stderr as a message.                              *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void SocketPrintError(char *Str)
{
#   if defined(__UNIX__) || defined(OS2GCC) || defined(AMIGA)
	perror(Str);
#   endif /* __UNIX__ || OS2GCC */
#   ifdef __WINNT__
	fprintf(stderr, "iritserver: %s error %d\n", Str, WSAGetLastError());
#   endif /* __WINNT__ */
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Writes a single char to client's socket.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   Handler: The socket info handler index.                                  M
*   c:       Character to write.                                             M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   SocWriteChar, ipc                                                        M
*****************************************************************************/
void SocWriteChar(int Handler, char c)
{
#if defined(__UNIX__) || defined(__WINNT__)
    int i;

    if (_IPStream[Handler].CommuSoc <= 0) {
	IritPrsrFatalError("Attempt to write to a closed (broken!?) socket");
	return;
    }

    while ((i = send(_IPStream[Handler].CommuSoc, &c, 1, 0)) != 1) {
	if (i < 0)			       /* Lost connection probably. */
	    SocServerCloseSocket(Handler);
	IritSleep(10);
    }
#endif /* __UNIX__ || __WINNT__ */
#ifdef OS2GCC
    ULONG BytesWritten;
    ULONG rc;

    rc = DosWrite(_IPStream[Handler].pipIrit, &c, 1, &BytesWritten);
#endif /* OS2GCC */
#ifdef AMIGA
    struct IritMessage msg;

    msg.msg.mn_Node.ln_Type = NT_MESSAGE;
    msg.msg.mn_ReplyPort = _IPStream[Handler].ReplySoc;
    msg.nbytes = 1;
    msg.txt[0] = c;
    msg.msg.mn_Length = sizeof(struct IritMessage) - LINE_LEN_LONG + msg.nbytes;
    PutMsg(_IPStream[Handler].CommuSoc, (struct Message *) &msg);
    WaitPort(_IPStream[Handler].ReplySoc);
    (void)GetMsg(_IPStream[Handler].ReplySoc);
#endif /* AMIGA */
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Writes a single line of line length characters.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   Handler: The socket info handler index.                                  M
*   Line:       Line to write.                                               M
*   LineLen:    Length of line to write.                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   SocWriteLine, ipc                                                        M
*****************************************************************************/
void SocWriteLine(int Handler, char *Line, int LineLen)
{
#if defined(__UNIX__) || defined(__WINNT__)
    int i;

    if (_IPStream[Handler].CommuSoc <= 0) {
	IritPrsrFatalError("Attempt to write to a closed (broken!?) socket");
	return;
    }

    while ((i = send(_IPStream[Handler].CommuSoc, Line,
		                                      LineLen, 0)) < LineLen) {
	if (i < 0)			       /* Lost connection probably. */
	    SocServerCloseSocket(Handler);
	IritSleep(10);
	LineLen = LineLen - i;
	Line = &Line[i];
    }
#endif /* __UNIX__ || __WINNT__ */
#ifdef OS2GCC
    ULONG BytesWritten;
    ULONG rc;

    rc = DosWrite(_IPStream[Handler].pipIrit, Line,
		  LineLen, &BytesWritten);
#endif /* OS2GCC */
#ifdef AMIGA
    struct IritMessage msg;

    msg.msg.mn_Node.ln_Type = NT_MESSAGE;
    msg.msg.mn_ReplyPort = _IPStream[Handler].ReplySoc;
    for ( ; LineLen>0; LineLen-=msg.nbytes, Line+=msg.nbytes) {
	msg.nbytes = (LineLen > LINE_LEN_LONG) ? LINE_LEN_LONG : LineLen;
	memcpy(msg.txt, Line, msg.nbytes);
	msg.msg.mn_Length =
	    sizeof(struct IritMessage) - LINE_LEN_LONG + msg.nbytes;
	PutMsg(_IPStream[Handler].CommuSoc, (struct Message *) &msg);
	WaitPort(_IPStream[Handler].ReplySoc);
	GetMsg(_IPStream[Handler].ReplySoc);
    }
#endif /* AMIGA */
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Creates a server socket. Returns handler if successful. Also sets the      M
* IRIT_SERVER_PORT environment variable to the allocated socket.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   BinaryIPC:	 Do we want to communicate text or binary?                   M
*   Read:	 Is this socket for read (TRUE) or write (FALSE).            M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:         Non negative handler if successful, -1 otherwise.           M
*                                                                            *
* KEYWORDS:                                                                  M
*   SocServerCreateSocket, ipc                                               M
*****************************************************************************/
int SocServerCreateSocket(int BinaryIPC, int Read)
{
    int Handler;
    static char Line[LINE_LEN];

#if defined(__UNIX__) || defined(__WINNT__)
    int i, s;
    struct sockaddr_in Sain;

    ZAP_MEM(&Sain, sizeof(struct sockaddr_in));
    Sain.sin_addr.s_addr = htonl(INADDR_ANY);
    Sain.sin_family = AF_INET;
#endif /* __UNIX__ || __WINNT__ */

#if defined(__UNIX__) || defined(__WINNT__)
#ifdef __WINNT__
    {
	WSADATA WSAData;

	if ((s = WSAStartup(MAKEWORD(1, 1), &WSAData)) != 0 ||
	    LOBYTE(WSAData.wVersion) != 1 ||
	    HIBYTE(WSAData.wVersion) != 1) {
	    fprintf(stderr, "iritserver: WSAStartup: error %d\n",
		    WSAGetLastError());
	    return -1;
	}
    }
#endif

    /* Create the socket, make it nonblocking if Read, and bind. */
    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
	fprintf(stderr, "iritserver: socket");
	return -1;
    }

    if (Read) {
#ifdef __UNIX__
#ifdef __hpux
        if (fcntl(s, F_SETFL, O_NDELAY) < 0) {
#else
        if (fcntl(s, F_SETFL, FNDELAY) < 0) {
#endif /* __hpux */
	    SocketPrintError("iritclient: fcntl");
	    return -1;
        }
#endif /* __UNIX__ */
    }

    Sain.sin_port = 0;
    if (bind(s, (struct sockaddr *) &Sain, sizeof(struct sockaddr_in)) < 0) {
	fprintf(stderr, "iritserver: bind");
	return -1;
    }

    if (listen(s, 5) < 0) {
	fprintf(stderr, "iritserver: listen");
	return -1;
    }

    i = sizeof(struct sockaddr_in);
    if (getsockname(s, (struct sockaddr *) &Sain, &i) < 0) {
	fprintf(stderr, "iritserver: getsockname");
	return -1;
    }

    sprintf(Line, "IRIT_SERVER_PORT=%d", Sain.sin_port);
    putenv(Line);

#ifdef __UNIX__
    signal(SIGPIPE, (SignalFuncType) SocNoConnectionHandler);
#endif /* __UNIX__ */

    Handler = IritPrsrOpenStreamFromSocket(0, FALSE, BinaryIPC);
    _IPStream[Handler].MasterSoc = s;
#endif /* __UNIX__ || __WINNT__ */

#ifdef OS2GCC
    static int
	PipeCount = 0;
    HPIPE pipIrit;
    ULONG rc;

    sprintf(Line, "\\pipe\\irit_%d", ++PipeCount);

    rc = DosCreateNPipe(Line,
			&pipIrit,
			NP_ACCESS_DUPLEX | NP_NOWRITEBEHIND,
			NP_WAIT | NP_TYPE_BYTE | NP_READMODE_BYTE | 1,
			IRIT_PIPE_BUFFER_SIZE,
			IRIT_PIPE_BUFFER_SIZE,
			0);

    if (rc != 0) {
	fprintf(stderr,
		"DosCreateNPipe: error in creating %s\n", Line);
	return FALSE;
    }

    Handler = IritPrsrOpenStreamFromSocket(0, FALSE, BinaryIPC);
    _IPStream[Handler].pipIrit = pipIrit;

    sprintf(Line, "IRIT_SERVER_PORT=\\pipe\\irit_%d", PipeCount);
    putenv(Line);
#endif /* OS2GCC */
#ifdef AMIGA
    static int portno = 0;
    char *name;

    Handler = IritPrsrOpenStreamFromSocket(0, FALSE, BinaryIPC);
    _IPStream[Handler].CommuSoc = CreateMsgPort();
    if (_IPStream[Handler].CommuSoc) {
	name = malloc(16);
	sprintf(name, "IRIT%d", portno++);
	_IPStream[Handler].CommuSoc->mp_Node.ln_Name = name;
	_IPStream[Handler].CommuSoc->mp_Node.ln_Pri = 0;
	AddPort(_IPStream[Handler].CommuSoc);
	_IPStream[Handler].ReplySoc = CreateMsgPort();
	if (!_IPStream[Handler].ReplySoc) {
	    RemPort(_IPStream[Handler].CommuSoc);
	    DeleteMsgPort(_IPStream[Handler].CommuSoc);
	    _IPStream[Handler].CommuSoc = NULL;
	    return FALSE;
	}
    } else {
	return FALSE;
    }
    sprintf(Line, "IRIT_SERVER_PORT=%s", name);
    putenv(Line);
#endif /* AMIGA */

    return Handler;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Accepts a client socket connection.                                        M
*   This function blocks until a connection with a client is established.    M
*                                                                            *
* PARAMETERS:                                                                M
*   Handler: The socket info handler index.                                  M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:     TRUE if succesful, FALSE otherwise.                             M
*                                                                            *
* KEYWORDS:                                                                  M
*   SocServerAcceptConnection, ipc                                           M
*****************************************************************************/
int SocServerAcceptConnection(int Handler)
{
#if defined(__UNIX__) || defined(__WINNT__)
    struct sockaddr_in ClientSain;
    int i = SOC_TIME_OUT,
	Len = sizeof(ClientSain);

    while (i-- > 0 &&
	   (_IPStream[Handler].CommuSoc =
	    accept(_IPStream[Handler].MasterSoc,
		   (struct sockaddr *) &ClientSain, &Len)) < 0)
	IritSleep(10);

    return i > 0;
#endif /* __UNIX__ || __WINNT__ */
#ifdef OS2GCC
    ULONG rc;

    if ((rc = DosConnectNPipe(_IPStream[Handler].pipIrit)) != 0) {
	fprintf(stderr,
		"DosConnectNPipe: error in connection %s\n",
		getenv("IRIT_SERVER_PORT"));
	return FALSE;
    }

    _IPStream[Handler].CommuSoc = 1;

    return TRUE;
#endif /* OS2GCC */
#ifdef AMIGA
    int len = -1;
    char buf[4];
    static int timescalled = 0;

    timescalled++;
    buf[0] = '\0';
    if (timescalled % 2) {
	while (len < 0) {
	    len = GetVar(CLIENT_VAR, buf, sizeof(buf), GVF_GLOBAL_ONLY);
	    if (len < 0) {
#ifdef __SASC
		chkabort();
#endif
		Delay(50L);
	    }
	}
	DeleteVar(CLIENT_VAR, GVF_GLOBAL_ONLY);
    }
    return TRUE;
#endif
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Closes a server socket/connection.                                         M
*                                                                            *
* PARAMETERS:                                                                M
*   Handler: The socket info handler index.                                  M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   SocServerCloseSocket, ipc                                                M
*****************************************************************************/
void SocServerCloseSocket(int Handler)
{
#ifndef AMIGA
    if (_IPStream[Handler].CommuSoc > 0) {
#ifdef __UNIX__
	char Line[LINE_LEN];

	/* Force the system to realize that the connection is down. This is */
	/* a kluge to solve the time out problem that happens under unix.   */
	IritSleep(1000);
	signal(SIGPIPE, (SignalFuncType) SocNoConnectionDummyHandler);
	send(_IPStream[Handler].CommuSoc, Line, LINE_LEN, 0);

	if (close(_IPStream[Handler].CommuSoc) != 0)
	    SocketPrintError("iritserver: close");
#endif /* __UNIX__ */
#ifdef __WINNT__
	closesocket(_IPStream[Handler].CommuSoc);
#endif /* __WINNT__ */
#ifdef OS2GCC
	DosClose(_IPStream[Handler].pipIrit);
#endif /* OS2GCC */
    }
#else /* AMIGA */
    if (_IPStream[Handler].CommuSoc) {
	RemPort(_IPStream[Handler].CommuSoc);
	free(_IPStream[Handler].CommuSoc->mp_Node.ln_Name);
	_IPStream[Handler].CommuSoc->mp_Node.ln_Name = NULL;
	DeleteMsgPort(_IPStream[Handler].CommuSoc);
	_IPStream[Handler].CommuSoc = NULL;
	if (_IPStream[Handler].ReplySoc) {
	    DeleteMsgPort(_IPStream[Handler].ReplySoc);
	}
    }
#endif /* AMIGA */
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns TRUE if connection is alive and active.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   Handler: The socket info handler index.                                  M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:         TRUE if connection to client exists.                        M
*                                                                            *
* KEYWORDS:                                                                  M
*   SocServerActive, ipc                                                     M
*****************************************************************************/
int SocServerActive(int Handler)
{
#ifndef AMIGA
    return _IPStream[Handler].CommuSoc > 0;
#else
    return _IPStream[Handler].CommuSoc != 0;
#endif /* AMIGA */
}

#ifdef __UNIX__
/*****************************************************************************
* DESCRIPTION:                                                               *
* Cleans the server socket in case of a broken pipe.			     *
*                                                                            *
* PARAMETERS:                                                                *
*   None                                                                     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void SocNoConnectionHandler(void)
{
    signal(SIGPIPE, (SignalFuncType) SocNoConnectionHandler);
    _IPParserAbort(IP_ERR_SOCKET_BROKEN, "");
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Dummy handler.                                                             *
*                                                                            *
* PARAMETERS:                                                                *
*   None                                                                     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void SocNoConnectionDummyHandler(void)
{
}
#endif /* __UNIX__ */

/*****************************************************************************
* DESCRIPTION:                                                               M
* Attempts to write an object to a socket.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   Handler: The socket info handler index.                                  M
*   PObj:     Object to write to the client's socket.                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   SocWriteOneObject, ipc                                                   M
*****************************************************************************/
void SocWriteOneObject(int Handler, IPObjectStruct *PObj)
{
    char *ErrorMsg;

    if (IP_IS_UNDEF_OBJ(PObj)) {
	fprintf(stderr, "Socket: Attempt to write an undefined object.\n");
	return;
    }
    if (IP_IS_POLY_OBJ(PObj) && PObj -> U.Pl == NULL) {
	fprintf(stderr, "Socket: Attempt to write an empty poly object.\n");
	return;
    }

    IritPrsrPutObjectToHandler(Handler, PObj);

    if (IritPrsrParseError(_IPStream[Handler].LineNum, &ErrorMsg)) {
	fprintf(stderr, "Socket: %s\n", ErrorMsg);
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Executes the given program and connect to it io ports.                   M
*                                                                            *
* PARAMETERS:                                                                M
*   Program:    Name of program to execute. Name can be NULL, in which the   M
*               user is prompt to execute the program manually.		     M
*   PrgmInput:  A handler to the Program's input channel.		     M
*   PrgmOutput: A handler to the Program's output channel.		     M
*   IsBinary:   If TRUE sets channels to binary, if FALSE channels are text. M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:        TRUE, if succesful, FALSE otherwise.                         M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrSrvrExecAndConnect                                               M
*****************************************************************************/
int IritPrsrSrvrExecAndConnect(char *Program,
			       int *PrgmInput,
			       int *PrgmOutput,
			       int IsBinary)
{
    int Input, Output;
    char Line[LINE_LEN];
    IPObjectStruct *PObj;
#ifdef AMIGA
    char buf[4];

    buf[0] = '\0';
    SetVar(SERVER_VAR, buf, -1, GVF_GLOBAL_ONLY);
#endif
    if ((Input = SocServerCreateSocket(IsBinary, FALSE)) < 0)
	return FALSE;

    if (Program != NULL && strlen(Program) > 0) {
#if defined(__UNIX__)
	char DisplayCommand[LINE_LEN];
	
	sprintf(DisplayCommand, "%s &", Program);
	if (system(DisplayCommand) < 0) {
	    /* This, in fact, is not working since system does not return    */
	    /* error code of display device.				     */
	    sprintf(Line, "system: Failed to fork \"%s\"\n", Program);
	    IritPrsrFatalError(Line);
	    return FALSE;
	}
#else
#if defined(__WINNT__) || defined(OS2GCC)
	char DisplayCommand[LINE_LEN];

	sprintf(DisplayCommand, "start %s", Program);
	system(DisplayCommand);
#else
#ifdef AMIGA
	int HaveDisplay;
	BPTR f;

	f = Open("*", MODE_OLDFILE);
	HaveDisplay = !SystemTags(Program, SYS_Input,  NULL,
					   SYS_Output, f,
					   SYS_Asynch, TRUE,
					   TAG_END);
	/* SystemTags will always return success for programs run
	   asynchronously, so wait a bit and then check if the synchronization
	   environment variable created by the client exists. If not, we
	   probably failed to start the program.
	*/
	Delay(150L);
	if (GetVar(CLIENT_VAR, buf, sizeof(buf), GVF_GLOBAL_ONLY) < 0) {
	    HaveDisplay = FALSE;
	}
	if (!HaveDisplay){
	    fprintf(stderr,
		    "Irit: Startup your program - I am waiting...\n\nsetenv IRIT_SERVER_PORT %s\n\n",
		    getenv("IRIT_SERVER_PORT"));
	}
#endif /* AMIGA */
#endif /* __WINNT__ || OS2GCC */
#endif /* __UNIX__ || AMIGA */
    }
    else
	fprintf(stderr,
		"Irit: Startup your program - I am waiting...\n\nsetenv IRIT_SERVER_PORT %s\n\n",
		getenv("IRIT_SERVER_PORT"));

    if (!SocServerAcceptConnection(Input))
	return FALSE;

    /* Send on open channel the name of the port for the other direction. */
    if ((Output = SocServerCreateSocket(IsBinary, TRUE)) < 0)
	return FALSE;

    sprintf(Line, "IRIT_SERVER_PORT=%s", getenv("IRIT_SERVER_PORT"));
    PObj = GenSTRObject(Line);
    SocWriteOneObject(Input, PObj);
    IPFreeObject(PObj);

    if (!SocServerAcceptConnection(Output))
	return FALSE;

    fprintf(stderr, "Connection established to \"%s\"\n",
	    Program == NULL ? "?" : Program);

    *PrgmInput = Input;
    *PrgmOutput = Output;

    return TRUE;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Optionally kill and close channels to another process.                   M
*                                                                            *
* PARAMETERS:                                                                M
*   Kill:       If TRUE, send a KILL message to the other process.           M
*   PrgmInput:  A handler to the Program's input channel.		     M
*   PrgmOutput: A handler to the Program's output channel.		     M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:        TRUE, if succesful, FALSE otherwise.                         M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrSrvrKillAndDisConnect                                            M
*****************************************************************************/
int IritPrsrSrvrKillAndDisConnect(int Kill, int PrgmInput, int PrgmOutput)
{
    IPObjectStruct
	*PObj = GenStrObject("COMMAND_", Kill ? "EXIT" : "DISCONNECT", NULL);

    SocWriteOneObject(PrgmInput, PObj);
    IPFreeObject(PObj);

    SocServerCloseSocket(PrgmInput);
    SocServerCloseSocket(PrgmOutput);

    return TRUE;
}

#ifdef DEBUG_SERVER

/*****************************************************************************
* DESCRIPTION:                                                               *
* Simple test for the server - reads file and dumps one object every key.    *
*****************************************************************************/
void main(int argc, char ** argv)
{
    int Handler, PrgmInput, PrgmOutput;
    FILE
	*f = argc == 2 ? fopen(argv[1], "r") : stdin;
    IPObjectStruct *PObj,
        *PObjClear = IPAllocObject("_COMMAND_", IP_OBJ_STRING, NULL),
	*PObjs = IritPrsrGetObjects(f);

    if (argc == 2)
	fclose(f);
    strcpy(PObjClear -> U.Str, "CLEAR");

    Handler = IritPrsrSrvrExecAndConnect("x11drvs", &PrgmInput, &PrgmOutput,
					 TRUE);

    while (TRUE) {
	for (PObj = PObjs; PObj != NULL; PObj = PObj -> Pnext) {
	    fprintf(stderr, "Press return to send data.\n");
	    getchar();
	    SocWriteOneObject(Handler, PObj);
	}

	fprintf(stderr, "Done. Press return to send clear.\n");
	getchar();
	SocWriteOneObject(Handler, PObjClear);
    }

    IritPrsrSrvrKillAndDisConnect(TRUE, PrgmInput, PrgmOutput);
}

#endif /* DEBUG_SERVER */
