Initial commit

This commit is contained in:
Andy Teijelo 2016-07-20 10:26:51 -04:00
commit ef7a4af571
3 changed files with 317 additions and 0 deletions

1
README.md Normal file
View file

@ -0,0 +1 @@
# A simple command class for C++

255
command.cpp Normal file
View file

@ -0,0 +1,255 @@
#include <iostream>
#include <sstream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include "command.h"
using std::vector;
using std::string;
using std::ostringstream;
using std::cout;
using std::endl;
command::command()
{
readbuf = (char *)malloc(bufsize);
}
command &command::operator <<(int n)
{
ostringstream s;
s << n;
args.push_back(s.str());
return *this;
}
#ifdef COMMAND_BOOST_FILESYSTEM
command& command::operator<<(const boost::filesystem::path& p) {
args.push_back(p.string());
return *this;
}
#else
command& command::operator<<(const string& s) {
args.push_back(s);
return *this;
}
#endif
command &command::operator <<(const vector<string> &v)
{
std::copy(v.begin(), v.end(), std::back_inserter(args));
return *this;
}
command &command::operator <<(double d)
{
ostringstream s;
s << d;
args.push_back(s.str());
return *this;
}
void command::clear()
{
args.clear();
async = false;
}
int command::read_from(int fd, std::ostream *stream)
{
int r = read(fd, readbuf, bufsize);
if (r <= 0)
return r;
stream->write(readbuf, r);
if (r == bufsize) {
bufsize *= 2;
free(readbuf);
readbuf = (char *)malloc(bufsize);
}
return r;
}
int command::run() {
if (args.empty())
return -1;
if (stdout_stream != nullptr)
pipe(stdout_pipe);
if (stderr_stream != nullptr)
pipe(stderr_pipe);
childpid = fork();
if (childpid == 0) {
char **argv = (char**)malloc((args.size() + 1) * sizeof(char*));
for (uint i=0; i<args.size(); i++) {
argv[i] = const_cast<char*>(args.at(i).c_str());
}
argv[args.size()] = nullptr;
if (_silence_stdout || _silence_stderr) {
int f = open("/dev/null", O_WRONLY);
if (_silence_stdout)
dup2(f, STDOUT_FILENO);
if (_silence_stderr)
dup2(f, STDERR_FILENO);
}
if (stdout_stream != nullptr) {
dup2(stdout_pipe[1], STDOUT_FILENO);
close(stdout_pipe[0]);
}
if (stderr_stream != nullptr) {
dup2(stderr_pipe[1], STDERR_FILENO);
close(stderr_pipe[0]);
}
execvp(args.at(0).c_str(), argv);
perror(args.at(0).c_str());
exit(-1);
}
int child_out = -1;
int child_err = -1;
// parent won't be writing anything to the pipes
// and keeping the write ends open will prevent
// EOF from happening on the read ends
if (stdout_stream != nullptr) {
close(stdout_pipe[1]);
child_out = stdout_pipe[0];
}
if (stderr_stream != nullptr) {
child_err = stderr_pipe[0];
close(stderr_pipe[1]);
}
if (async) {
return 0;
}
// now we'll wait on both ends, using select
while (child_err >=0 || child_out >= 0) {
fd_set read_fds;
FD_ZERO(&read_fds);
int nfds = 0;
if (child_out >= 0) {
FD_SET(child_out, &read_fds);
nfds = std::max(nfds, child_out);
}
if (child_err >= 0) {
FD_SET(child_err, &read_fds);
nfds = std::max(nfds, child_err);
}
nfds++;
int r = select(nfds, &read_fds, nullptr, nullptr, nullptr);
if (r < 0) {
perror("select failed");
break;
}
if (child_out >= 0 && FD_ISSET(child_out, &read_fds)) {
int r = read_from(child_out, stdout_stream);
if (r < 0) {
perror("error reading from child stdout");
break;
}
if (r == 0) {
close(child_out);
child_out = -1;
}
}
if (child_err >= 0 && FD_ISSET(child_err, &read_fds)) {
read_from(child_err, stderr_stream);
if (r < 0) {
perror("error reading from child stderr");
break;
}
if (r == 0) {
close(child_err);
child_err = -1;
}
}
}
return this->wait();
}
void command::runbg()
{
async = true;
run();
}
bool command::isrunning()
{
siginfo_t info;
info.si_pid = 0;
int r = waitid(P_PID, childpid, &info, WEXITED | WNOHANG | WNOWAIT);
if (r == 0 && info.si_pid != 0)
return true;
switch (info.si_code) {
case CLD_EXITED: return false;
case CLD_KILLED: return false;
case CLD_DUMPED: return false;
}
return true;
}
int command::wait()
{
int status;
::wait(&status);
if (WIFEXITED(status))
return WEXITSTATUS(status);
if (WIFSIGNALED(status))
return WTERMSIG(status) + 127;
return -1;
}
void command::silence_stdout()
{
_silence_stdout = true;
stdout_stream = nullptr;
}
void command::silence_stderr()
{
stderr_stream = nullptr;
_silence_stderr = true;
}
void command::silence()
{
silence_stdout();
silence_stderr();
}
void command::stdout(std::ostream &o)
{
stdout_stream = &o;
_silence_stdout = false;
}
void command::stderr(std::ostream &o)
{
stderr_stream = &o;
_silence_stderr = false;
}
std::ostream &operator<<(std::ostream &o, const command &cmd)
{
for (auto& s: cmd.args) {
o << s << " ";
}
return o;
}

61
command.h Normal file
View file

@ -0,0 +1,61 @@
#ifndef COMMAND_H
#define COMMAND_H
#include <iostream>
#include <string>
#include <vector>
#include <functional>
#if __has_include(<boost/filesystem.hpp>)
#include <boost/filesystem.hpp>
#define COMMAND_BOOST_FILESYSTEM
#endif
class command {
public:
command();
command& operator <<(int n);
#ifdef COMMAND_BOOST_FILESYSTEM
command& operator <<(const boost::filesystem::path& p);
#else
command& operator <<(const std::string& s);
#endif
command& operator <<(const std::vector<std::string>& v);
command& operator <<(double d);
void clear();
int run();
void runbg();
bool isrunning();
int wait();
void silence_stdout();
void silence_stderr();
void silence();
void stdout(std::ostream& o);
void stderr(std::ostream& o);
private:
std::vector<std::string> args;
pid_t childpid;
int stdout_pipe[2];
int stderr_pipe[2];
bool _silence_stdout = false;
bool _silence_stderr = false;
std::ostream* stdout_stream = nullptr;
std::ostream* stderr_stream = nullptr;
friend std::ostream& operator<<(std::ostream& o, const command& cmd);
char *readbuf;
int bufsize = 1024;
int async = false;
int read_from(int fd, std::ostream *stream);
};
std::ostream& operator<<(std::ostream& o, const command& cmd);
#endif // COMMAND_H