diff --git a/mrw/exec.cpp b/mrw/exec.cpp
index f3fe3c5..7eec02a 100644
--- a/mrw/exec.cpp
+++ b/mrw/exec.cpp
@@ -9,6 +9,9 @@
@license LGPL, see file COPYING
$Log$
+ Revision 1.5 2004/12/14 20:30:09 marc
+ added possibility to pass string to stdin of child process
+
Revision 1.4 2004/08/28 16:21:25 marc
mrw-c++-0.92 (mrw)
- new file: version.cpp
@@ -26,9 +29,11 @@
#include
#include
#include
+#include // max
#include // waitpid
#include // fork, exec
#include // memcpy
+#include // assert
mrw::ExecutionFailedExc::ExecutionFailedExc(const std::string& w,
const std::string& c)
@@ -73,8 +78,13 @@ mrw::Cmd::operator mrw::Exec() const throw(std::bad_exception) {
return mrw::Exec(*this);
}
-mrw::Exec mrw::Cmd::execute(bool throwExc) const throw(std::exception) {
- return mrw::Exec(*this).execute(throwExc);
+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);
}
const char* mrw::Cmd::path() const throw(std::bad_exception) {
@@ -109,10 +119,11 @@ mrw::Exec& mrw::Exec::operator=(const mrw::Exec& e) throw(std::bad_exception) {
return *this;
}
-mrw::Exec& mrw::Exec::execute(bool throwExc) throw(std::exception) {
+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 process
- using mrw::Pipe and calls @c execvp to execute the program. */
+ stdout and @c stderr from the child process to the parent
+ process using mrw::Pipe and calls @c execvp to execute the
+ program. */
_success = false;
_res = _err = "";
mrw::Pipe stdOut, stdErr;
@@ -128,17 +139,18 @@ mrw::Exec& mrw::Exec::execute(bool throwExc) throw(std::exception) {
throw ExecutionFailedExc("cannot close pipe", *_cmd);
int num1(0), num2(0);
for (char buf1[4096], buf2[4096];
- (num1=read(stdOut.istream(), buf1, sizeof(buf1)))>0 ||
- num1==-1 && errno==EINTR ||
- (num2=read(stdErr.istream(), buf2, sizeof(buf2)))>0 ||
- num2==-1 && errno==EINTR;
- _res += std::string(buf1, num1), _err += std::string(buf2, num2));
- if (num1==-1 || num2==-1)
- throw ExecutionFailedExc("cannot_ read pipe", *_cmd);
+ (num1=read(stdOut.istream(), buf1, sizeof(buf1)))>0
+ || num1==-1 && (errno==EINTR||errno==EAGAIN)
+ || (num2=read(stdErr.istream(), buf2, sizeof(buf2)))>0
+ || num2==-1 && (errno==EINTR||errno==EAGAIN);) {
+ if (num1>0) _res += std::string(buf1, num1);
+ if (num2>0) _err += std::string(buf2, num2);
+ }
// wait for child to get return code
int s(0);
- if (waitpid(pid, &s, 0)!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0) {
- if (throwExc) {
+ if (waitpid(pid, &s, 0)!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0
+ || num1==-1 || num2==-1) {
+ if (exc) {
throw ExecutionFailedExc("execution failed", *_cmd);
} else {
_success = false;
@@ -157,6 +169,106 @@ mrw::Exec& mrw::Exec::execute(bool throwExc) 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.
+ /// 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");
+ /** 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
+ parent process using mrw::Pipe and calls @c execvp to execute
+ the program. */
+ _success = false;
+ _res = _err = "";
+ mrw::Pipe stdIn(true), // child terminates if stdin is non-blocking
+ stdOut, stdErr;
+ if (!stdIn || !stdOut || !stdErr)
+ throw mrw::ExecutionFailedExc("cannot create pipe", *_cmd);
+ pid_t 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);
+ ssize_t num0(-1), num1(-1), num2(-1);
+ std::string in(input);
+ fd_set writefds, readfds;
+ char buf[4096];
+ while (num0 || num1 || num2) try { // no end of file in stdin, -out, -err
+ int res(0), s(0);
+ if ((res=waitpid(pid, &s, WNOHANG))) {
+ if (res!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0)
+ throw ExecutionFailedExc("execution failed", *_cmd);
+ _success = true;
+ return *this;
+ }
+ // select...
+ FD_ZERO(&writefds);
+ FD_ZERO(&readfds);
+ if (num0) FD_SET(stdIn.ostream(), &writefds);
+ if (num1) FD_SET(stdOut.istream(), &readfds);
+ if (num2) FD_SET(stdErr.istream(), &readfds);
+ timeval tm = {1, 0};
+ int n(mrw::max((num0?stdIn.ostream():0),
+ mrw::max((num1?stdOut.istream():0),
+ (num2?stdErr.istream():0))));
+ int num = select(n, &readfds, &writefds, 0, &tm);
+ if (num<0) throw ExecutionFailedExc("select failed", *_cmd);
+ // check and handle stdin
+ if (num0 && num>0
+ && FD_ISSET(stdIn.ostream(), &writefds))
+ if ((num0=write(stdIn.ostream(), in.c_str(), in.size()))>0) {
+ if ((unsigned int)num00 && FD_ISSET(stdOut.istream(), &readfds))
+ if ((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);
+ // check and handle stderr
+ if (num>0 && FD_ISSET(stdErr.istream(), &readfds))
+ if ((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);
+ } catch (...) {
+ if (exc) throw;
+ _success = false;
+ return *this;
+ }
+ // wait for child to get return code
+ int s(0);
+ if (waitpid(pid, &s, 0)!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0) {
+ if (exc) {
+ throw ExecutionFailedExc("execution failed", *_cmd);
+ } else {
+ _success = false;
+ return *this;
+ }
+ }
+ } else { // child
+ stdIn.close_out();
+ stdOut.close_in();
+ stdErr.close_in();
+ stdIn.connect_cin();
+ stdOut.connect_cout(); // if cin is non blocking, child terminates here?!?
+ stdErr.connect_cerr();
+ execvp(_cmd->path(), _cmd->args());
+ exit(1); // execute failed
+ }
+ _success = true;
+ return *this;
+}
+
mrw::Exec& mrw::Exec::operator>>(std::string& res) throw(std::exception) {
execute();
res += _res;
diff --git a/mrw/exec.hpp b/mrw/exec.hpp
index b0ff4bc..5a93a27 100644
--- a/mrw/exec.hpp
+++ b/mrw/exec.hpp
@@ -9,6 +9,9 @@
@license LGPL, see file COPYING
$Log$
+ Revision 1.5 2004/12/14 20:30:09 marc
+ added possibility to pass string to stdin of child process
+
Revision 1.4 2004/10/07 09:27:01 marc
errors in documentation
@@ -67,6 +70,18 @@ namespace mrw {
// you can trace x.what() and x.stacktrace()
}
@endcode
+
+ It is also possible to pass an @c stdin input argument to the
+ subprocess called:
+
+ @code
+ try {
+ mrw::Exec cat = mrw::Cmd("/bin/cat")
+ .execute("this is passed to stdin");
+ // "cat" passes all from stdin to stdout, therefore:
+ assert(cat.result()=="this is passed to stdin");
+ } catch (...) {} // ignore
+ @endcode
*/
//@{
@@ -99,6 +114,9 @@ namespace mrw {
and returns the two streams @c cout and @c cerr, also known as @c
stderr and @c stdout.
+ Method @c execute can optionally also take a string parameter
+ that is passed to @c stdin of the child process.
+
There are different ways of usage for this class. A simple way,
one line of code, to get only the resulting stream (no error)
is:
@@ -146,6 +164,42 @@ namespace mrw {
*/
Exec& execute(bool exc=true) throw(std::exception);
+ /** @brief Execute the command, pass @c stdin.
+
+ @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
+
+ @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.
+
+ @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
+
+ @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
@@ -220,7 +274,7 @@ namespace mrw {
/** @brief A system command to be executed
@pre #include
- This class is used in conjunction with mrw::Exec. It mus be
+ This class is used in conjunction with mrw::Exec. It must be
initialized with the command name, then the command parameters
are appended either with commas, or by streaming them into the
command, whatever you like.
@@ -284,6 +338,60 @@ namespace mrw {
*/
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 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(char const*const input, bool exc=true) const
+ throw(std::exception) {
+ return execute(std::string(input), exc);
+ }
+
private:
/// Exec is allowed to call @c path() and @c args().
friend class Exec;
diff --git a/mrw/exec_test.cpp b/mrw/exec_test.cpp
index fe0e6e1..a216ba1 100644
--- a/mrw/exec_test.cpp
+++ b/mrw/exec_test.cpp
@@ -9,6 +9,9 @@
@license LGPL, see file COPYING
$Log$
+ Revision 1.6 2004/12/14 20:30:10 marc
+ added possibility to pass string to stdin of child process
+
Revision 1.5 2004/10/13 10:43:11 marc
test for bad exception specification
@@ -40,14 +43,20 @@ public:
std::string res = (mrw::Cmd("/bin/ls"), "-l", "..").execute();
CPPUNIT_ASSERT(res.find("COPYING")