|
|
@@ -5,7 +5,7 @@ |
|
|
|
* |
|
|
|
* Created on: Feb, 2019 |
|
|
|
* Author: Christos Choutouridis AEM: 8997 |
|
|
|
* email : cchoutou@ece.auth.gr |
|
|
|
* email : <cchoutou@ece.auth.gr> |
|
|
|
*/ |
|
|
|
#ifndef __sequencer_h__ |
|
|
|
#define __sequencer_h__ |
|
|
@@ -29,32 +29,30 @@ namespace snel { |
|
|
|
//! file descriptor type |
|
|
|
using fd_t = int; |
|
|
|
|
|
|
|
constexpr fd_t STDIN_ = STDIN_FILENO; |
|
|
|
constexpr fd_t STDOUT_ = STDOUT_FILENO; |
|
|
|
constexpr fd_t STDERR_ = STDERR_FILENO; |
|
|
|
constexpr fd_t STDIN_ = STDIN_FILENO; //!< Constant for stdin file descriptor |
|
|
|
constexpr fd_t STDOUT_ = STDOUT_FILENO; //!< Constant for stdout file descriptor |
|
|
|
constexpr fd_t STDERR_ = STDERR_FILENO; //!< Constant for stderr file descriptor |
|
|
|
|
|
|
|
std::string filter (const std::string in); |
|
|
|
|
|
|
|
/*! |
|
|
|
* A vector based wrapper above execvp()'s `char* argv[]` interface. |
|
|
|
* A vector based wrapper above <tt>execvp()'s char* argv[]</tt> interface. |
|
|
|
* We use a vector for convenience and resizing capabilities. We can then |
|
|
|
* use a pointer to underling data to pass to execvp() |
|
|
|
* std::vector elements guaranteed to be contiguous. To quote the standard[1] |
|
|
|
* \code |
|
|
|
* std::vector elements guaranteed to be contiguous. To quote the |
|
|
|
* [standard](www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3690.pdf) |
|
|
|
* <pre> |
|
|
|
* The elements of a vector are stored contiguously, meaning that if v is |
|
|
|
* a vector<T, Allocator> where T is some type other than bool, then it obeys |
|
|
|
* the identity &v[n] == &v[0] + n for all 0 <= n < v.size(). |
|
|
|
* \endcode |
|
|
|
* |
|
|
|
* See also cppreference[2]. |
|
|
|
* </pre> |
|
|
|
* |
|
|
|
* [1]: www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3690.pdf |
|
|
|
* [2]: https://en.cppreference.com/w/cpp/container/vector |
|
|
|
* See also [cppreference](https://en.cppreference.com/w/cpp/container/vector) |
|
|
|
*/ |
|
|
|
struct ArgList { |
|
|
|
using type = typename std::string::value_type; // Basic data type (aka char) |
|
|
|
using vtype = type*; // Vector type |
|
|
|
using vtype_ptr = vtype*; // Pointer to vector type |
|
|
|
using type = typename std::string::value_type; //!< Basic data type (aka char) |
|
|
|
using vtype = type*; //!< Vector type |
|
|
|
using vtype_ptr = vtype*; //!< Pointer to vector type |
|
|
|
|
|
|
|
~ArgList(); |
|
|
|
ArgList() = default; |
|
|
@@ -65,12 +63,12 @@ namespace snel { |
|
|
|
vtype front () { return args_.front(); } //!< A vector::front() wrapper |
|
|
|
size_t size () { return args_.size(); } //!< A vector::size() wrapper |
|
|
|
|
|
|
|
//! return a pointer to underling data for `execvp()` |
|
|
|
//! return a pointer to underling data for \c execvp() |
|
|
|
vtype_ptr data() noexcept { return args_.data(); } |
|
|
|
//! same as `data()` |
|
|
|
//! same as \c data() |
|
|
|
vtype_ptr operator*() noexcept { return data(); } |
|
|
|
private: |
|
|
|
// data |
|
|
|
//! underling data for the \c execvp() arguments |
|
|
|
std::vector<vtype> args_ {nullptr}; |
|
|
|
}; |
|
|
|
|
|
|
@@ -78,8 +76,8 @@ namespace snel { |
|
|
|
/*! |
|
|
|
* A pipe file descriptor container for each child. |
|
|
|
* \note |
|
|
|
* We store the pipe in the first child and use the flags `from` |
|
|
|
* and `to` to inform child's executer for where can find the data. |
|
|
|
* We store the pipe in the first child and use the flags \c from |
|
|
|
* and \c to to inform child's executer for where can find the data. |
|
|
|
*/ |
|
|
|
struct Pipe { |
|
|
|
~Pipe(); |
|
|
@@ -91,13 +89,15 @@ namespace snel { |
|
|
|
/*! |
|
|
|
* An object to represent each process |
|
|
|
* |
|
|
|
* We create Child objects from command_t passed from Sequence::parse() and we |
|
|
|
* keep in each child informations about the file descriptors that may the process |
|
|
|
* will use, pipes that may need and logic flags(&&, ||) the command may have to control |
|
|
|
* the execution flow. |
|
|
|
* We create Child objects from \c command_t passed to constructor from \c Sequence::parse(). |
|
|
|
* In child we store information about: |
|
|
|
* * The file descriptors for std redirection the process may use |
|
|
|
* * Pipes that may needed for communication with the \b next child |
|
|
|
* * Logic flags \c (&&, ||) the command may have, to control the execution flow. |
|
|
|
*/ |
|
|
|
class Child { |
|
|
|
public: |
|
|
|
//! Enumerator for the logic operators between commands |
|
|
|
enum class LogicOp { |
|
|
|
NONE=0, OR, AND |
|
|
|
}; |
|
|
@@ -111,20 +111,19 @@ namespace snel { |
|
|
|
Child (command_t&& c) : command{std::move(c)} { } |
|
|
|
|
|
|
|
Child& make_arguments (); //!< A per child parser before execution |
|
|
|
//! the actual execution routine |
|
|
|
bool execute(std::vector<Child>::iterator it, bool first); |
|
|
|
Pipe& pipe() { return pipe_; } //!< A pipe_ getter |
|
|
|
|
|
|
|
//! Private api |
|
|
|
//! @{ |
|
|
|
private: |
|
|
|
void redirect_std_if(std::string fn, fd_t std_fd); //!< tool to redirect std |
|
|
|
void restore_std_if(fd_t std_fd); //!< tool to restor std redirection |
|
|
|
void redirect_std_if(std::string fn, fd_t std_fd); //!< Tool to redirect std |
|
|
|
void restore_std_if(fd_t std_fd); //!< Tool to restore std redirection |
|
|
|
//! @} |
|
|
|
|
|
|
|
//! Data members |
|
|
|
//! \note |
|
|
|
//! `arguments`, `files`, `sstd` and `pipe_` have linked resources so destructor(s) must be called. |
|
|
|
//! \c arguments, \c files, \c sstd and \c pipe_ have linked resources so destructor(s) must be called. |
|
|
|
//! We use RAII by having all children to static allocated vectors. '}' will do the trick even |
|
|
|
//! if we throw ;) |
|
|
|
//! @{ |
|
|
@@ -155,13 +154,13 @@ namespace snel { |
|
|
|
* \endcode |
|
|
|
* By storing them in different vector we make the execution model simpler. |
|
|
|
* Each chain can stopped and the sequencer will continue with the next chain etc... |
|
|
|
* So in this example if `more` returns false(child::execute() returned true), |
|
|
|
* then `uname` will not parsed and executed and the flow will continue to `cat` |
|
|
|
* So in this example if \c more returns false(child::execute() returned true), |
|
|
|
* then \c uname will not parsed and executed and the flow will continue to \c cat |
|
|
|
*/ |
|
|
|
class Sequencer { |
|
|
|
public: |
|
|
|
Sequencer& parse (const std::string& input); //!< Input line parser |
|
|
|
Sequencer& execute (); //!< Main sequencer executor |
|
|
|
Sequencer& parse (const std::string& input); |
|
|
|
Sequencer& execute (); |
|
|
|
|
|
|
|
//! private tools |
|
|
|
//! @{ |
|
|
|