|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- /*!
- * \file
- * Sequencer.h
- * \brief A basic sequence interpreter for snel
- *
- * Created on: Feb, 2019
- * Author: Christos Choutouridis AEM: 8997
- * email : cchoutou@ece.auth.gr
- */
- #ifndef __sequencer_h__
- #define __sequencer_h__
-
- #include <exception>
- #include <string>
- #include <iostream>
- #include <sstream>
- #include <fstream>
- #include <vector>
- #include <utility>
- #include <algorithm>
-
- #include <unistd.h>
- #include <fcntl.h>
- #include <sys/wait.h>
-
-
- 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;
-
- std::string filter (const std::string in);
-
- /*!
- * A vector based wrapper above execvp()'s `char* argv[]` 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
- * 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].
- *
- * [1]: www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3690.pdf
- * [2]: 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
-
- ~ArgList();
- ArgList() = default;
- ArgList(const ArgList&) = default;
- ArgList(ArgList&&) = default;
-
- ArgList& push_back(const std::string& item); //!< A vector::push_back wrapper
- 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()`
- vtype_ptr data() noexcept { return args_.data(); }
- //! same as `data()`
- vtype_ptr operator*() noexcept { return data(); }
- private:
- // data
- std::vector<vtype> args_ {nullptr};
- };
-
-
- /*!
- * 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.
- */
- struct Pipe {
- ~Pipe();
- fd_t fd[2] {-1, -1};
- bool from {false};
- bool to {false};
- };
-
- /*!
- * 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.
- */
- class Child {
- public:
- enum class LogicOp {
- NONE=0, OR, AND
- };
- using command_t = std::vector<std::string>; //!< A command type
- using Error = std::runtime_error; //!< An error type
-
- public:
- ~Child ();
- Child () = default;
- Child (const command_t& c) : command{c} { }
- 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
- //! @}
-
- //! Data members
- //! \note
- //! `arguments`, `files`, `sstd` and `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 ;)
- //! @{
- private:
- command_t command {}; //!< Each command is a vector of string as passed on from Sequence::parse()
- ArgList arguments {}; //!< The after parsing, actual null terminated arguments to pass to execvp()
-
- fd_t files[3] = {-1, -1, -1}; //!< file descriptors for the user requested redirections
- fd_t sstd [3] = {-1, -1, -1}; //!< file descriptors to store std fd's before redirection
- std::string filenames[3] = {"", "", ""}; //!< filenames of the user requested redirections
- LogicOp logic {LogicOp::NONE}; //!< Logic flags to store && and || command separators
- Pipe pipe_; //!< Pipe object for each child
- //! @}
- };
-
- /*!
- * The main object to represent and handle a command flow.
- *
- * We store commands as vector of vectors of commands. The inner vector
- * represent a command chain. For example the line:
- * \code ls | more && uname; cat lala \endcode
- * is an input with 2 command chains and result to a:
- * \code
- * vector<
- * vector<(ls |), (more &&), (uname)>,
- * vector<(cat)>
- * >
- * \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`
- */
- class Sequencer {
- public:
- Sequencer& parse (const std::string& input); //!< Input line parser
- Sequencer& execute (); //!< Main sequencer executor
-
- //! private tools
- //! @{
- private:
- //! separator trait predicate
- bool is_seperator (std::string& s) {
- return (s == "&&" || s == "||" || s == "|") ? true : false;
- }
- //! pipe trait predicate
- bool is_pipe (std::string& s) {
- return (s == "|") ? true : false;
- }
- //! @}
- private:
- //! The sequencer data representation
- std::vector <
- std::vector <Child>
- > seq_ {};
- };
-
- }
-
- #endif /* __sequencer_h__ */
|