diff --git a/mrw/exec.cpp b/mrw/exec.cpp
index 15b3bf3..72411af 100644
--- a/mrw/exec.cpp
+++ b/mrw/exec.cpp
@@ -9,6 +9,9 @@
@license LGPL, see file COPYING
$Log$
+ Revision 1.11 2005/04/19 18:48:00 marc
+ new feature PartialExec
+
Revision 1.10 2005/03/14 16:26:34 marc
bugs have been fixed a long time ago, now no more in buglist
@@ -48,7 +51,12 @@
#include // fork, exec
#include // memcpy
#include // assert
+#include // kill
+#include // kill
+
+//=========================================================== ExecutionFailedExc
+//------------------------------------------------------------------------------
mrw::ExecutionFailedExc::ExecutionFailedExc(const std::string& w,
const std::string& c)
throw(std::bad_exception):
@@ -65,22 +73,28 @@ mrw::ExecutionFailedExc::ExecutionFailedExc(const std::string& w,
*/
}
+//========================================================================== Cmd
+
+//------------------------------------------------------------------------------
mrw::Cmd::Cmd(const std::string& c) throw(std::bad_exception) {
_cmd.push_back(c);
}
+//------------------------------------------------------------------------------
mrw::Cmd& mrw::Cmd::operator,(const std::string& arg)
throw(std::bad_exception) {
_cmd.push_back(arg);
return *this;
}
+//------------------------------------------------------------------------------
mrw::Cmd& mrw::Cmd::operator<<(const std::string& arg)
throw(std::bad_exception) {
_cmd.push_back(arg);
return *this;
}
+//------------------------------------------------------------------------------
mrw::Cmd::operator std::string() const throw(std::bad_exception) {
ArgList::const_iterator it(_cmd.begin());
std::string c(*it);
@@ -88,22 +102,38 @@ mrw::Cmd::operator std::string() const throw(std::bad_exception) {
return c;
}
+//------------------------------------------------------------------------------
mrw::Cmd::operator mrw::Exec() const throw(std::bad_exception) {
return mrw::Exec(*this);
}
+//------------------------------------------------------------------------------
+mrw::Cmd::operator mrw::PartialExec() const throw(std::bad_exception) {
+ return mrw::PartialExec(*this);
+}
+
+//------------------------------------------------------------------------------
mrw::Exec mrw::Cmd::execute(bool exc) const throw(std::exception) {
return mrw::Exec(*this).execute(exc);
}
+//------------------------------------------------------------------------------
mrw::Exec mrw::Cmd::execute(const std::string& input, bool exc) const
throw(std::exception) {
return mrw::Exec(*this).execute(input, exc);
}
+//------------------------------------------------------------------------------
+mrw::PartialExec mrw::Cmd::start(bool useInput) const throw(std::exception) {
+ return mrw::PartialExec(*this).start(useInput);
+}
+
+//------------------------------------------------------------------------------
const char* mrw::Cmd::path() const throw(std::bad_exception) {
return _cmd.front().c_str();
}
+
+//------------------------------------------------------------------------------
char** mrw::Cmd::args() const throw(std::bad_exception) {
if (_cmd.size()==0) return 0;
char** array = new char*[_cmd.size()+1];
@@ -114,25 +144,32 @@ char** mrw::Cmd::args() const throw(std::bad_exception) {
return array;
}
+//========================================================================= Exec
+
+//------------------------------------------------------------------------------
mrw::Exec::Exec(const mrw::Cmd& c) throw(std::bad_exception):
_cmd(new mrw::Cmd(c)), _success(false) {
}
+//------------------------------------------------------------------------------
mrw::Exec::Exec(const mrw::Exec& e) throw(std::bad_exception):
_cmd(new mrw::Cmd(*e._cmd)),
_res(e._res), _err(e._err), _success(e._success) {
}
+//------------------------------------------------------------------------------
mrw::Exec::~Exec() throw() {
delete _cmd;
}
+//------------------------------------------------------------------------------
mrw::Exec& mrw::Exec::operator=(const mrw::Exec& e) throw(std::bad_exception) {
if (this==&e) return *this;
*_cmd=*e._cmd; _res=e._res; _err=e._err; _success=e._success;
return *this;
}
+//------------------------------------------------------------------------------
mrw::Exec& mrw::Exec::execute(bool exc) throw(std::exception) {
/** This method calls @c fork, sets up a pipe connection to pass @c
stdout and @c stderr from the child process to the parent
@@ -167,16 +204,22 @@ mrw::Exec& mrw::Exec::execute(bool exc) throw(std::exception) {
// check and handle stdout
if (num1 && (num1=read(stdOut.istream(), buf, sizeof(buf)))>0)
_res += std::string(buf, num1);
- else if (num1==-1 && (errno!=EINTR&&errno!=EAGAIN))
- throw ExecutionFailedExc("readin stdout", *_cmd);
+ else if (num1==-1)
+ if (errno!=EINTR&&errno!=EAGAIN)
+ throw ExecutionFailedExc("readin stdout", *_cmd);
+ else
+ num1 = 1;
// check and handle stderr
if (num2 && (num2=read(stdErr.istream(), buf, sizeof(buf)))>0)
_err += std::string(buf, num2);
- else if (num2==-1 && (errno!=EINTR&&errno!=EAGAIN))
- throw ExecutionFailedExc("readin stderr", *_cmd);
+ else if (num2==-1)
+ if (errno!=EINTR&&errno!=EAGAIN)
+ throw ExecutionFailedExc("readin stderr", *_cmd);
+ else
+ num2 = 1;
} catch (...) {
- if (exc) throw;
_success = false;
+ if (exc) throw;
return *this;
}
} else { // child
@@ -191,6 +234,7 @@ mrw::Exec& mrw::Exec::execute(bool exc) throw(std::exception) {
return *this;
}
+//------------------------------------------------------------------------------
mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc)
throw(std::exception) {
/// @c input length must be smaller than @c SSIZE_MAX.
@@ -199,7 +243,7 @@ mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc)
"sdin input exeeds C library limit in mrw::Exec "
"please contact the author of the library");
/** This method calls @c fork, sets up a pipe connection to pass @c
- stdin, @c stdout and @c stderr from the child process to the
+ stdin, @c stdout and @c stderr between the child process and the
parent process using mrw::Pipe and calls @c execvp to execute
the program. */
_success = false;
@@ -236,21 +280,30 @@ mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc)
in = in.substr(num0);
else if ((unsigned int)num0==in.size())
num0=0, stdIn.close_out();
- } else if (num0==-1 && (errno!=EINTR&&errno!=EAGAIN))
- throw ExecutionFailedExc("writing stdin", *_cmd);
+ } else if (num0==-1)
+ if (errno!=EINTR&&errno!=EAGAIN)
+ throw ExecutionFailedExc("writing stdin", *_cmd);
+ else
+ num0 = 1;
// check and handle stdout
if (num1 && (num1=read(stdOut.istream(), buf, sizeof(buf)))>0)
_res += std::string(buf, num1);
- else if (num1==-1 && (errno!=EINTR&&errno!=EAGAIN))
- throw ExecutionFailedExc("readin stdout", *_cmd);
+ else if (num1==-1)
+ if (errno!=EINTR&&errno!=EAGAIN)
+ throw ExecutionFailedExc("readin stdout", *_cmd);
+ else
+ num1 = 1;
// check and handle stderr
if (num2 && (num2=read(stdErr.istream(), buf, sizeof(buf)))>0)
_err += std::string(buf, num2);
- else if (num2==-1 && (errno!=EINTR&&errno!=EAGAIN))
- throw ExecutionFailedExc("readin stderr", *_cmd);
+ else if (num2==-1)
+ if (errno!=EINTR&&errno!=EAGAIN)
+ throw ExecutionFailedExc("readin stderr", *_cmd);
+ else
+ num2 = 1;
} catch (...) {
- if (exc) throw;
_success = false;
+ if (exc) throw;
return *this;
}
} else { // child
@@ -267,31 +320,218 @@ mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc)
return *this;
}
+//------------------------------------------------------------------------------
mrw::Exec& mrw::Exec::operator>>(std::string& res) throw(std::exception) {
execute();
res += _res;
return *this;
}
+//------------------------------------------------------------------------------
mrw::Exec::operator std::string&() throw(std::exception) {
if (!_success) execute();
return _res;
}
+//------------------------------------------------------------------------------
mrw::Exec::operator bool() throw(std::bad_exception) {
return _success;
}
+//------------------------------------------------------------------------------
std::string& mrw::Exec::result() throw(std::exception) {
if (!_success) execute();
return _res;
}
+//------------------------------------------------------------------------------
std::string& mrw::Exec::error() throw(std::exception) {
if (!_success) execute();
return _err;
}
+//------------------------------------------------------------------------------
bool mrw::Exec::success() throw(std::bad_exception) {
return _success;
}
+
+//================================================================== PartialExec
+
+//------------------------------------------------------------------------------
+mrw::PartialExec::PartialExec(const mrw::Cmd& c) throw(std::bad_exception):
+ Exec(c), _finished(true), _finish(false) {
+}
+
+//------------------------------------------------------------------------------
+mrw::PartialExec::PartialExec(mrw::PartialExec& e)
+ throw(std::bad_exception):
+ Exec(e), _finished(e._finished), _finish(e._finish),
+ _stdIn(e._stdIn), _stdOut(e._stdOut), _stdErr(e._stdErr), _input(e._input),
+ _num0(e._num0), _num1(e._num1), _num2(e._num2),
+ _lastPid(e._lastPid), _pid(e._pid) {
+ e._finished = true;
+}
+
+//------------------------------------------------------------------------------
+mrw::PartialExec::PartialExec(const mrw::PartialExec& e)
+ throw(std::bad_exception):
+ Exec(e), _finished(e._finished), _finish(e._finish),
+ _input(e._input),
+ _num0(e._num0), _num1(e._num1), _num2(e._num2),
+ _lastPid(e._lastPid), _pid(e._pid) {
+ /// @warning @c const is casted away
+ mrw::PartialExec& nonConstE(const_cast(e));
+ _stdIn = nonConstE._stdIn;
+ _stdOut = nonConstE._stdOut;
+ _stdErr = nonConstE._stdErr;
+ nonConstE._finished = true;
+ nonConstE._finish = false;
+}
+
+//------------------------------------------------------------------------------
+mrw::PartialExec& mrw::PartialExec::operator=(mrw::PartialExec& e)
+ throw(std::bad_exception) {
+ if (this==&e) return *this;
+ *_cmd=*e._cmd; _res=e._res; _err=e._err; _success=e._success;
+ _finished = e._finished;
+ _finish = e._finish;
+ _stdIn = e._stdIn; _stdOut = e._stdOut; _stdErr = e._stdErr;
+ _input = e._input; _num0 = e._num0; _num1 = e._num1; _num2 = e._num2;
+ _lastPid = e._lastPid;
+ _pid = e._pid;
+ e._finished = true;
+ e._finish = false;
+ return *this;
+}
+
+//------------------------------------------------------------------------------
+mrw::PartialExec& mrw::PartialExec::finish() throw() {
+ _finish = true;
+ return *this;
+}
+
+//------------------------------------------------------------------------------
+bool mrw::PartialExec::finished() throw() {
+ return _finished;
+}
+
+//------------------------------------------------------------------------------
+mrw::PartialExec& mrw::PartialExec::start(bool useInput)
+ throw(std::exception) {
+ if (!_finished) throw mrw::runtime_error("running process not yet finished");
+ /** This method calls @c fork, sets up a pipe connection to pass @c
+ stdin, @c stdout and @c stderr between the child process and the
+ parent process using mrw::Pipe and calls @c execvp to execute
+ the program. */
+ _success = false;
+ _finish = !useInput;
+ _input = "";
+ _res = _err = "";
+ _stdIn = std::auto_ptr(new mrw::Pipe(mrw::Pipe::block_input));
+ _stdOut = std::auto_ptr(new mrw::Pipe(mrw::Pipe::block_output));
+ _stdErr = std::auto_ptr(new mrw::Pipe(mrw::Pipe::block_output));
+ if (!*_stdIn || !*_stdOut || !*_stdErr)
+ throw mrw::ExecutionFailedExc("cannot create pipe", *_cmd);
+ _lastPid = 0;
+ _pid = fork();
+ if (_pid<0) throw ExecutionFailedExc("cannot fork", *_cmd);
+ if (_pid) { // parent
+ _stdIn->close_in();
+ _stdOut->close_out();
+ _stdErr->close_out();
+ if (!*_stdIn || !*_stdOut || !*_stdErr)
+ throw ExecutionFailedExc("cannot close pipes", *_cmd);
+ _num0 = _num1 = _num2 = 1;
+ _finished = false;
+ } else { // child
+ _stdIn->close_out();
+ _stdOut->close_in();
+ _stdErr->close_in();
+ _stdIn->connect_cin();
+ _stdOut->connect_cout();
+ _stdErr->connect_cerr();
+ execvp(_cmd->path(), _cmd->args());
+ exit(1); // execute failed
+ }
+ _success = true;
+ return *this;
+}
+
+//------------------------------------------------------------------------------
+std::pair
+ mrw::PartialExec::read(const std::string& input, bool exc)
+ throw(std::exception) {
+ std::pair output;
+ /** @note @c input length must be smaller than @c SSIZE_MAX.
+ I'll only add support for longer strings upon request. */
+ assert(input.size()<=SSIZE_MAX &&
+ "sdin input exeeds C library limit in mrw::Exec "
+ "please contact the author of the library");
+ /** @warning After calling finish(), it is forbidden to pass
+ any @c input string, it must then always be empty,
+ because the pipe is already closed! */
+ assert(!(_finish && input.size()) &&
+ "after calling PartialExec::finish(), it is forbidden "
+ "to pass new input text, because the pipe is already "
+ "closed! this is a programming but that must be solved!");
+ _input += input;
+ /* It sometimes did not get the whole input since I changed to non
+ blocking pipes. Now I have found the solution to the problems:
+ Non blocking IO does not work in conjunction with select! Also
+ the pipe must not be nonblocking on both ends, but only on
+ one. */
+ if (!_finished) try { // not finished
+ char buf[4096];
+ int s(0);
+ if (!_lastPid && (_lastPid=waitpid(_pid, &s, WNOHANG))) {
+ if (_lastPid!=_pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0)
+ throw ExecutionFailedExc("execution failed", *_cmd);
+ }
+ // check and handle stdin
+ if (_input.size() && _num0!=-1 &&
+ (_num0=write(_stdIn->ostream(), _input.c_str(), _input.size()))>0) {
+ if ((unsigned int)_num0<_input.size())
+ _input = _input.substr(_num0);
+ else if ((unsigned int)_num0==_input.size())
+ _input.clear();
+ } else if (_num0==-1)
+ if (errno!=EINTR&&errno!=EAGAIN)
+ throw ExecutionFailedExc("writing stdin", *_cmd);
+ else
+ _num0 = 1;
+ // check and handle stdout
+ if (_num1 && (_num1=::read(_stdOut->istream(), buf, sizeof(buf)))>0)
+ _res += output.first=std::string(buf, _num1);
+ else if (_num1==-1)
+ if (errno!=EINTR&&errno!=EAGAIN)
+ throw ExecutionFailedExc("readin stdout", *_cmd);
+ else
+ _num1 = 1;
+ // check and handle stderr
+ if (_num2 && (_num2=::read(_stdErr->istream(), buf, sizeof(buf)))>0)
+ _err += output.second=std::string(buf, _num2);
+ else if (_num2==-1)
+ if (errno!=EINTR&&errno!=EAGAIN)
+ throw ExecutionFailedExc("readin stderr", *_cmd);
+ else
+ _num2 = 1;
+ if (_finish && !_input.size()) {
+ _stdIn->close_out();
+ _num0 = 0;
+ }
+ _finished = _finish && !_input.size() && !_num1 && !_num2 && _lastPid;
+ } catch (mrw::exception& x) {
+ _finished = true;
+ _success = false;
+ if (exc) throw;
+ return output;
+ }
+ if (_finished) _success = true;
+ return output;
+}
+
+//------------------------------------------------------------------------------
+mrw::PartialExec& mrw::PartialExec::terminate() throw() {
+ kill(_pid, SIGTERM);
+ return *this;
+}
diff --git a/mrw/exec.hpp b/mrw/exec.hpp
index 5a93a27..92e3737 100644
--- a/mrw/exec.hpp
+++ b/mrw/exec.hpp
@@ -9,6 +9,9 @@
@license LGPL, see file COPYING
$Log$
+ Revision 1.6 2005/04/19 18:48:00 marc
+ new feature PartialExec
+
Revision 1.5 2004/12/14 20:30:09 marc
added possibility to pass string to stdin of child process
@@ -35,6 +38,7 @@
#include
#include
#include
+#include
namespace mrw {
@@ -81,12 +85,12 @@ namespace mrw {
// "cat" passes all from stdin to stdout, therefore:
assert(cat.result()=="this is passed to stdin");
} catch (...) {} // ignore
- @endcode
- */
+ @endcode */
//@{
class Cmd;
-
+
+ //============================================================================
/** @brief Exception: Execution of command failed.
@pre #include
@@ -95,18 +99,18 @@ namespace mrw {
to create the necessary pipes, or the command executing process
terminated with an error. In the last case, you can access the
error stream from @c stderr respectively @c cerr with method
- mrw::Exec::error().
- */
+ mrw::Exec::error(). */
class ExecutionFailedExc: public mrw::exception {
- public:
- ExecutionFailedExc(const std::string&, const std::string&)
- throw(std::bad_exception);
- virtual ~ExecutionFailedExc() throw() {}
- virtual const char* what() const throw() {return _what.c_str();}
- private:
- std::string _what;
+ public:
+ ExecutionFailedExc(const std::string&, const std::string&)
+ throw(std::bad_exception);
+ virtual ~ExecutionFailedExc() throw() {}
+ virtual const char* what() const throw() {return _what.c_str();}
+ private:
+ std::string _what;
};
+ //============================================================================
/** @brief Execute a command in a new process.
@pre #include
@@ -137,140 +141,295 @@ namespace mrw {
// ls.error() contains stderr
@endcode
- @note Please note that the command execution may throw an exception.
- */
+ @note Please note that the command execution may throw an exception. */
class Exec {
- public:
+
+ //................................................................ methods
+ public:
- /** @brief Create an executor given a command.
- Construction without passing a command is not possible. */
- Exec(const mrw::Cmd&) throw(std::bad_exception);
+ /** @brief Create an executor given a command.
+ Construction without passing a command is not possible. */
+ Exec(const mrw::Cmd&) throw(std::bad_exception);
- Exec(const mrw::Exec&) throw(std::bad_exception);
- ~Exec() throw();
- Exec& operator=(const mrw::Exec&) throw(std::bad_exception);
+ Exec(const mrw::Exec&) throw(std::bad_exception);
+ ~Exec() throw();
+ Exec& operator=(const mrw::Exec&) throw(std::bad_exception);
- /** @brief Execute the command.
+ /** @brief Execute the command.
- @param exc
- - @c true throw an exception if return status is not zero
- - @c false throw only an exception in case of a fatal error
+ @param exc
+ - @c true throw an exception if return status is not zero
+ - @c false throw only an exception in case of a fatal error
- @throw ExecutionFailedExc is thrown if
- - fork fails
- - creation or setup of pipes failed
- - if given parameter is @c true (the default) also if the
- executed program terminates with an error
- */
- Exec& execute(bool exc=true) throw(std::exception);
+ @throw ExecutionFailedExc is thrown if
+ - fork fails
+ - creation or setup of pipes failed
+ - if given parameter is @c true (the default) also if the
+ executed program terminates with an error */
+ Exec& execute(bool exc=true) throw(std::exception);
- /** @brief Execute the command, pass @c stdin.
+ /** @brief Execute the command, pass @c stdin.
- @param input Input that is passed to @c stdin of the child process.
+ @param input Input that is passed to @c stdin of the child process.
- @param exc
- - @c true throw an exception if return status is not zero
- - @c false throw only an exception in case of a fatal error
+ @param exc
+ - @c true throw an exception if return status is not zero
+ - @c false throw only an exception in case of a fatal error
- @throw ExecutionFailedExc is thrown if
- - fork fails
- - creation or setup of pipes failed
- - if given parameter is @c true (the default) also if the
- executed program terminates with an error
- */
- Exec& execute(const std::string& input, bool exc=true)
- throw(std::exception);
+ @throw ExecutionFailedExc is thrown if
+ - fork fails
+ - creation or setup of pipes failed
+ - if given parameter is @c true (the default) also if the
+ executed program terminates with an error */
+ Exec& execute(const std::string& input, bool exc=true)
+ throw(std::exception);
- /** @brief Execute the command, pass @c stdin.
+ /** @brief Execute the command, pass @c stdin.
- @param input Input that is passed to @c stdin of the child process.
+ @param input Input that is passed to @c stdin of the child process.
- @param exc
- - @c true throw an exception if return status is not zero
- - @c false throw only an exception in case of a fatal error
+ @param exc
+ - @c true throw an exception if return status is not zero
+ - @c false throw only an exception in case of a fatal error
- @throw ExecutionFailedExc is thrown if
- - fork fails
- - creation or setup of pipes failed
- - if given parameter is @c true (the default) also if the
- executed program terminates with an error
- */
- Exec& execute(char const*const input, bool exc=true)
- throw(std::exception) {
- return execute(std::string(input), exc);
- }
-
- /** @brief Executes the command if not done, streams @c stdout into a string
+ @throw ExecutionFailedExc is thrown if
+ - fork fails
+ - creation or setup of pipes failed
+ - if given parameter is @c true (the default) also if the
+ executed program terminates with an error */
+ Exec& execute(char const*const input, bool exc=true)
+ throw(std::exception) {
+ return execute(std::string(input), exc);
+ }
+
+ /** @brief Executes the command if not done, streams @c stdout
+ into a string
- If the command has not yet been executed successfully, it is
- first executed, then the @c stdout output of the called
- program is appended to the string.
+ If the command has not yet been executed successfully, it is
+ first executed, then the @c stdout output of the called
+ program is appended to the string.
- @throw ExecutionFailedExc in case of any failure or if the
- executed program does not return a zero exit status.
- */
- Exec& operator>>(std::string&) throw(std::exception);
+ @throw ExecutionFailedExc in case of any failure or if the
+ executed program does not return a zero exit status. */
+ Exec& operator>>(std::string&) throw(std::exception);
- /** @brief Executes the command if not done, returns @c stdout as string
+ /** @brief Executes the command if not done, returns @c stdout as string
- If the command has not yet been executed successfully, it is
- first executed, then the @c stdout output of the called
- program is returned.
+ If the command has not yet been executed successfully, it is
+ first executed, then the @c stdout output of the called
+ program is returned.
- @return @c stdout of the called program
+ @return @c stdout of the called program
- @throw ExecutionFailedExc in case of any failure or if the
- executed program does not return a zero exit status.
- */
- operator std::string&() throw(std::exception);
+ @throw ExecutionFailedExc in case of any failure or if the
+ executed program does not return a zero exit status. */
+ operator std::string&() throw(std::exception);
- /** @return
- - @c true if the last execution was successful
- - @c false if the last execution failed or the command was
- never executed
- */
- operator bool() throw(std::bad_exception);
+ /** @return
+ - @c true if the last execution was successful
+ - @c false if the last execution failed or the command was
+ never executed */
+ operator bool() throw(std::bad_exception);
- /** @brief Executes the command if not done, returns @c stdout as string
+ /** @brief Executes the command if not done, returns @c stdout as string
- If the command has not yet been executed successfully, it is
- first executed, then the @c stdout output of the called
- program is returned.
+ If the command has not yet been executed successfully, it is
+ first executed, then the @c stdout output of the called
+ program is returned.
- @return @c stdout of the called program
+ @return @c stdout of the called program
- @throw ExecutionFailedExc in case of any failure or if the
- executed program does not return a zero exit status.
- */
- std::string& result() throw(std::exception);
+ @throw ExecutionFailedExc in case of any failure or if the
+ executed program does not return a zero exit status. */
+ std::string& result() throw(std::exception);
- /** @brief Executes the command if not done, returns @c stderr as string
+ /** @brief Executes the command if not done, returns @c stderr as string
- If the command has not yet been executed successfully, it is
- first executed, then the @c stderr error output of the called
- program is returned.
+ If the command has not yet been executed successfully, it is
+ first executed, then the @c stderr error output of the called
+ program is returned.
- @return @c stderr of the called program
+ @return @c stderr of the called program
- @throw ExecutionFailedExc in case of any failure or if the
- executed program does not return a zero exit status.
- */
- std::string& error() throw(std::exception);
+ @throw ExecutionFailedExc in case of any failure or if the
+ executed program does not return a zero exit status. */
+ std::string& error() throw(std::exception);
- /** @return
- - @c true if the last execution was successful
- - @c false if the last execution failed or the command was
- never executed
- */
- bool success() throw(std::bad_exception);
-
- private:
- Exec(); // no default constructor
- mrw::Cmd* _cmd;
- std::string _res, _err;
- bool _success;
+ /** @return
+ - @c true if the last execution was successful
+ - @c false if the last execution failed or the command was
+ never executed */
+ bool success() throw(std::bad_exception);
+
+ //................................................................ methods
+ private:
+
+ Exec(); // no default constructor
+
+ //.............................................................. variables
+ private:
+
+ friend class PartialExec; // don't want the variables protected
+ mrw::Cmd* _cmd;
+ std::string _res, _err;
+ bool _success;
};
+ //============================================================================
+ /** @brief Execute a UNIX program in non blocking parts.
+ @pre #include
+
+ A given UNIX command is executed, but the class does not wait
+ until it is finished, instead it gives back the control to the
+ caller. This behaviour is achieved using non blocking
+ communication. But the caller is responsible to retrieve all
+ information from the client, and if necessary to close the input
+ pipe of the client executable program. Therefore you have to
+ give back control from time to time, normally this is doen in a
+ @c while loop, where you can execute also different thing,
+ e.g. update a display of the result or similar.
+
+ With this class, you can communicate with a child process, and
+ do other things at the same time, without the need for multi
+ threading.
+
+ Execution of a program works the following way:
+ - do not use execute() (otherwise the behaviours is identical
+ to class mrw::Exec, you gain nothing, but also loose nothing)
+ - use start() to start the external program
+ - use start() or @c start(false) if you don't want to pass
+ input to the child process
+ - use @c start(true) if you want to pass input to the
+ child process
+ - if you called @c start(true), call finish() if you have no more
+ input to send to the child process (it's like an end-of-file)
+ - the execution is not terminated, before finished() returns
+ @c true
+ - while finished() is false, subsequently call read() to read
+ the output of the child process
+
+ @warning After calling finish(), or if you did not call
+ start() with parameter @c true, it is forbidden to
+ pass anything but an empty string as first parameter to
+ read()! Anything else is a programming error and
+ results in an assertion failure and a core dump!
+
+ @note If your program seems to hang, check if you call finish()
+ correctly!
+
+ Here an example:
+ @code
+mrw::PartialExec exec = mrw::Cmd("/bin/cat").start(true);
+std::string res = exec.read("This is a test\n").first;
+res += exec.read("This is another test\n").first;
+exec.finish(); // close the input pipe of @c cat
+while (!exec.finished()) res+=exec.read().first;
+@endcode */
+ class PartialExec: public Exec {
+
+ //................................................................ methods
+ public:
+
+ /** @brief Create an executor given a command.
+ Construction without passing a command is not possible. */
+ PartialExec(const mrw::Cmd&) throw(std::bad_exception);
+
+ /** @brief Copy construction invalidates the original object.
+
+ All opened pipes (opened with start()) are lost in the
+ original object and are then owned by the new object. */
+ PartialExec(mrw::PartialExec&) throw(std::bad_exception);
+
+ /** @brief Copy construction invalidates the original object.
+
+ @copydoc PartialExec(mrw::PartialExec&)
+
+ @warning @c const for the argument is a fake! It is casted away!
+
+ @param e @b Warning: const is casted away! */
+ PartialExec(const mrw::PartialExec& e) throw(std::bad_exception);
+
+ /** @brief Assignment invalidates the original object.
+
+ @copydoc PartialExec(mrw::PartialExec&) */
+ PartialExec& operator=(mrw::PartialExec&) throw(std::bad_exception);
+
+ /** @brief Close the input pipe of the child process.
+
+ If start() is called with argument @c false, then you
+ can pass input to @c stdin of the child process, but you @b
+ must call this method, after passing the last input
+ string. Otherwise, the child's input pipe won't be closed,
+ the child process does not stop waiting for more input! If
+ your program seems to hang, check if you call finish()
+ correctly! */
+ PartialExec& finish() throw();
+
+ /** @brief Check if there's more data left to read().
+ @return @c true if the child process has finished and all
+ data is read. */
+ bool finished() throw();
+
+ /** @brief Start a new child process.
+
+ At most one child process can run at the same time.
+
+ @throw mrw::runtime_error if a previous child has not finished() yet
+ @throw mrw::ExecutionFailedExc if the child process cannot be started
+ @param useInput
+ - @c true if input will be sent to the child's @c stdin
+ - pass all input in the first parameter of read()
+ - finish() must be called when all input is sent
+ - @c false if no input is sent to the child's @c stdin
+ - the first parameter of read must allways be passed an
+ empty string */
+ PartialExec& start(bool useInput=false) throw(std::exception);
+
+ /** @brief Read from the subprocess, optionally pass an @c input to
+ @c stdin of the subprocess.
+
+ @param input a string to pass to the child processes @c stdin
+
+ @param exc
+ - @c true throw an exception if return status is not zero
+ - @c false throw only an exception in case of a fatal error
+
+ @return a pair containing the last read @c stdout and @c stderr
+ of the child
+
+ @throw ExecutionFailedExc is thrown if
+ - fork fails
+ - creation or setup of pipes failed
+ - if given parameter is @c true (the default) also if the
+ executed program terminates with an error
+
+ @note If start() was not called with parameter @c true, then
+ @c input must always be an empty string!
+
+ @pre start() was called */
+ std::pair read(const std::string& input="",
+ bool exc=true)
+ throw(std::exception);
+
+ /// Terminates a running job by sending @c SIGTERM to the child process.
+ PartialExec& terminate() throw();
+
+ //................................................................ methods
+ private:
+
+ PartialExec(); // no default constructor
+
+ //.............................................................. variables
+ private:
+
+ bool _finished;
+ bool _finish;
+ std::auto_ptr _stdIn, _stdOut, _stdErr;
+ std::string _input;
+ int _num0, _num1, _num2, _lastPid, _pid;
+ };
+
+ //============================================================================
/** @brief A system command to be executed
@pre #include
@@ -293,115 +452,62 @@ namespace mrw {
@endcode
*/
class Cmd {
- public:
- /** @brief Create a command given the name of the executable
- @param command the name of the program to execute (no parameter)
- @note There is no default constructor. */
- Cmd(const std::string& command) throw(std::bad_exception);
-
- /** @brief Append a parameter to a command
- @param param a parameter / commandline argument
- to append to the command */
- Cmd& operator,(const std::string& param) throw(std::bad_exception);
+ public:
+ /** @brief Create a command given the name of the executable
+ @param command the name of the program to execute (no parameter)
+ @note There is no default constructor. */
+ Cmd(const std::string& command) throw(std::bad_exception);
+
+ /** @brief Append a parameter to a command
+ @param param a parameter / commandline argument
+ to append to the command */
+ Cmd& operator,(const std::string& param) throw(std::bad_exception);
- /** @brief Append a parameter to a command
- @param param a parameter / commandline argument
- to append to the command */
- Cmd& operator<<(const std::string& param) throw(std::bad_exception);
+ /** @brief Append a parameter to a command
+ @param param a parameter / commandline argument
+ to append to the command */
+ Cmd& operator<<(const std::string& param) throw(std::bad_exception);
- /** @return the command including parameter */
- operator std::string() const throw(std::bad_exception);
+ /** @return the command including parameter */
+ operator std::string() const throw(std::bad_exception);
- /** @return a mrw::Exec that's constructed with this class */
- operator mrw::Exec() const throw(std::bad_exception);
+ /** @return a mrw::Exec that's constructed with this class */
+ operator mrw::Exec() const throw(std::bad_exception);
- /** @brief Create a mrw::Exec and execute the command
-
- Creates a mrw::Exec, executes the command, passes the flag to
- mrw::Exec::execute() and returns the created mrw::Exec. The
- result of the execution can be retrieved through the returned
- mrw::Exec object: The methods mrw::Exec::success(),
- mrw::Exec::result() and mrw::Exec::error() provide the
- necessary information.
-
- @param exc
- - @c true throw an exception if return status is not zero
- - @c false throw only an exception in case of a fatal error
+ /** @return a mrw::PartialExec that's constructed with this class */
+ operator mrw::PartialExec() const throw(std::bad_exception);
- @return the mrw::Exec that has executed the command
-
- @throw ExecutionFailedExc is thrown if
- - fork fails
- - creation or setup of pipes failed
- - if given parameter is @c true (the default) also if the
- executed program terminates with an error
- */
- Exec execute(bool exc=true) const throw(std::exception);
+ /** @brief Create a mrw::Exec and execute a child process.
+ @see Exec::execute(bool) */
+ Exec execute(bool exc=true) const throw(std::exception);
- /** @brief Create a mrw::Exec and execute the command given an input
-
- Creates a mrw::Exec, executes the command, passes the input
- and the flag to mrw::Exec::execute() and returns the created
- mrw::Exec. The result of the execution can be retrieved
- through the returned mrw::Exec object: The methods
- mrw::Exec::success(), mrw::Exec::result() and
- mrw::Exec::error() provide the necessary information.
-
- @param input Input that is passed to @c stdin of the child process.
-
- @param exc
- - @c true throw an exception if return status is not zero
- - @c false throw only an exception in case of a fatal error
-
- @return the mrw::Exec that has executed the command
-
- @throw ExecutionFailedExc is thrown if
- - fork fails
- - creation or setup of pipes failed
- - if given parameter is @c true (the default) also if the
- executed program terminates with an error
- */
- Exec execute(const std::string& input, bool exc=true) const
- throw(std::exception);
+ /** @brief Create a mrw::Exec and execute a child process.
+ @see Exec::execute(const std::string&, bool) */
+ Exec execute(const std::string& input, bool exc=true) const
+ throw(std::exception);
- /** @brief Create a mrw::Exec and execute the command given an input
-
- Creates a mrw::Exec, executes the command, passes the input
- and the flag to mrw::Exec::execute() and returns the created
- mrw::Exec. The result of the execution can be retrieved
- through the returned mrw::Exec object: The methods
- mrw::Exec::success(), mrw::Exec::result() and
- mrw::Exec::error() provide the necessary information.
-
- @param input Input that is passed to @c stdin of the child process.
-
- @param exc
- - @c true throw an exception if return status is not zero
- - @c false throw only an exception in case of a fatal error
+ /** @brief Create a mrw::Exec and execute a child process.
+ @see Exec::execute(char const*const, bool) */
+ Exec execute(char const*const input, bool exc=true) const
+ throw(std::exception) {
+ return execute(std::string(input), exc);
+ }
- @return the mrw::Exec that has executed the command
-
- @throw ExecutionFailedExc is thrown if
- - fork fails
- - creation or setup of pipes failed
- - if given parameter is @c true (the default) also if the
- executed program terminates with an error
- */
- Exec execute(char const*const input, bool exc=true) const
- throw(std::exception) {
- return execute(std::string(input), exc);
- }
+ /** @brief Create a new mrw::PartialExec and start a new child process.
+ @see PartialExec::start(bool) */
+ PartialExec start(bool useInput=false) const throw(std::exception);
private:
- /// Exec is allowed to call @c path() and @c args().
+ // Exec and PartialExec are allowed to call @c path() and @c args().
friend class Exec;
- /// No default constructor.
- Cmd();
+ friend class PartialExec;
+ Cmd(); // No default constructor.
const char* path() const throw(std::bad_exception);
char** args() const throw(std::bad_exception);
typedef std::list ArgList;
ArgList _cmd;
};
+
//@}
}
#endif
diff --git a/mrw/exec_test.cpp b/mrw/exec_test.cpp
index 542f838..a0130c8 100644
--- a/mrw/exec_test.cpp
+++ b/mrw/exec_test.cpp
@@ -9,6 +9,9 @@
@license LGPL, see file COPYING
$Log$
+ Revision 1.9 2005/04/19 18:48:00 marc
+ new feature PartialExec
+
Revision 1.8 2005/04/07 20:55:21 marc
Oops, there's a make distcheck...? Now it works.
@@ -65,12 +68,44 @@ public:
void unexpectedExc() throw(std::bad_exception) {
std::string res = (mrw::Cmd("/bin/false")).execute().result();
}
+ void lsTest2() {
+ std::string res;
+ mrw::PartialExec exec = (mrw::Cmd("/bin/ls"), "-l",
+ std::string(getenv("srcdir"))+"/..").start();
+ while (!exec.finished()) res+=exec.read().first;
+ CPPUNIT_ASSERT(res.find("COPYING")