
/* pipe routines for OS/2
 *  lee johnson, spring 1995
 */
#define INCL_DOSFILEMGR
#define INCL_DOSQUEUES
#define INCL_DOSERRORS
#define INCL_NOPMAPI
#include <os2.h>

#include "estruct.h"

#include <process.h>

#ifndef EOS2ERR
#define EOS2ERR EINVAL
#endif

#ifndef EOUTOFMEM
#define EOUTOFMEM ENOMEM
#endif

#if CC_WATCOM
#define _fdopen fdopen
#define _cwait  cwait
#endif

/* We have one pipe running from the parent to the child... */
static HFILE pipe_p_to_c = NULLHANDLE, pipe_c_from_p = NULLHANDLE;

/* ...and another running from the child to the parent. */
static HFILE pipe_c_to_p = NULLHANDLE, pipe_p_from_c = NULLHANDLE;

static FILE *vile_in  = NULL;	/* stream attached to pipe_p_from_c */
static FILE *vile_out = NULL;	/* stream attached to pipe_p_to_c */

static int pid = -1;

static void
DupError(APIRET rc)
{
	switch(rc)
	{
	case ERROR_TOO_MANY_OPEN_FILES:
		errno = EMFILE;
		break;

	case ERROR_INVALID_HANDLE:
	case ERROR_INVALID_TARGET_HANDLE:
		errno = EBADF;
		break;

	default:
		errno = EOS2ERR;
		break;
	}
}

int
_os2_command(char *command)
{
	char buffer[1024];
	char *shell;

	if ((shell = getenv("COMSPEC")) != NULL)
	{
		sprintf(buffer, "/C %s", command);
		return spawnlp(P_NOWAIT, shell, shell, buffer, NULL);
	}
	else
	{
		return -1;
	}
}

static void
close_pipes(void)
{
	if (vile_in != NULL)
	{
		TRACE(("close_pipes, vile_in #%d\n", fileno(vile_in)))
		fclose(vile_in);
		vile_in = NULL;
		pipe_p_from_c = NULLHANDLE;
	}
	else if (pipe_p_from_c != NULLHANDLE)
	{
		TRACE(("close_pipes, pipe_p_from_c #%d\n", pipe_p_from_c))
		DosClose(pipe_p_from_c);
		pipe_p_from_c = NULLHANDLE;
	}

	if (vile_out != NULL)
	{
		TRACE(("close_pipes, vile_out #%d\n", fileno(vile_out)))
		fclose(vile_out);
		vile_out = NULL;
		pipe_p_to_c = NULLHANDLE;
	}
	else if (pipe_p_to_c != NULLHANDLE)
	{
		TRACE(("close_pipes, pipe_p_to_c #%d\n", pipe_p_to_c))
		DosClose(pipe_p_to_c);
		pipe_p_to_c = NULLHANDLE;
	}

	if (pipe_c_to_p != NULLHANDLE)
	{
		TRACE(("close_pipes, pipe_c_to_p #%d\n", pipe_c_to_p))
		DosClose(pipe_c_to_p);
		pipe_c_to_p = NULLHANDLE;
	}
	if (pipe_c_from_p != NULLHANDLE)
	{
		TRACE(("close_pipes, pipe_c_from_p #%d\n", pipe_c_from_p))
		DosClose(pipe_c_from_p);
		pipe_c_from_p = NULLHANDLE;
	}
}

