JError
JExecute
	(
	const JCharacter*		argv[],
	const JSize				size,
	pid_t*					childPID,
	const JExecuteAction	toAction,
	int*					toFD,
	const JExecuteAction	origFromAction,
	int*					fromFD,
	const JExecuteAction	errAction,
	int*					errFD
	)
{
	assert( size > sizeof(JCharacter*) );
	assert( argv[ (size/sizeof(JCharacter*)) - 1 ] == NULL );

	const JExecuteAction fromAction =
		(origFromAction == kJForceNonblockingPipe ? kJCreatePipe : origFromAction);

	assert( toAction != kJTossOutput && toAction != kJAttachToFromFD &&
			toAction != kJForceNonblockingPipe );
	assert( fromAction != kJAttachToFromFD );
	assert( errAction != kJForceNonblockingPipe );

	assert( (toAction != kJCreatePipe && toAction != kJAttachToFD) ||
			toFD != NULL );
	assert( (fromAction != kJCreatePipe && fromAction != kJAttachToFD) ||
			fromFD != NULL );
	assert( (errAction != kJCreatePipe && errAction != kJAttachToFD) ||
			errFD != NULL );

	JString progName;
	if (!JProgramAvailable(argv[0], &progName))
		{
		return JProgramNotAvailable(argv[0]);
		}
	argv[0] = progName.GetCString();

	int fd[3][2];

	if (toAction == kJCreatePipe)
		{
		const JError err = JCreatePipe(fd[0]);
		if (!err.OK())
			{
			return err;
			}
		}

	if (fromAction == kJCreatePipe)
		{
		const JError err = JCreatePipe(fd[1]);
		if (!err.OK())
			{
			if (toAction == kJCreatePipe)
				{
				close(fd[0][0]);
				close(fd[0][1]);
				}
			return err;
			}
		}

	if (errAction == kJCreatePipe)
		{
		const JError err = JCreatePipe(fd[2]);
		if (!err.OK())
			{
			if (toAction == kJCreatePipe)
				{
				close(fd[0][0]);
				close(fd[0][1]);
				}
			if (fromAction == kJCreatePipe)
				{
				close(fd[1][0]);
				close(fd[1][1]);
				}
			return err;
			}
		}

	pid_t pid;
	const JError err = JThisProcess::Fork(&pid);
	if (!err.OK())
		{
		if (toAction == kJCreatePipe)
			{
			close(fd[0][0]);
			close(fd[0][1]);
			}
		if (fromAction == kJCreatePipe)
			{
			close(fd[1][0]);
			close(fd[1][1]);
			}
		if (errAction == kJCreatePipe)
			{
			close(fd[2][0]);
			close(fd[2][1]);
			}
		return err;
		}

	// child

	else if (pid == 0)
		{
		const int stdinFD = fileno(stdin);
		if (toAction == kJCreatePipe)
			{
			dup2(fd[0][0], stdinFD);
			close(fd[0][0]);
			close(fd[0][1]);
			}
		else if (toAction == kJAttachToFD)
			{
			dup2(*toFD, stdinFD);
			close(*toFD);
			}

		const int stdoutFD = fileno(stdout);
		if (fromAction == kJCreatePipe)
			{
			dup2(fd[1][1], stdoutFD);
			close(fd[1][0]);
			close(fd[1][1]);
			}
		else if (fromAction == kJAttachToFD)
			{
			dup2(*fromFD, stdoutFD);
			close(*fromFD);
			}
		else if (fromAction == kJTossOutput)
			{
			FILE* nullFile = fopen("/dev/null", "a");
			int nullfd     = fileno(nullFile);
			dup2(nullfd, stdoutFD);
			fclose(nullFile);
			}

		const int stderrFD = fileno(stderr);
		if (errAction == kJCreatePipe)
			{
			dup2(fd[2][1], stderrFD);
			close(fd[2][0]);
			close(fd[2][1]);
			}
		else if (errAction == kJAttachToFD)
			{
			dup2(*errFD, stderrFD);
			close(*errFD);
			}
		else if (errAction == kJTossOutput)
			{
			FILE* nullFile = fopen("/dev/null", "a");
			int nullfd     = fileno(nullFile);
			dup2(nullfd, stderrFD);
			fclose(nullFile);
			}
		else if (errAction == kJAttachToFromFD && fromAction != kJIgnoreConnection)
			{
			dup2(stdoutFD, stderrFD);
			}

		ACE_OS::execvp(argv[0], const_cast<char* const*>(argv));

		cerr << "Unable to run program \"" << argv[0] << '"' << endl;
		cerr << endl;
		cerr << "JExecute()::execvp() failed" << endl;
		cerr << "Errno value: " << jerrno() << endl;

		JThisProcess::Exit(1);
		return JNoError();
		}

	// parent

	else
		{
		if (origFromAction == kJForceNonblockingPipe)
			{
			pid_t pid2;
			const JError err2 = JThisProcess::Fork(&pid2);
			if (err2.OK() && pid2 == 0)
				{
				for (int i=0; i<150; i++)
					{
					JWait(0.1);

					int value = fcntl(fd[1][1], F_GETFL, 0);
					if (value & O_NONBLOCK)
						{
						cerr << "turning off nonblocking for cout: " << value << endl;
						fcntl(fd[1][1], F_SETFL, value & (~ O_NONBLOCK));
						}
					}

				JThisProcess::Exit(0);
				return JNoError();
				}

			JProcess* p = new JProcess(pid2);
			p->KillAtExit(kJTrue);
			}

		if (toAction == kJCreatePipe)
			{
			close(fd[0][0]);
			*toFD = fd[0][1];
			}
		if (fromAction == kJCreatePipe)
			{
			close(fd[1][1]);
			*fromFD = fd[1][0];
			}
		if (errAction == kJCreatePipe)
			{
			close(fd[2][1]);
			*errFD = fd[2][0];
			}

		if (childPID == NULL)
			{
			return JWaitForChild(pid);
			}
		else
			{
			*childPID = pid;
			return JNoError();
			}
		}
}