/* process.hh - fork/exec made userfriendly
 * Copyright 2005-2008 Bas Wijnen <wijnen@debian.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef SHEVEK_PROCESS_HH
#define SHEVEK_PROCESS_HH

#include <string>
#include "refbase.hh"
#include "fd.hh"

namespace shevek
{
  /// Create a process, optionally connection its standard in- and output streams to the calling program.
  class process : public shevek::refbase
  {
  public:
    /// Create a process from a filename and an argument list.
    static Glib::RefPtr <process> create (std::string const &command,
					  std::list <std::string> &argv,
					  bool pipe_stdin = true,
					  bool pipe_stdout = true,
					  bool pipe_stderr = true);
    /// Create a process from a filename, an argument list and an environment.
    static Glib::RefPtr <process> create (std::string const &command,
					  std::list <std::string> &argv,
					  std::list <std::string> const &envp,
					  bool pipe_stdin = true,
					  bool pipe_stdout = true,
					  bool pipe_stderr = true);
    /// Run a string with the shell.
    /** Note that the process is forked from the shell, and so does not get killed when the process goes away.
     */
    static Glib::RefPtr <process> shell (std::string const &command,
					 bool pipe_stdin = true,
					 bool pipe_stdout = true,
					 bool pipe_stderr = true,
					 std::string const &sh = "/bin/sh")
    {
	    std::list <std::string> args;
	    args.push_back (sh);
	    args.push_back ("-c");
	    args.push_back (command);
	    return create (command, args, pipe_stdin, pipe_stdout, pipe_stderr);
    }
    /// The standard input pipe, if it was requested.
    Glib::RefPtr <shevek::fd> in ();
    /// The standard output pipe, if it was requested.
    Glib::RefPtr <shevek::fd> out ();
    /// The standard error pipe, if it was requested.
    Glib::RefPtr <shevek::fd> err ();
    /// The process ID.
    pid_t pid ();
    /// The destructor.  This kills the process if it was still running.
    ~process ();
    /// Run a process and return its output.
    /** A convenience function for running a process and catching its standard output in a string.
     *  This blocks until the process has exited.
     */
    static std::string run (std::string const &command, std::string const &sh);
    /// Create a process without arguments.
    static Glib::RefPtr <process> create (std::string const &command,
					  bool pipe_stdin = true,
					  bool pipe_stdout = true,
					  bool pipe_stderr = true)
    {
	    std::list <std::string> args;
	    args.push_back (command);
	    return create (command, args, pipe_stdin, pipe_stdout, pipe_stderr);
    }
    /// Create a process with one argument.
    static Glib::RefPtr <process> create (std::string const &command,
					  std::string const &a1,
					  bool pipe_stdin = true,
					  bool pipe_stdout = true,
					  bool pipe_stderr = true)
    {
	    std::list <std::string> args;
	    args.push_back (command);
	    args.push_back (a1);
	    return create (command, args, pipe_stdin, pipe_stdout, pipe_stderr);
    }
    /// Create a process with two arguments.
    static Glib::RefPtr <process> create (std::string const &command,
					  std::string const &a1,
					  std::string const &a2,
					  bool pipe_stdin = true,
					  bool pipe_stdout = true,
					  bool pipe_stderr = true)
    {
	    std::list <std::string> args;
	    args.push_back (command);
	    args.push_back (a1);
	    args.push_back (a2);
	    return create (command, args, pipe_stdin, pipe_stdout, pipe_stderr);
    }
    /// Create a process with three arguments.
    static Glib::RefPtr <process> create (std::string const &command,
					  std::string const &a1,
					  std::string const &a2,
					  std::string const &a3,
					  bool pipe_stdin = true,
					  bool pipe_stdout = true,
					  bool pipe_stderr = true)
    {
	    std::list <std::string> args;
	    args.push_back (command);
	    args.push_back (a1);
	    args.push_back (a2);
	    args.push_back (a3);
	    return create (command, args, pipe_stdin, pipe_stdout, pipe_stderr);
    }
    /// Create a process with four arguments.
    static Glib::RefPtr <process> create (std::string const &command,
					  std::string const &a1,
					  std::string const &a2,
					  std::string const &a3,
					  std::string const &a4,
					  bool pipe_stdin = true,
					  bool pipe_stdout = true,
					  bool pipe_stderr = true)
    {
	    std::list <std::string> args;
	    args.push_back (command);
	    args.push_back (a1);
	    args.push_back (a2);
	    args.push_back (a3);
	    args.push_back (a4);
	    return create (command, args, pipe_stdin, pipe_stdout, pipe_stderr);
    }
    /// Create a process with five arguments.
    static Glib::RefPtr <process> create (std::string const &command,
					  std::string const &a1,
					  std::string const &a2,
					  std::string const &a3,
					  std::string const &a4,
					  std::string const &a5,
					  bool pipe_stdin = true,
					  bool pipe_stdout = true,
					  bool pipe_stderr = true)
    {
	    std::list <std::string> args;
	    args.push_back (command);
	    args.push_back (a1);
	    args.push_back (a2);
	    args.push_back (a3);
	    args.push_back (a4);
	    args.push_back (a5);
	    return create (command, args, pipe_stdin, pipe_stdout, pipe_stderr);
    }
    /// Create a process with six arguments.
    static Glib::RefPtr <process> create (std::string const &command,
					  std::string const &a1,
					  std::string const &a2,
					  std::string const &a3,
					  std::string const &a4,
					  std::string const &a5,
					  std::string const &a6,
					  bool pipe_stdin = true,
					  bool pipe_stdout = true,
					  bool pipe_stderr = true)
    {
	    std::list <std::string> args;
	    args.push_back (command);
	    args.push_back (a1);
	    args.push_back (a2);
	    args.push_back (a3);
	    args.push_back (a4);
	    args.push_back (a5);
	    args.push_back (a6);
	    return create (command, args, pipe_stdin, pipe_stdout, pipe_stderr);
    }
    /// Create a process with seven arguments.
    static Glib::RefPtr <process> create (std::string const &command,
					  std::string const &a1,
					  std::string const &a2,
					  std::string const &a3,
					  std::string const &a4,
					  std::string const &a5,
					  std::string const &a6,
					  std::string const &a7,
					  bool pipe_stdin = true,
					  bool pipe_stdout = true,
					  bool pipe_stderr = true)
    {
	    std::list <std::string> args;
	    args.push_back (command);
	    args.push_back (a1);
	    args.push_back (a2);
	    args.push_back (a3);
	    args.push_back (a4);
	    args.push_back (a5);
	    args.push_back (a6);
	    args.push_back (a7);
	    return create (command, args, pipe_stdin, pipe_stdout, pipe_stderr);
    }
    /// Create a process with eight arguments.
    static Glib::RefPtr <process> create (std::string const &command,
					  std::string const &a1,
					  std::string const &a2,
					  std::string const &a3,
					  std::string const &a4,
					  std::string const &a5,
					  std::string const &a6,
					  std::string const &a7,
					  std::string const &a8,
					  bool pipe_stdin = true,
					  bool pipe_stdout = true,
					  bool pipe_stderr = true)
    {
	    std::list <std::string> args;
	    args.push_back (command);
	    args.push_back (a1);
	    args.push_back (a2);
	    args.push_back (a3);
	    args.push_back (a4);
	    args.push_back (a5);
	    args.push_back (a6);
	    args.push_back (a7);
	    args.push_back (a8);
	    return create (command, args, pipe_stdin, pipe_stdout, pipe_stderr);
    }
    /// Create a process with nine arguments.
    static Glib::RefPtr <process> create (std::string const &command,
					  std::string const &a1,
					  std::string const &a2,
					  std::string const &a3,
					  std::string const &a4,
					  std::string const &a5,
					  std::string const &a6,
					  std::string const &a7,
					  std::string const &a8,
					  std::string const &a9,
					  bool pipe_stdin = true,
					  bool pipe_stdout = true,
					  bool pipe_stderr = true)
    {
	    std::list <std::string> args;
	    args.push_back (command);
	    args.push_back (a1);
	    args.push_back (a2);
	    args.push_back (a3);
	    args.push_back (a4);
	    args.push_back (a5);
	    args.push_back (a6);
	    args.push_back (a7);
	    args.push_back (a8);
	    args.push_back (a9);
	    return create (command, args, pipe_stdin, pipe_stdout, pipe_stderr);
    }
  private:
    Glib::RefPtr <shevek::fd> m_in, m_out, m_err;
    pid_t m_pid;
    process (std::string const &command, char **argv, char **envp, bool pipe_stdin, bool pipe_stdout, bool pipe_stderr);
    static char **make_pointers (std::list <std::string> const &source);
    static void clean (char **pointers);
    static char *dup (char const *str);
  };
}

#endif
