Snel/src/sequencer.cpp
2019-02-14 12:12:00 +02:00

204 lines
5.8 KiB
C++
Executable File

/*
* child.cpp
*
* Created on: Feb 11, 2019
* Author: hoo2
*/
#include "sequencer.h"
namespace snel {
//! A type-safe memcpy
template <typename T>
void memcpy (T* to, const T* from, size_t n) {
for (size_t i=0 ; i<n ; ++i)
*to++ = *from++;
}
/*!
* Split a string to tokens and store them in a container, using a delimiter
* character.
* \note
* Requires: container with push_back functionality
* @param str
* @param cont
* @param delim
*/
template <typename T, typename Container>
void split(const std::basic_string<T>& str, Container& cont, T delim) {
std::basic_stringstream<T> ss(str);
std::basic_string<T> token;
while (std::getline(ss, token, delim)) {
cont.push_back(token);
}
}
/*
* ========== ArgList ==========
*/
ArgList::~ArgList() {
for (vtype p : args_) {
if (p != nullptr)
delete[] p;
}
}
ArgList& ArgList::push_back(const std::basic_string<type>& item) {
vtype it = new type[item.length()+1];
// get data
memcpy (it, item.c_str(), item.length());
it[item.length()] = 0;
// update the argument vector
args_.back() = it;
args_.push_back(nullptr);
return *this;
}
/*
* ========== Child ==========
*/
Child::~Child () {
for (int i=0 ; i<3 ; ++i)
restore_std_if(i);
}
void Child::redirect_std_if(std::string fn, fd_t std_fd) {
if (fn != "") {
if ((files[std_fd] = openat(AT_FDCWD, fn.c_str(), O_RDWR | O_CREAT, 0640)) == -1)
throw Error ("Child: Can not open file");
if ((sstd[std_fd] = dup (std_fd)) == -1) // save STDxxx
throw Error ("Child: Can not create file descriptor");
if ((dup2(files[std_fd], std_fd)) == -1) // use input as STDIN_
throw Error ("Child: Can not redirect file descriptor");
}
}
void Child::restore_std_if(fd_t std_fd) {
if (sstd[std_fd] != -1) {
dup2(sstd[std_fd], std_fd);
close (sstd[std_fd]);
sstd[std_fd] = -1;
}
if (files[std_fd] != -1) {
close (files[std_fd]);
files[std_fd] = -1;
}
}
Child& Child::make_arguments () {
bool in{false}, out{false}, err{false};
for (auto& t: command) {
if (t == "" || t== " ") continue; // skip crap
if (t == "&&") logic = LogicOp::AND;
else if (t == "||") logic = LogicOp::OR;
// one pass redirection parsing
else if (!t.compare(0, 1, "<")) filenames[STDIN_] = t.substr(1);
else if (!t.compare(0, 1, ">")) filenames[STDOUT_] = t.substr(1);
else if (!t.compare(0, 2, "1>")) filenames[STDOUT_] = t.substr(2);
else if (!t.compare(0, 2, "2>")) filenames[STDERR_] = t.substr(2);
// two pass redirection parsing (if redirection came in 2 arguments)
else if (t == "<") in = true;
else if (t == "1>" || t == ">") out = true;
else if (t == "2>") err = true;
else if (in) {
filenames[STDIN_] = t; in = false;
}
else if (out) {
filenames[STDOUT_] = t; out = false;
}
else if (err) {
filenames[STDERR_] = t; err = false;
}
else
arguments.push_back(t);
}
return *this;
}
bool Child::execute() {
bool stop = {false};
redirect_std_if (filenames[STDIN_], STDIN_);
redirect_std_if (filenames[STDOUT_], STDOUT_);
redirect_std_if (filenames[STDERR_], STDERR_);
pid_t pid = fork();
if (pid < 0) {
throw Error("Child::execute(): Can not create child process");
}
else if (pid == 0) { // child
execvp(arguments.front(), arguments.data());
// error if we got here
std::cout << "Can not invoke: " << arguments.front() << std::endl;
throw Error("Child::execute(): Can not run child process");
}
else { // parent
int exit_status;
waitpid(pid, &exit_status, 0); // wait child process to finish
restore_std_if (STDIN_);
restore_std_if (STDOUT_);
restore_std_if (STDERR_);
switch (logic) {
case LogicOp::AND:
if (exit_status) stop = true;
break;
case LogicOp::OR:
if (!exit_status) stop = true;
break;
default: break;
}
}
return stop;
}
/*
* ======== Sequencer ===========
*/
Sequencer& Sequencer::parse (const std::string& input) {
std::vector<std::string> commands;
std::vector<std::string> tokens;
Child::command_t command;
split (input, commands, ';'); // split all commands
for (auto& s : commands) {
command.clear(); //clear child tokens
tokens.clear(); // make a new token vector for command
split (s, tokens, ' ');
seq_.emplace_back(std::vector<Child>{}); // make a new child for command
// check tokens inside command
for (auto& t: tokens) {
if (is_pipe(t)) {
}
command.push_back(t); // get current token
if (is_seperator(t)) {
// construct a child with what we have so far and clear command
seq_.back().emplace_back(command);
command.clear();
}
}
seq_.back().emplace_back(command); // construct the child with tokens
}
return *this;
}
Sequencer& Sequencer::execute() {
for (auto& batch : seq_)
for (auto& command: batch)
if (command.make_arguments().execute())
break;
seq_.clear();
return *this;
}
}