new feature PartialExec
This commit is contained in:
		
							
								
								
									
										266
									
								
								mrw/exec.cpp
									
									
									
									
									
								
							
							
						
						
									
										266
									
								
								mrw/exec.cpp
									
									
									
									
									
								
							@@ -9,6 +9,9 @@
 | 
			
		||||
    @license LGPL, see file <a href="license.html">COPYING</a>
 | 
			
		||||
 | 
			
		||||
    $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 <unistd.h> // fork, exec
 | 
			
		||||
#include <string.h> // memcpy
 | 
			
		||||
#include <assert.h> // assert
 | 
			
		||||
#include <sys/types.h> // kill
 | 
			
		||||
#include <signal.h> // 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<mrw::PartialExec&>(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<mrw::Pipe>(new mrw::Pipe(mrw::Pipe::block_input));
 | 
			
		||||
  _stdOut = std::auto_ptr<mrw::Pipe>(new mrw::Pipe(mrw::Pipe::block_output));
 | 
			
		||||
  _stdErr = std::auto_ptr<mrw::Pipe>(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<std::string, std::string>
 | 
			
		||||
  mrw::PartialExec::read(const std::string& input, bool exc)
 | 
			
		||||
    throw(std::exception) {
 | 
			
		||||
  std::pair<std::string, std::string> 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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										536
									
								
								mrw/exec.hpp
									
									
									
									
									
								
							
							
						
						
									
										536
									
								
								mrw/exec.hpp
									
									
									
									
									
								
							@@ -9,6 +9,9 @@
 | 
			
		||||
    @license LGPL, see file <a href="license.html">COPYING</a>
 | 
			
		||||
 | 
			
		||||
    $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 <string>
 | 
			
		||||
#include <list>
 | 
			
		||||
#include <mrw/exception.hpp>
 | 
			
		||||
#include <mrw/unistd.hpp>
 | 
			
		||||
 | 
			
		||||
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 <mrw/exec.hpp>
 | 
			
		||||
 | 
			
		||||
@@ -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 <mrw/exec.hpp>
 | 
			
		||||
 | 
			
		||||
@@ -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);
 | 
			
		||||
    }
 | 
			
		||||
          @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.
 | 
			
		||||
 | 
			
		||||
          @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, streams @c stdout into a 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 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 returned.
 | 
			
		||||
 | 
			
		||||
        @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);
 | 
			
		||||
          @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);
 | 
			
		||||
 | 
			
		||||
      /** @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
 | 
			
		||||
 | 
			
		||||
          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
 | 
			
		||||
 | 
			
		||||
          @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
 | 
			
		||||
 | 
			
		||||
          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
 | 
			
		||||
 | 
			
		||||
          @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);
 | 
			
		||||
    
 | 
			
		||||
    /** @brief Executes the command if not done, returns @c stdout as string
 | 
			
		||||
      /** @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);
 | 
			
		||||
 | 
			
		||||
        If the command has not yet been executed successfully, it is
 | 
			
		||||
        first executed, then the @c stdout output of the called
 | 
			
		||||
        program is returned.
 | 
			
		||||
      //................................................................ methods
 | 
			
		||||
    private:
 | 
			
		||||
      
 | 
			
		||||
      Exec(); // no default constructor
 | 
			
		||||
 | 
			
		||||
        @return @c stdout of the called program
 | 
			
		||||
      //.............................................................. variables
 | 
			
		||||
    private:
 | 
			
		||||
 | 
			
		||||
        @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);
 | 
			
		||||
 | 
			
		||||
    /** @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.
 | 
			
		||||
 | 
			
		||||
        @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);
 | 
			
		||||
 | 
			
		||||
    /** @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.
 | 
			
		||||
 | 
			
		||||
        @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);
 | 
			
		||||
    
 | 
			
		||||
    /** @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;
 | 
			
		||||
      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 <mrw/exec.hpp>
 | 
			
		||||
 | 
			
		||||
      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<std::string, std::string> 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<mrw::Pipe> _stdIn, _stdOut, _stdErr;
 | 
			
		||||
      std::string _input;
 | 
			
		||||
      int _num0, _num1, _num2, _lastPid, _pid;
 | 
			
		||||
  };
 | 
			
		||||
  
 | 
			
		||||
  //============================================================================
 | 
			
		||||
  /** @brief A system command to be executed
 | 
			
		||||
      @pre #include <mrw/exec.hpp>
 | 
			
		||||
 | 
			
		||||
@@ -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);
 | 
			
		||||
    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);
 | 
			
		||||
      /** @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
 | 
			
		||||
      /** @return a mrw::PartialExec that's constructed with this class */
 | 
			
		||||
      operator mrw::PartialExec() const throw(std::bad_exception);
 | 
			
		||||
 | 
			
		||||
        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 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
 | 
			
		||||
      /** @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);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
        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(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<std::string> ArgList;
 | 
			
		||||
    ArgList _cmd;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  //@}
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,9 @@
 | 
			
		||||
    @license LGPL, see file <a href="license.html">COPYING</a>
 | 
			
		||||
 | 
			
		||||
    $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")<res.size());
 | 
			
		||||
  }
 | 
			
		||||
  void catTest2() {
 | 
			
		||||
    mrw::PartialExec exec = mrw::Cmd("/bin/cat").start(true);
 | 
			
		||||
    std::string res = exec.read("This is a test\n").first;
 | 
			
		||||
    res += exec.read("More to come...\n").first;
 | 
			
		||||
    exec.finish();
 | 
			
		||||
    while (!exec.finished()) res+=exec.read().first;
 | 
			
		||||
    CPPUNIT_ASSERT(res=="This is a test\nMore to come...\n");
 | 
			
		||||
  }
 | 
			
		||||
  void excTest12() {
 | 
			
		||||
    mrw::PartialExec exec = (mrw::Cmd("/bin/false")).start();
 | 
			
		||||
    while (!exec.finished()) exec.read();
 | 
			
		||||
  }
 | 
			
		||||
  void excTest22() {
 | 
			
		||||
    mrw::PartialExec exec = (mrw::Cmd("/bin/false")).start(true);
 | 
			
		||||
    while (!exec.finished()) exec.read("xxx");
 | 
			
		||||
  }
 | 
			
		||||
  void unexpectedExc2() throw(std::bad_exception) {
 | 
			
		||||
    mrw::PartialExec exec = (mrw::Cmd("/bin/false")).start();
 | 
			
		||||
    while (!exec.finished()) exec.read();
 | 
			
		||||
  }
 | 
			
		||||
  CPPUNIT_TEST_SUITE(ExecTest);
 | 
			
		||||
  CPPUNIT_TEST(lsTest);
 | 
			
		||||
  CPPUNIT_TEST(catTest);
 | 
			
		||||
  CPPUNIT_TEST_EXCEPTION(excTest1, mrw::ExecutionFailedExc);
 | 
			
		||||
  CPPUNIT_TEST_EXCEPTION(excTest2, mrw::ExecutionFailedExc);
 | 
			
		||||
  CPPUNIT_TEST_EXCEPTION(unexpectedExc, std::bad_exception);
 | 
			
		||||
  CPPUNIT_TEST(lsTest2);
 | 
			
		||||
  CPPUNIT_TEST(catTest2);
 | 
			
		||||
  CPPUNIT_TEST_EXCEPTION(excTest12, mrw::ExecutionFailedExc);
 | 
			
		||||
  CPPUNIT_TEST_EXCEPTION(excTest22, mrw::ExecutionFailedExc);
 | 
			
		||||
  CPPUNIT_TEST_EXCEPTION(unexpectedExc2, std::bad_exception);
 | 
			
		||||
  CPPUNIT_TEST_SUITE_END();
 | 
			
		||||
};
 | 
			
		||||
CPPUNIT_TEST_SUITE_REGISTRATION(ExecTest);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user