--- /dev/null
+#include <msp/io/console.h>
+#include "process.h"
+#include "process_private.h"
+
+using namespace std;
+
+namespace Msp {
+
+Process *Process::_self = 0;
+
+Process::Process(const Private &p):
+ priv(new Private(p))
+{
+ init();
+}
+
+Process::Process():
+ priv(new Private)
+{
+ init();
+}
+
+void Process::init()
+{
+ redirect = false;
+ cin = 0;
+ cout = 0;
+ cerr = 0;
+ running = false;
+ finished = false;
+ exit_code = 0;
+}
+
+Process &Process::self()
+{
+ if(!_self)
+ {
+ Private _priv;
+ platform_get_self_info(_priv);
+ _self = new Process(_priv);
+ }
+ return *_self;
+}
+
+void Process::set_working_directory(const FS::Path &d)
+{
+ work_dir = d;
+}
+
+void Process::redirect_cin(IO::Base &io)
+{
+ do_redirect(cin, io);
+}
+
+void Process::redirect_cout(IO::Base &io)
+{
+ do_redirect(cout, io);
+}
+
+void Process::redirect_cerr(IO::Base &io)
+{
+ do_redirect(cerr, io);
+}
+
+void Process::do_redirect(IO::Base *&ptr, IO::Base &io)
+{
+ if(this==_self)
+ {
+ if(&ptr==&cin)
+ IO::cin.redirect(io);
+ else if(&ptr==&cout)
+ IO::cout.redirect(io);
+ else if(&ptr==&cerr)
+ IO::cerr.redirect(io);
+ }
+ else
+ {
+ redirect = true;
+ ptr = &io;
+ }
+}
+
+void Process::execute(const string &command, const Arguments &args)
+{
+ execute(command, true, args);
+}
+
+void Process::execute(const FS::Path &command, const Arguments &args)
+{
+ execute(command.str(), false, args);
+}
+
+unsigned Process::get_exit_code() const
+{
+ if(!finished)
+ throw logic_error("not finished");
+ return exit_code;
+}
+
+} // namespace Msp
--- /dev/null
+#ifndef MSP_CORE_PROCESS_H_
+#define MSP_CORE_PROCESS_H_
+
+#include <string>
+#include <vector>
+#include <msp/fs/path.h>
+#include <msp/io/base.h>
+
+namespace Msp {
+
+/**
+Provides an interface for running and controlling programs.
+
+The creation of a new system-level process does not happen immediately when a
+Process object is created, but only when exexute is called. If called on the
+object obtained with self(), the process is replaced with a new program
+immediately. In particular, destructors are not invoked.
+
+Output redirections can be specified before calling execute. To feed input to
+the process or capture its output, use an IO::Pipe. Redirections performed on
+the self object take effect immediately. It is recommended to perform such
+redirections directly on the Console objects.
+*/
+class Process
+{
+public:
+ typedef std::vector<std::string> Arguments;
+
+private:
+ struct Private;
+
+ Private *priv;
+ FS::Path work_dir;
+ bool redirect;
+ IO::Base *cin;
+ IO::Base *cout;
+ IO::Base *cerr;
+ bool running;
+ bool finished;
+ unsigned exit_code;
+
+ static Process *_self;
+
+ Process(const Private &);
+ void init();
+public:
+ Process();
+ ~Process();
+
+ /** Returns an object referring to the current process. */
+ static Process &self();
+private:
+ static void platform_get_self_info(Private &);
+
+public:
+ /** Sets the working directory for the new process. By default, the working
+ directory is not changed. */
+ void set_working_directory(const FS::Path &);
+
+ /** Redirects console input from an I/O object. */
+ void redirect_cin(IO::Base &);
+
+ /** Redirects console output to an I/O object. */
+ void redirect_cout(IO::Base &);
+
+ /** Redirects error output to an I/O object. */
+ void redirect_cerr(IO::Base &);
+private:
+ void do_redirect(IO::Base *&, IO::Base &);
+
+public:
+ /** Executes a command, searching for it in the standard locations. */
+ void execute(const std::string &, const Arguments &);
+
+ /** Executes a command specified by a full path. */
+ void execute(const FS::Path &, const Arguments &);
+
+private:
+ void execute(const std::string &, bool, const Arguments &);
+
+public:
+ bool is_running() const { return running; }
+ bool has_finished() const { return finished; }
+
+ /** Checks the status of a running process. If block is true, waits for the
+ process to finish. */
+ bool wait(bool block = true);
+
+ /** Returns the exit code for a finished process. */
+ unsigned get_exit_code() const;
+
+ /** Terminates the process. */
+ void terminate();
+
+ /** Brutally murders the process. It is not given a chance to terminate
+ gracefully. */
+ void kill();
+
+ /** Sends the process an interrupt signal, as if ^C was pressed. */
+ void interrupt();
+};
+
+} // namespace Msp
+
+#endif
--- /dev/null
+#ifndef MSP_CORE_PROCESS_PRIVATE_H_
+#define MSP_CORE_PROCESS_PRIVATE_H_
+
+#include "process_platform.h"
+
+namespace Msp {
+
+struct Process::Private
+{
+ PlatformProcessInformation info;
+
+ Private();
+};
+
+} // namespace Msp
+
+#endif
--- /dev/null
+#include <unistd.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <msp/core/systemerror.h>
+#include <msp/fs/dir.h>
+#include <msp/io/console.h>
+#include "process.h"
+#include "process_private.h"
+
+using namespace std;
+
+namespace Msp {
+
+Process::~Process()
+{ }
+
+void Process::platform_get_self_info(Private &priv)
+{
+ priv.info.pid = getpid();
+}
+
+void Process::execute(const string &command, bool path_search, const Arguments &args)
+{
+ pid_t pid = 0;
+ if(this!=_self)
+ pid = fork();
+
+ if(pid==-1)
+ throw system_error("fork");
+ else if(pid==0)
+ {
+ try
+ {
+ vector<const char *> argv(args.size()+2);
+ argv[0] = command.c_str();
+ for(unsigned i=0; i<args.size(); ++i)
+ argv[i+1] = args[i].c_str();
+ argv[args.size()+1] = 0;
+
+ if(redirect)
+ {
+ if(cin)
+ IO::cin.redirect(*cin);
+ if(cout)
+ IO::cout.redirect(*cout);
+ if(cerr)
+ IO::cerr.redirect(*cerr);
+ }
+
+ if(!work_dir.empty())
+ FS::chdir(work_dir);
+
+ if(path_search)
+ execvp(command.c_str(), const_cast<char *const *>(&argv[0]));
+ else
+ execv(command.c_str(), const_cast<char *const *>(&argv[0]));
+ }
+ catch(...)
+ { }
+ _exit(255);
+ }
+ else
+ {
+ priv->info.pid = pid;
+ running = true;
+ }
+}
+
+bool Process::wait(bool block)
+{
+ if(!running)
+ throw logic_error("not running");
+
+ int status;
+ int pid = waitpid(priv->info.pid, &status, (block ? 0 : WNOHANG));
+ if(pid==-1)
+ throw system_error("waitpid");
+
+ if(pid)
+ {
+ finished = true;
+ running = false;
+ if(WIFEXITED(status))
+ exit_code = WEXITSTATUS(status);
+ else if(WIFSIGNALED(status))
+ exit_code = 0x100|WTERMSIG(status);
+ }
+
+ return finished;
+}
+
+void Process::terminate()
+{
+ ::kill(priv->info.pid, SIGTERM);
+}
+
+void Process::kill()
+{
+ ::kill(priv->info.pid, SIGKILL);
+}
+
+void Process::interrupt()
+{
+ ::kill(priv->info.pid, SIGINT);
+}
+
+
+Process::Private::Private()
+{
+ info.pid = 0;
+}
+
+} // namespace Msp
--- /dev/null
+#ifndef MSP_CORE_PROCESS_PLATFORM_H_
+#define MSP_CORE_PROCESS_PLATFORM_H_
+
+#include <sys/types.h>
+
+namespace Msp {
+
+struct PlatformProcessInformation
+{
+ pid_t pid;
+};
+
+} // namespace Msp
+
+#endif
--- /dev/null
+#include <windows.h>
+#include <msp/core/systemerror.h>
+#include <msp/io/handle_private.h>
+#include "process.h"
+#include "process_private.h"
+
+using namespace std;
+
+namespace {
+
+string quote_argument(const string &arg)
+{
+ string result;
+ bool need_quotes = false;
+ bool backslash = false;
+ for(string::const_iterator i=arg.begin(); i!=arg.end(); ++i)
+ {
+ if(*i=='\\')
+ backslash = true;
+ else if(*i=='"')
+ {
+ if(backslash)
+ result += '\\';
+ result += '\\';
+ }
+ else if(*i==' ')
+ need_quotes = true;
+ result += *i;
+ }
+
+ if(need_quotes)
+ return "\""+result+"\"";
+ else
+ return result;
+}
+
+}
+
+namespace Msp {
+
+Process::~Process()
+{
+ CloseHandle(priv->info.hProcess);
+ CloseHandle(priv->info.hThread);
+}
+
+void Process::platform_get_self_info(Private &priv)
+{
+ priv.info.hProcess = GetCurrentProcess();
+ priv.info.hThread = 0;
+ priv.info.dwProcessId = GetCurrentProcessId();
+ priv.info.dwThreadId = 0;
+}
+
+void Process::execute(const string &command, bool path_search, const Arguments &args)
+{
+ string cmdline = quote_argument(command);
+ for(Arguments::const_iterator i=args.begin(); i!=args.end(); ++i)
+ {
+ cmdline += ' ';
+ cmdline += quote_argument(*i);
+ }
+
+ STARTUPINFO startup;
+ startup.cb = sizeof(STARTUPINFO);
+ startup.lpReserved = 0;
+ startup.lpDesktop = 0;
+ startup.lpTitle = 0;
+ startup.dwFlags = 0;
+ startup.cbReserved2 = 0;
+ startup.lpReserved2 = 0;
+ if(redirect)
+ {
+ startup.dwFlags |= STARTF_USESTDHANDLES;
+ HANDLE self_handle = self().priv->info.hProcess;
+ HANDLE cin_handle = (cin ? *cin->get_handle(IO::M_READ) : GetStdHandle(STD_INPUT_HANDLE));
+ DuplicateHandle(self_handle, cin_handle, self_handle, &startup.hStdInput, 0, true, DUPLICATE_SAME_ACCESS);
+ HANDLE cout_handle = (cout ? *cout->get_handle(IO::M_WRITE) : GetStdHandle(STD_OUTPUT_HANDLE));
+ DuplicateHandle(self_handle, cout_handle, self_handle, &startup.hStdOutput, 0, true, DUPLICATE_SAME_ACCESS);
+ HANDLE cerr_handle = (cerr ? *cerr->get_handle(IO::M_WRITE) : GetStdHandle(STD_ERROR_HANDLE));
+ DuplicateHandle(self_handle, cerr_handle, self_handle, &startup.hStdError, 0, true, DUPLICATE_SAME_ACCESS);
+ }
+ const char *cmdptr = (path_search ? 0 : command.c_str());
+ const char *wd = (work_dir.empty() ? 0 : work_dir.c_str());
+ if(!CreateProcess(cmdptr, const_cast<char *>(cmdline.c_str()), 0, 0, false, 0, 0, wd, &startup, &priv->info))
+ throw system_error("CreateProcess");
+ // XXX Should we close the duplicated handles? What if CreateProcess fails?
+
+ running = true;
+
+ if(this==_self)
+ TerminateProcess(priv->info.hProcess, 0);
+}
+
+bool Process::wait(bool block)
+{
+ if(!running)
+ throw logic_error("not running");
+
+ DWORD ret = WaitForSingleObject(priv->info.hProcess, (block ? INFINITE : 0));
+ if(ret==WAIT_FAILED)
+ throw system_error("WaitForSingleObject");
+
+ if(ret==WAIT_OBJECT_0)
+ {
+ running = false;
+ finished = true;
+ }
+
+ return finished;
+}
+
+void Process::terminate()
+{
+ TerminateProcess(priv->info.hProcess, 1);
+}
+
+void Process::kill()
+{
+ TerminateProcess(priv->info.hProcess, 1);
+}
+
+void Process::interrupt()
+{
+ TerminateProcess(priv->info.hProcess, 1);
+}
+
+
+Process::Private::Private()
+{
+ info.hProcess = 0;
+ info.hThread = 0;
+ info.dwProcessId = 0;
+ info.dwThreadId = 0;
+}
+
+} // namespace Msp
--- /dev/null
+#ifndef MSP_CORE_PROCESS_PLATFORM_H_
+#define MSP_CORE_PROCESS_PLATFORM_H_
+
+#include <windows.h>
+
+namespace Msp {
+
+typedef PROCESS_INFORMATION PlatformProcessInformation;
+
+} // namespace Msp
+
+#endif