/******************************************************************************
 JSimpleProcess.cc

	Class to run a program and report to the user if an error occurs.
	We use a JMessageProtocol to accumulate the text from the process
	so the pipe doesn't fill up.

	BASE CLASS = JProcess

	Copyright © 1999 by John Lindal. All rights reserved.

 *****************************************************************************/

#include <JSimpleProcess.h>
#include <JThisProcess.h>
#include <jGlobals.h>
#include <jAssert.h>

/******************************************************************************
 Create (static)

	The versions that don't return a pointer to the object imply
	deleteWhenFinished and automatically report any error from JExecute().

 ******************************************************************************/

void
JSimpleProcess::Create
	(
	const JCharacter*	cmdStr,
	const JBoolean		detach
	)
{
	JSimpleProcess* p;
	const JError err = Create(&p, cmdStr, kTrue);
	if (!err.OK())
		{
		err.ReportError();
		}
	else if (detach)
		{
		JThisProcess::Ignore(p);
		}
}

JError
JSimpleProcess::Create
	(
	JSimpleProcess**	process,
	const JCharacter*	cmdStr,
	const JBoolean		deleteWhenFinished
	)
{
	pid_t childPID;
	int errFD;
	const JError err = JExecute(cmdStr, &childPID,
								kJIgnoreConnection, NULL,
								kJTossOutput, NULL,
								kJCreatePipe, &errFD);
	if (err.OK())
		{
		*process = new JSimpleProcess(childPID, errFD, deleteWhenFinished);
		assert( *process != NULL );
		}
	else
		{
		*process = NULL;
		}

	return err;
}

void
JSimpleProcess::Create
	(
	const JCharacter*	argv[],
	const JSize			count,
	const JBoolean		detach
	)
{
	JSimpleProcess* p;
	const JError err = Create(&p, argv, count, kTrue);
	if (!err.OK())
		{
		err.ReportError();
		}
	else if (detach)
		{
		JThisProcess::Ignore(p);
		}
}

JError
JSimpleProcess::Create
	(
	JSimpleProcess**	process,
	const JCharacter*	argv[],
	const JSize			count,
	const JBoolean		deleteWhenFinished
	)
{
	pid_t childPID;
	int errFD;
	const JError err = JExecute(argv, count, &childPID,
								kJIgnoreConnection, NULL,
								kJTossOutput, NULL,
								kJCreatePipe, &errFD);
	if (err.OK())
		{
		*process = new JSimpleProcess(childPID, errFD, deleteWhenFinished);
		assert( *process != NULL );
		}
	else
		{
		*process = NULL;
		}

	return err;
}

/******************************************************************************
 Destructor

 ******************************************************************************/

JSimpleProcess::~JSimpleProcess()
{
	delete itsLink;
}

/******************************************************************************
 Receive (virtual protected)

 ******************************************************************************/

void
JSimpleProcess::Receive
	(
	JBroadcaster*	sender,
	const Message&	message
	)
{
	if (sender == this && message.Is(JProcess::kFinished))
		{
		ReportError();
		if (itsAutoDeleteFlag)
			{
			delete this;
			}
		}

	else
		{
		JProcess::Receive(sender, message);
		}
}

/******************************************************************************
 ReportError (private)

 ******************************************************************************/

void
JSimpleProcess::ReportError()
{
	if (itsLink->HasMessages())
		{
		JString text, line;
		while (itsLink->GetNextMessage(&line))
			{
			if (!text.IsEmpty())
				{
				text.AppendCharacter('\n');
				}
			text += line;
			}

		if (itsLink->PeekPartialMessage(&line))
			{
			if (!text.IsEmpty())
				{
				text.AppendCharacter('\n');
				}
			text += line;
			}

		text.Prepend("An error occurred:\n\n");
		(JGetUserNotification())->ReportError(text);
		}
}

/******************************************************************************
 Constructor

 ******************************************************************************/

// This function has to be last so JCore::new works for everything else.

#undef new

JSimpleProcess::JSimpleProcess
	(
	const pid_t		pid,
	const int		fd,
	const JBoolean	deleteWhenFinished
	)
	:
	JProcess(pid),
	itsAutoDeleteFlag(deleteWhenFinished)
{
	itsLink = new ProcessLink(fd);
	assert( itsLink != NULL );

	ListenTo(this);
}
