From e2dc7122774e5d8de07731f3abe15ec0a9ffa643 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Wed, 30 May 2012 19:28:12 +0000 Subject: [PATCH] Add support for capturing output in ExternalTask --- source/externaltask.cpp | 63 +++++++++++++++++++++++++++++++++++++++-- source/externaltask.h | 18 ++++++++++++ 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/source/externaltask.cpp b/source/externaltask.cpp index 6ff6a3e..0da81e5 100644 --- a/source/externaltask.cpp +++ b/source/externaltask.cpp @@ -2,7 +2,10 @@ #include #include #include +#include +#include #include +#include #include "externaltask.h" using namespace std; @@ -12,9 +15,17 @@ ExternalTask::ExternalTask(const vector &a, const FS::Path &wd): argv(a), work_dir(wd), pid(-1), - exit_code(-1) + exit_code(-1), + stdout_dest(PASSTHROUGH), + stderr_dest(PASSTHROUGH), + capture_pipe(0) { } +ExternalTask::~ExternalTask() +{ + delete capture_pipe; +} + string ExternalTask::get_command() const { string cmd; @@ -36,6 +47,9 @@ string ExternalTask::get_command() const void ExternalTask::start() { + if(stdout_dest==CAPTURE || stderr_dest==CAPTURE) + capture_pipe = new IO::Pipe; + if((pid = fork())) { if(pid==-1) @@ -50,6 +64,24 @@ void ExternalTask::start() cargv[i] = argv[i].c_str(); cargv.back() = 0; + if(stdout_dest==IGNORE || stderr_dest==IGNORE) + { + IO::File devnull("/dev/null", IO::M_WRITE); + if(stdout_dest==IGNORE) + IO::cout.redirect(devnull); + if(stderr_dest==IGNORE) + IO::cerr.redirect(devnull); + } + + if(capture_pipe) + { + if(stdout_dest==CAPTURE) + IO::cout.redirect(*capture_pipe); + if(stderr_dest==CAPTURE) + IO::cerr.redirect(*capture_pipe); + delete capture_pipe; + } + if(!work_dir.empty()) FS::chdir(work_dir); execvp(cargv.front(), const_cast(&cargv.front())); @@ -62,6 +94,7 @@ Task::Status ExternalTask::check() { if(pid>0) { + // XXX This is sub-optimal, should have support for a blocking wait int status; if(waitpid(pid, &status, WNOHANG)==pid) { @@ -72,12 +105,36 @@ Task::Status ExternalTask::check() else exit_code = 1025; pid = 0; + } - signal_finished.emit(!exit_code); + // Do this after waiting to avoid a race condition + if(capture_pipe) + { + while(IO::poll(*capture_pipe, IO::P_INPUT, Time::zero)) + { + char buf[1024]; + unsigned len = capture_pipe->read(buf, sizeof(buf)); + output.append(buf, len); + if(len0) return RUNNING; + else + signal_finished.emit(!exit_code); } return exit_code ? ERROR : SUCCESS; } + +void ExternalTask::set_stdout(Destination d) +{ + stdout_dest = d; +} + +void ExternalTask::set_stderr(Destination d) +{ + stderr_dest = d; +} diff --git a/source/externaltask.h b/source/externaltask.h index 0bd108e..b23bb66 100644 --- a/source/externaltask.h +++ b/source/externaltask.h @@ -4,22 +4,40 @@ #include #include #include +#include #include "task.h" class ExternalTask: public Task { +public: + enum Destination + { + PASSTHROUGH, + CAPTURE, + IGNORE + }; + private: std::vector argv; Msp::FS::Path work_dir; int pid; int exit_code; + Destination stdout_dest; + Destination stderr_dest; + Msp::IO::Pipe *capture_pipe; + std::string output; public: ExternalTask(const std::vector &, const Msp::FS::Path &); + virtual ~ExternalTask(); virtual std::string get_command() const; virtual void start(); virtual Status check(); + + void set_stdout(Destination); + void set_stderr(Destination); + const std::string &get_output() const { return output; } }; #endif -- 2.43.0