* @returns the output (stdout) of the called program
*/
template <typename CmdT>
-std::string capture_exec(CmdT command, ExecResult &rescode)
+std::string capture_exec(CmdT command, ExecResult &rescode,
+ const bool out, const bool err)
{
std::string output;
try
{
{
- inpipestream ips(command);
+ inpipestream ips(command, out, err);
ips.store_exit_status(&exit_set, &exit_status_waitpid);
return output;
}
+/** @brief Instantiation of \c capture_exec for STL string arguments.
+ * Caveat emptor: this will cause the backing stream to use \c
+ * popen(3). To avoid shelling out, please refer to one of the
+ * variants that allow passing an argument list.
+ *
+ * @param command String specifying the shell expression to be executed.
+ * @param res (Out parameter) Store information about the termination
+ * state in this struct.
+ *
+ * @returns Result of \c stdout. Note that due to the use of \c
+ * popen, the correct way to collect stderr output as
+ * well is to use shell redirection inside the expression
+ * passed.
+ */
std::string capture_exec (const std::string &command, ExecResult &res)
-{ return capture_exec<const std::string &>(command, res); }
-
-std::string capture_exec (const char *const *command, ExecResult &res)
-{ return capture_exec<const char *const *>(command, res); }
-
-std::string capture_exec (const std::vector<std::string> &command, ExecResult &res)
+{ return capture_exec<const std::string &>(command, res, true, false); }
+
+/** @brief Instantiation of \c capture_exec for argument lists. The
+ * pipestream used to run the command will not shell out.
+ * One of \c out or \c err must be set.
+ *
+ * @param command List of \c char* specifying the \c argv array of the
+ * command to run. Note that the binary to executed is
+ * assumed to be present at index 0 and that the input
+ * is properly \c NULL terminated.
+ * @param res (Out parameter) Store information about the termination
+ * state in this struct.
+ * @param out Whether to collect \c stdout.
+ * @param err Whether to collect \c stderr; combines with \c out.
+ *
+ * @returns Captured output, combined into one string.
+ */
+std::string capture_exec (const char *const *command, ExecResult &res,
+ const bool out, const bool err)
+{ return capture_exec<const char *const *>(command, res, out, err); }
+
+/** @brief Instantiation of \c capture_exec for argument lists. The
+ * pipestream used to run the command will not shell out.
+ * One of \c out or \c err must be set.
+ *
+ * @param command String vector specifying the \c argv array of the
+ * command to run. Note that the binary to executed is
+ * assumed to be present at index 0.
+ * @param res (Out parameter) Store information about the termination
+ * state in this struct.
+ * @param out Whether to collect \c stdout.
+ * @param err Whether to collect \c stderr; combines with \c out.
+ *
+ * @returns Captured output, combined into one string.
+ */
+std::string capture_exec (const std::vector<std::string> &command, ExecResult &res,
+ const bool out, const bool err)
{
- return capture_exec<const std::vector<std::string> &>(command, res);
+ return capture_exec<const std::vector<std::string> &>
+ (command, res, out, err);
}
#define PIPE_CTOR_FAIL(where) \
+ " (" + std::string (strerror (errno)) + ")"); \
} while (0)
+/** @brief Convert a string vector to a refcounted \c char**
+ * that is \c NULL terminated for use with e. g. \c execve(2).
+ *
+ * @param command List of arguments including the binary at index 0.
+ *
+ * @returns A \c boost::shared_array of pointers to the
+ * arguments plus a trailing \c NULL. Note that
+ * while the array itself is refcounted, the
+ * pointees are assumed owned by the caller and
+ * *not copyied*. I. e. they lose validity if the
+ * original strings are freed.
+ */
static boost::shared_array <char *>
mk_argv (const std::vector<std::string> &command)
{
}
+/** @brief Helper aggregating common code for the shell-free ctors.
+ *
+ * @param argv Argument list prepared for \c execve(2).
+ * @param out Whether to capture \c stdout.
+ * @param err Whether to capture \c stderr.
+ *
+ * @returns A \c FILE* handle for streaming if successful, \c NULL
+ * otherwise.
+ */
FILE *
-inpipebuf::init_without_shell (const char *const *argv) const
+inpipebuf::init_without_shell (const char *const *argv,
+ const bool out,
+ const bool err) const
{
FILE *pipeobj = NULL;
int pipefd [2];
+ if (!out && !err) {
+ errno = EINVAL;
+ PIPE_CTOR_FAIL("ctor");
+ }
+
errno = 0;
if (::pipe (pipefd) == -1) {
PIPE_CTOR_FAIL("pipe");
case 0: {
close (pipefd [0]);
- if (dup2 (pipefd[1], STDOUT_FILENO) == -1) {
- PIPE_CTOR_FAIL("dup2");
+ if (!out) {
+ close (STDOUT_FILENO);
+ } else if (dup2 (pipefd[1], STDOUT_FILENO) == -1) {
+ PIPE_CTOR_FAIL("dup2/stdout");
}
- if (dup2 (pipefd[1], STDERR_FILENO) == -1) {
- PIPE_CTOR_FAIL("dup2");
+ if (!err) {
+ close (STDERR_FILENO);
+ } else if (dup2 (pipefd[1], STDERR_FILENO) == -1) {
+ PIPE_CTOR_FAIL("dup2/stderr");
}
errno = 0;
return pipeobj;
}
-inpipebuf::inpipebuf(const char *const *command)
+inpipebuf::inpipebuf(const char *const *command,
+ const bool out,
+ const bool err)
: pipe (NULL) /* brr: shadowing global ident */
, status_set (NULL)
, exit_status (NULL)
PIPE_CTOR_FAIL("command");
}
- this->pipe = this->init_without_shell (command);
+ this->pipe = this->init_without_shell (command, out, err);
setg (&buffer, &buffer, &buffer);
}
-inpipebuf::inpipebuf(const std::vector<std::string> &command)
+inpipebuf::inpipebuf(const std::vector<std::string> &command,
+ const bool out,
+ const bool err)
: pipe (NULL) /* brr: shadowing global ident */
, status_set (NULL)
, exit_status (NULL)
PIPE_CTOR_FAIL("malloc");
}
- this->pipe = this->init_without_shell (argv.get ());
+ this->pipe = this->init_without_shell (argv.get (), out, err);
setg (&buffer, &buffer, &buffer);
}
-inpipebuf::inpipebuf(const std::string& command)
+inpipebuf::inpipebuf(const std::string& command,
+ const bool _ignored_out,
+ const bool _ignored_err)
{
status_set = NULL;
exit_status = NULL;
typedef struct ExecResult ExecResult;
std::string capture_exec(const std::string& command, ExecResult &rescode);
-std::string capture_exec(const char *const *command, ExecResult &rescode);
-std::string capture_exec(const std::vector<std::string>& command, ExecResult &rescode);
+std::string capture_exec(const char *const *command, ExecResult &rescode,
+ const bool out=true, const bool err=false);
+std::string capture_exec(const std::vector<std::string>& command, ExecResult &rescode,
+ const bool out=true, const bool err=false);
inline std::string capture_exec (const std::string &command)
{
int *exit_status;
public:
- inpipebuf(const std::string& command);
- inpipebuf(const char *const *command);
- inpipebuf(const std::vector<std::string> &command);
+ inpipebuf(const std::string& command,
+ const bool out, const bool err);
+ inpipebuf(const char *const *command,
+ const bool out, const bool err);
+ inpipebuf(const std::vector<std::string> &command,
+ const bool out, const bool err);
~inpipebuf();
virtual int_type underflow();
private:
- FILE *init_without_shell (const char *const *argv) const;
+ FILE *init_without_shell (const char *const *argv,
+ const bool out, const bool err) const;
};
/** @brief stream around inpipebuf -- see comment there */
inpipebuf buf;
public:
- inpipestream(const std::string& command)
- : std::istream(&buf), buf(command)
+ inpipestream(const std::string& command,
+ const bool out=true, const bool err=false)
+ : std::istream(&buf), buf(command, out, err)
{}
- inpipestream(const char *const command[])
- : std::istream(&buf), buf(command)
+
+ inpipestream(const char *const command[],
+ const bool out=true, const bool err=false)
+ : std::istream(&buf), buf(command, out, err)
{}
- inpipestream(const std::vector<std::string> &command)
- : std::istream(&buf), buf(command)
+ inpipestream(const std::vector<std::string> &command,
+ const bool out=true, const bool err=false)
+ : std::istream(&buf), buf(command, out, err)
{}
void store_exit_status(bool *_status_set, int *_exit_status)
BOOST_CHECK_EQUAL(result.size (), ENOUGH_ZEROS);
}
+ const char *const bad_command [] = { "/does_not_exist", NULL };
+
+ BOOST_AUTO_TEST_CASE(abspath_zeros_shell_fail)
+ {
+ assert (access(bad_command [0], X_OK) != 0);
+
+ ExecResult exres;
+ const std::string result =
+ capture_exec (I2n::join_string (bad_command, " "));
+
+ BOOST_CHECK_EQUAL(result.size (), 0);
+ }
+
+ BOOST_AUTO_TEST_CASE(abspath_zeros_noshell_fail)
+ {
+ assert (access(bad_command [0], X_OK) != 0);
+
+ ExecResult exres;
+ const std::string result = capture_exec (bad_command, exres);
+
+ BOOST_CHECK(!exres.normal_exit);
+ BOOST_CHECK(!exres.terminated_by_signal);
+ BOOST_CHECK_EQUAL(result.size (), 0);
+ }
+
+ BOOST_AUTO_TEST_CASE(abspath_zeros_noshell_stderr)
+ {
+ assert (access(bad_command [0], X_OK) != 0);
+
+ ExecResult exres;
+ const std::string result = capture_exec (bad_command, exres, false, true);
+
+ BOOST_CHECK_NE(result.size (), 0);
+ }
+
BOOST_AUTO_TEST_SUITE_END() /* [pipestream->read] */
BOOST_AUTO_TEST_SUITE_END() /* [pipestream] */