-#include <cstdlib>
-#include <unistd.h>
-#include <sys/wait.h>
-#include <msp/fs/dir.h>
-#include <msp/io/console.h>
-#include <msp/io/file.h>
-#include <msp/io/print.h>
-#include <msp/time/timedelta.h>
-#include "externaltask.h"
-
-using namespace std;
-using namespace Msp;
-
-ExternalTask::ExternalTask(const Arguments &a, const FS::Path &wd):
- argv(a),
- work_dir(wd),
- process(0),
- exit_code(-1),
- stdin_action(PASSTHROUGH),
- stdout_action(PASSTHROUGH),
- stderr_action(PASSTHROUGH),
- capture_pipe(0)
-{
- if(argv.empty())
- throw invalid_argument("ExternalTask::ExternalTask");
-}
-
-ExternalTask::~ExternalTask()
-{
- delete capture_pipe;
-}
-
-string ExternalTask::get_command() const
-{
- string cmd;
- for(vector<string>::const_iterator i=argv.begin(); i!=argv.end(); ++i)
- {
- if(i!=argv.begin())
- cmd += ' ';
-
- for(string::const_iterator j=i->begin(); j!=i->end(); ++j)
- {
- if(*j=='"' || *j=='\'' || *j==' ' || *j=='\\' || *j=='&')
- cmd += '\\';
- cmd += *j;
- }
- }
-
- if(stdin_action==REDIRECT)
- {
- cmd += " <";
- cmd += stdin_file.str();
- }
-
- if(stdout_action==REDIRECT)
- {
- cmd += " >";
- cmd += stdout_file.str();
- }
-
- return cmd;
-}
-
-void ExternalTask::start()
-{
- IO::File *devnull = 0;
- IO::File *infile = 0;
- IO::File *outfile = 0;
-
- prepare();
-
- process = new Process;
-
- if(stdin_action==IGNORE || stdout_action==IGNORE || stderr_action==IGNORE)
- {
- devnull = new IO::File("/dev/null", IO::M_RDWR);
- if(stdin_action==IGNORE)
- process->redirect_cin(*devnull);
- if(stdout_action==IGNORE)
- process->redirect_cout(*devnull);
- if(stderr_action==IGNORE)
- process->redirect_cerr(*devnull);
- }
-
- if(stdout_action==REDIRECT)
- {
- outfile = new IO::File((work_dir/stdout_file).str(), IO::M_WRITE);
- process->redirect_cout(*outfile);
- }
-
- if(stdout_action==CAPTURE || stderr_action==CAPTURE)
- {
- capture_pipe = new IO::Pipe;
- if(stdout_action==CAPTURE)
- process->redirect_cout(*capture_pipe);
- if(stderr_action==CAPTURE)
- process->redirect_cerr(*capture_pipe);
- }
-
- if(stdin_action==REDIRECT)
- {
- infile = new IO::File((work_dir/stdin_file).str());
- process->redirect_cin(*infile);
- }
-
- if(!work_dir.empty())
- process->set_working_directory(work_dir);
-
- Process::Arguments args(argv.begin()+1, argv.end());
- process->execute(argv.front(), args);
- if(capture_pipe)
- capture_pipe->set_mode(IO::M_READ);
-
- delete devnull;
- delete infile;
- delete outfile;
-}
-
-Task::Status ExternalTask::check()
-{
- return do_wait(false);
-}
-
-Task::Status ExternalTask::wait()
-{
- return do_wait(true);
-}
-
-Task::Status ExternalTask::do_wait(bool block)
-{
- while(process)
- {
- if(process->wait(block && !capture_pipe))
- {
- exit_code = process->get_exit_code();
- delete process;
- process = 0;
- }
-
- // Do this after waiting to avoid a race condition
- while(capture_pipe && IO::poll(*capture_pipe, IO::P_INPUT, 10*Time::msec))
- {
- char buf[1024];
- unsigned len = capture_pipe->read(buf, sizeof(buf));
- if(len)
- output.append(buf, len);
- else
- break;
- }
-
- if(process)
- {
- if(!block)
- return RUNNING;
- }
- else
- signal_finished.emit(!exit_code);
- }
-
- return exit_code ? ERROR : SUCCESS;
-}
-
-void ExternalTask::set_stdin(const FS::Path &f)
-{
- stdin_action = REDIRECT;
- stdin_file = f;
-}
-
-void ExternalTask::set_stdout(StreamAction a)
-{
- if(a==REDIRECT)
- throw invalid_argument("ExternalTask::set_stdout");
- stdout_action = a;
-}
-
-void ExternalTask::set_stdout(const FS::Path &f)
-{
- stdout_action = REDIRECT;
- stdout_file = f;
-}
-
-void ExternalTask::set_stderr(StreamAction a)
-{
- if(a==REDIRECT)
- throw invalid_argument("ExternalTask::set_stdout");
- stderr_action = a;
-}
-
-string ExternalTask::run_and_capture_output(const Arguments &argv, const FS::Path &wd, bool capture_stderr)
-{
- ExternalTask task(argv, wd);
- task.stdin_action = IGNORE;
- task.set_stdout(CAPTURE);
- task.set_stderr(capture_stderr ? CAPTURE : IGNORE);
- task.start();
- if(task.wait()!=SUCCESS)
- throw runtime_error(format("%s failed", argv.front()));
- return task.get_output();
-}