/*
 * process.cc
 *
 * Copyright (c) 2003 Frerich Raabe <raabe@kde.org>
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. For licensing and distribution details, check the
 * accompanying file 'COPYING'.
 */
#include "process.h"

#include <kdebug.h>
#include <klocale.h>
#if __FreeBSD__ == 5
#  include <ktempfile.h>
#endif
#include <ktextedit.h>

#if __FreeBSD__ == 5
#  include <qfile.h>
#endif
#include <qvbox.h>

using namespace Barry;

QString Process::execute( const QString &command, Communication comm, bool *ok )
{
	Process proc;
	return proc.runBlocking( command, comm, ok );
}

QString Process::execute( const QStringList &command, Communication comm, bool *ok )
{
	Process proc;
	return proc.runBlocking( command, comm, ok );
}

Process::Process()
	: m_process( 0 )
{
#if __FreeBSD__ == 5
	m_stdoutTempFile = 0;
	m_stderrTempFile = 0;
#endif
}

QString Process::runBlocking( const QString &command, Communication comm,
                              bool *ok )
{
	return runBlocking( QStringList::split( ' ', command ), comm, ok );
}

QString Process::runBlocking( const QStringList &command, Communication comm,
                              bool *ok )
{
	m_buffer = QString::null;

	KProcess proc;
	proc << command;

	/* Work around something which seems to be a bug in FreeBSD 5.x :
	 * KProcess cannot return the output of the child process through the
	 * signals. Instead, we pipe the output of the command into a temporary
	 * file, and then return the contents of that file.
	 */
#if __FreeBSD__ != 5
	if ( comm & Stdout )
		connect( &proc, SIGNAL( receivedStdout( KProcess *, char *, int ) ),
		         this, SLOT( slotReceivedOutput( KProcess *, char *, int ) ) );
	if ( comm & Stderr )
		connect( &proc, SIGNAL( receivedStderr( KProcess *, char *, int ) ),
		         this, SLOT( slotReceivedOutput( KProcess *, char *, int ) ) );

	// C-style cast because gcc 2.95.4 is buggy and won't allow static_cast
	proc.start( KProcess::Block, ( KProcess::Communication )comm );
#else
	KTempFile tempFile;
	tempFile.setAutoDelete( true );
	if ( comm & Stdout && comm & Stderr )
		proc << ">" << tempFile.name() << "2>&1";
	else if  ( comm & Stdout )
		proc << "1>" << tempFile.name();
	else // comm & Stderr
		proc << "2>" << tempFile.name();
	proc.setUseShell( true );
	proc.start( KProcess::Block );
#endif

	if ( ok != 0 )
		*ok = proc.normalExit() && proc.exitStatus() == 0;

#if __FreeBSD__ != 5
	return m_buffer;
#else
	const QByteArray data = tempFile.file()->readAll();
	return QString::fromLocal8Bit( data.data(), data.size() );
#endif
}

void Process::run( const QString &command, Communication comm )
{
	run( QStringList::split( ' ', command ), comm );
}

void Process::run( const QStringList &command, Communication comm )
{
	if ( m_process )
		return;

	m_process = new KProcess;
	*m_process << command;
	connect( m_process, SIGNAL( processExited( KProcess * ) ),
	         this, SLOT( slotProcessExited( KProcess * ) ) );
#if __FreeBSD__ != 5
	if ( comm & Stdout )
		connect( m_process, SIGNAL( receivedStdout( KProcess *, char *, int ) ),
		         this, SLOT( slotReceivedStdout( KProcess *, char *, int ) ) );
	if ( comm & Stderr )
		connect( m_process, SIGNAL( receivedStderr( KProcess *, char *, int ) ),
		         this, SLOT( slotReceivedStderr( KProcess *, char *, int ) ) );

	// C-style cast because gcc 2.95.4 is buggy and won't allow static_cast
	m_process->start( KProcess::NotifyOnExit,
	                  ( KProcess::Communication ) comm );
#else
	if ( comm & Stdout && comm & Stderr ) {
		m_stdoutTempFile = new KTempFile;
		m_stdoutTempFile->setAutoDelete( true );
		m_stderrTempFile = new KTempFile;
		m_stderrTempFile->setAutoDelete( true );
		*m_process << "1>" << m_stdoutTempFile->name()
		           << "2>" << m_stderrTempFile->name();
	} else if  ( comm & Stdout ) {
		m_stdoutTempFile = new KTempFile;
		m_stdoutTempFile->setAutoDelete( true );
		*m_process << "1>" << m_stdoutTempFile->name();
	} else { // comm & Stderr
		m_stderrTempFile = new KTempFile;
		m_stderrTempFile->setAutoDelete( true );
		*m_process << "2>" << m_stderrTempFile->name();
	}
	m_process->setUseShell( true );
	m_process->start( KProcess::NotifyOnExit );
#endif
}