int
inout_popen(FILE **infile, FILE **outfile, char *command)
{
	HFILE save_in, save_out, save_err;
	HFILE fd;
	APIRET rc;

	save_in = save_out = save_err = NULLHANDLE;

	/* We have one pipe running from the parent to the child... */
	pipe_p_to_c = pipe_c_from_p = NULLHANDLE;

	/* ...and another running from the child to the parent. */
	pipe_c_to_p = pipe_p_from_c = NULLHANDLE;

	vile_in = vile_out = NULL;

	/* Create the pipes we'll use for IPC. */
	rc = DosCreatePipe(&pipe_p_from_c, &pipe_c_to_p, 4096);
	if (rc != NO_ERROR)
	{
		errno = EOUTOFMEM;
		goto Error;
	}
	rc = DosCreatePipe(&pipe_c_from_p, &pipe_p_to_c, 4096);
	if (rc != NO_ERROR)
	{
		errno = EOUTOFMEM;
		goto Error;
	}

	/*
	 * Save standard I/O handles.
	 */
	save_in = ~0;
	if (DosDupHandle(0, &save_in) != NO_ERROR)
	{
		DupError(rc);
		goto Error;
	}

	save_out = ~0;
	if (DosDupHandle(1, &save_out) != NO_ERROR)
	{
		DupError(rc);
		goto Error;
	}

	save_err = ~0;
	if (DosDupHandle(2, &save_err) != NO_ERROR)
	{
		DupError(rc);
		goto Error;
	}

	/*
	 * Redirect standard file handles for the CHILD PROCESS.
	 */

	/* Get standard input from the read end of the p_to_c pipe. */
	fd = 0;
	if (DosDupHandle(pipe_c_from_p, &fd) != NO_ERROR)
	{
		DupError(rc);
		goto Error;
	}

	/* Send standard output to the write end of the c_to_p pipe. */
	fd = 1;
	if (DosDupHandle(pipe_c_to_p, &fd) != NO_ERROR)
	{
		DupError(rc);
		goto Error;
	}

	/* Send error output to the write end of the c_to_p pipe. */
	fd = 2;
	if (DosDupHandle(pipe_c_to_p, &fd) != NO_ERROR)
	{
		DupError(rc);
		goto Error;
	}

	/*
	 * Ensure that the p_to_c pipe will close cleanly when the parent
	 * process (vile) is done with it.
	 */
	if (DosSetFHState(pipe_p_to_c, OPEN_FLAGS_NOINHERIT) != NO_ERROR)
		goto Error;

	/* Launch the command. */
	if ((pid = _os2_command((char *)command)) < 0)
		goto Error;

	/*
	 * Ensure that the c_to_p pipe will close cleanly when the child
	 * process is done with it.
	 */
	DosClose(pipe_c_to_p);
	pipe_c_to_p = NULLHANDLE;

	/* Connect the read end of the c_to_p pipe to a stream. */
	if (infile != NULL
		&& (vile_in = *infile = _fdopen(pipe_p_from_c, "r")) == NULL)
		goto Error;
	TRACE(("inout_popen, vile_in #%d\n", fileno(vile_in)))

	/* Connect the write end of the p_to_c pipe to a stream. */
	if (outfile != NULL
		&& (vile_out = *outfile = _fdopen(pipe_p_to_c, "w")) == NULL)
		goto Error;
	TRACE(("inout_popen, vile_out #%d\n", fileno(vile_out)))

	/* Restore redirected file handles. */
	if (save_in != NULLHANDLE)
	{
		fd = 0;
		DosDupHandle(save_in, &fd);
		DosClose(save_in);
		TRACE(("inout_popen, dup 0: #%d, #%d\n", save_in, fd))
	}
	if (save_out != NULLHANDLE)
	{
		fd = 1;
		DosDupHandle(save_out, &fd);
		DosClose(save_out);
		TRACE(("inout_popen, dup 1: #%d, #%d\n", save_out, fd))
	}
	if (save_err != NULLHANDLE)
	{
		fd = 2;
		DosDupHandle(save_err, &fd);
		DosClose(save_err);
		TRACE(("inout_popen, dup 2: #%d, #%d\n", save_err, fd))
	}

	return TRUE;

Error:
	TRACE(("inout_popen, Error\n"))
	/* Restore redirected file handles. */
	if (save_in != NULLHANDLE)
	{
		fd = 0;
		DosDupHandle(save_in, &fd);
	}
	if (save_out != NULLHANDLE)
	{
		fd = 1;
		DosDupHandle(save_out, &fd);
	}
	if (save_err != NULLHANDLE)
	{
		fd = 2;
		DosDupHandle(save_err, &fd);
	}

	close_pipes();
	return FALSE;
}

int
softfork(void)
{
	return FALSE;
}

void
npflush(void)
{
	if (vile_out != NULL)
	{
		TRACE(("npflush, vile_out #%d\n", fileno(vile_out)))
		fflush(vile_out);
		fclose(vile_out);
		vile_out = NULL;
		pipe_p_to_c = NULLHANDLE;
	}
}

void
npclose(FILE *pipe_file)
{
	int wait_status;

	npflush();
	_cwait(&wait_status, pid, WAIT_CHILD);
	close_pipes();
}
