/*
 * This file is part of DesktopBSD, see the README file.
 *
 * Author: Peter Hofer <ph@desktopbsd.net>
 *			(C) 2004, 2005
 *
 * Copyright: BSD, see the COPYING file included in this distribution
 *
 */

extern "C" {
#include <unistd.h>
#include <signal.h>
}

#include <unixprocess.h>


UnixProcess::UnixProcess(QObject *parent, const char *name):
	QProcess(parent, name),
	envSet(false)
{
}


UnixProcess::UnixProcess(const QString & arg0, QObject *parent, const char *name):
	QProcess(arg0, parent, name),
	envSet(false)
{
}


UnixProcess::UnixProcess(const QStringList & args, QObject *parent, const char *name):
	QProcess(args, parent, name),
	envSet(false)
{
}


/**
 * This simply calls UnixProcess::arguments(false)
 * 
 * @see arguments()
 */
QStringList UnixProcess::arguments() const
{
	return arguments(false);
}


/**
  * Returns the list of arguments for the process.
  *
  * @param useEnv TRUE to include the environment by calling /usr/bin/env first
  *
  * Example for arguments(TRUE):
  *   { "/usr/bin/env" "PATH=/bin:/usr/local/bin" "DISPLAY=:1"
  *     "/the/actual/command" "and" "its" "arguments"          }
  *
  * @see QProcess::arguments()
  */
QStringList UnixProcess::arguments(bool useEnv) const
{
	if(!useEnv)
		return QProcess::arguments();

	return (QStringList("/usr/bin/env") + env + QProcess::arguments());
}


/**
  * @attention This doesn't copy data other than environment (@see
  *            setEnvironment), initialStdin, arguments, communication,
  *            and working directory. Copying a QProcess object and
  *            accessing a running process doesn't work.
  *
  */
UnixProcess & UnixProcess::set(const UnixProcess & b) {
	if(&b != this) {
		env = b.env;
		envSet = b.envSet;
		
		setArguments(b.arguments());
		setCommunication(b.communication());
		setWorkingDirectory(b.workingDirectory());
	}

	return *this;
}


/** Kills the whole process group of this process (-9)
  * Returns false if killpg() returned false or the process isn't running
  *
  * Note that this method also kills the invoking process
  * if it belongs to the process group.
  */
bool UnixProcess::killGroup() {
	if(isRunning())
		return (killpg(getpgid(processIdentifier()), SIGKILL) == 0);

	return true;
}


static void (*oldSigHdl)(int);
/** Temporary signal handler. */
void signalHandler(int sig) {
	signal(sig, oldSigHdl);
}

/** Try to terminate all processes of this processes' group.
  * Returns false if killpg() returned false or the process isn't running
  *
  * If the calling process is in the same group as this process,
  * the process group id of the calling progress is changed using setsid().
  */
bool UnixProcess::tryTerminateGroup() {
	if(isRunning()) {
		oldSigHdl = signal(SIGTERM, signalHandler);

		return (killpg(getpgid(processIdentifier()), SIGTERM) == 0);
	}

	return true;
}

bool UnixProcess::start() {
	if(envSet)
		return QProcess::start(&env);
	
	return QProcess::start();
}

/** 
  * Sets which environment variables will be set for the
  * invocated process.
  *
  * If start() is called with a different QStringList
  * environment variable set, this has no effect.
  *
  * @see start()
  */
void UnixProcess::setEnvironment(const QStringList & e) {
	env = e;
	envSet = true;
}

/**
  * Disables setting own environment variables if enabled
  * by using setEnvironment() before.
  *
  */
void UnixProcess::unsetEnvironment() {
	env.clear();
	envSet = false;
}

/** 
  * Returns a list of environment variables that were
  * set. Empty if unsetEnvironment() was used.
  *
  * @see setEnvironment()
  * @see start()
  */
QStringList UnixProcess::getEnvironment() const {
	return env;
}


/**
  * Returns true when this UnixProcess has a collection of
  * environment variables that would be set when start() is
  * called.
  *
  */
bool UnixProcess::isEnvironmentSet() const {
	return envSet;
}


/** Returns all arguments joined in a command string.
  *
  * Example: In a UnixProcess with the arguments
  *
  * 0 = "/bin/cat"
  * 1 = "textfile"
  * 2 = "and_another_textfile"
  *
  * toCommandString() will return this string:
  *
  * "'/bin/cat' 'textfile' 'and_another_textfile'"
  *
  * When no arguments are specified, toCommandString()
  * will return an empty string ("").
  *
  */
QString UnixProcess::toCommandString() const {
	QStringList arg = arguments();
	if(arg.count() == 0)
		return "";

	return "'" + arguments().join("' '") + "'";
}