bool Process::kill( int signo )
{
	bool killed = m_process->kill( signo );
	if ( killed )
		slotProcessExited( m_process );
	return killed;
}

void Process::slotReceivedStdout( KProcess *, char *data, int len )
{
	emit receivedStdout( this, QString::fromLocal8Bit( data, len ) );
}

void Process::slotReceivedStderr( KProcess *, char *data, int len )
{
	emit receivedStderr( this, QString::fromLocal8Bit( data, len ) );
}

void Process::slotReceivedOutput( KProcess *, char *data, int len )
{
	m_buffer += QString::fromLocal8Bit( data, len );
}

void Process::slotProcessExited( KProcess *proc )
{
#if __FreeBSD__ == 5
	if ( m_stdoutTempFile ) {
		const QByteArray data  = m_stdoutTempFile->file()->readAll();
		emit receivedStdout( this, QString::fromLocal8Bit( data.data(), data.size() ) );
	}
	if ( m_stderrTempFile ) {
		const QByteArray data  = m_stderrTempFile->file()->readAll();
		emit receivedStderr( this, QString::fromLocal8Bit( data.data(), data.size() ) );
	}
	delete m_stdoutTempFile;
	delete m_stderrTempFile;
#endif
	emit finished( this, proc->normalExit() && proc->exitStatus() == 0 );
	m_process = 0;
}

ProcessMonitor::ProcessMonitor( QWidget *parent, const char *name )
	: KDialogBase( parent, name, true, i18n( "Running command" ),
	               Cancel, Cancel, true )
{
	m_process = new Process;
	connect( m_process, SIGNAL( receivedStdout( Process *, const QString & ) ),
	         this, SLOT( receivedStdout( Process *, const QString & ) ) );
	connect( m_process, SIGNAL( receivedStderr( Process *, const QString & ) ),
	         this, SLOT( receivedStderr( Process *, const QString & ) ) );
	connect( m_process, SIGNAL( finished( Process *, bool ) ),
	         this, SLOT( finished( Process *, bool ) ) );

	resize( 500, 300 );

	QVBox *mainWidget = makeVBoxMainWidget();

	m_outputView = new KTextEdit( mainWidget, "m_outputView" );
	m_outputView->setTextFormat( QTextEdit::RichText );
	m_outputView->setReadOnly( true );
}

ProcessMonitor::~ProcessMonitor()
{
	delete m_process;
}

void ProcessMonitor::slotCancel()
{
	reject();
}

void ProcessMonitor::receivedStdout( Process *, const QString &data )
{
	m_outputView->append( data.stripWhiteSpace() );
}

void ProcessMonitor::receivedStderr( Process *, const QString &data )
{
	m_outputView->append( QString::fromLatin1( "<i>%1</i>" ).arg( data.stripWhiteSpace() ) );
}

void ProcessMonitor::finished( Process *, bool ok )
{
	QString msg;
	if ( ok )
		msg = i18n( "Command finished successfully." );
	else
		msg = i18n( "There were errors while executing the command. Please "
		            "check the program's output for details." );
	m_outputView->append( QString::fromLatin1( "<p/><p/><b>%1</b>" ).arg( msg ) );
}

#include "process.moc"
// vim:ts=4:sw=4:noet:list
