Initial commit
Interactive and batch mode bare implementation
This commit is contained in:
commit
8f36da884a
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*Debug/*
|
||||||
|
*.cproject
|
||||||
|
*.project
|
||||||
|
*.settings/*
|
32
src/Snel.cpp
Executable file
32
src/Snel.cpp
Executable file
@ -0,0 +1,32 @@
|
|||||||
|
#include "sequencer.h"
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) try {
|
||||||
|
snel::Sequencer s{};
|
||||||
|
std::string line;
|
||||||
|
|
||||||
|
if (argc > 1) { // batch mode
|
||||||
|
std::ifstream file(argv[1]);
|
||||||
|
|
||||||
|
while (std::getline (file, line, '\n')) {
|
||||||
|
s.parse(line).execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else { // interactive mode
|
||||||
|
std::cout << "Snel. A quick and dirty shell implementation for auth.gr" << std::endl;
|
||||||
|
do {
|
||||||
|
std::cout << "Choutouridis_8997>";
|
||||||
|
std::getline (std::cin, line, '\n');
|
||||||
|
if (line == "quit")
|
||||||
|
break;
|
||||||
|
s.parse(line).execute();
|
||||||
|
} while (1);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (std::exception& e) {
|
||||||
|
std::cerr << e.what() << '\n';
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
197
src/sequencer.cpp
Executable file
197
src/sequencer.cpp
Executable file
@ -0,0 +1,197 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
if (t == "&&") logic = LogicOp::AND;
|
||||||
|
else if (t == "||") logic = LogicOp::OR;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
112
src/sequencer.h
Executable file
112
src/sequencer.h
Executable file
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* child.h
|
||||||
|
*
|
||||||
|
* Created on: Feb 11, 2019
|
||||||
|
* Author: hoo2
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
struct ArgList {
|
||||||
|
using type = char; // Basic data type
|
||||||
|
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::basic_string<type>& item);
|
||||||
|
vtype front () { return args_.front(); }
|
||||||
|
size_t size () { return args_.size(); }
|
||||||
|
|
||||||
|
vtype_ptr data() noexcept {
|
||||||
|
return args_.data();
|
||||||
|
}
|
||||||
|
vtype_ptr operator*() noexcept {
|
||||||
|
return data();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::vector<vtype> args_ {nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Child {
|
||||||
|
public:
|
||||||
|
enum class LogicOp { NONE=0, OR, AND };
|
||||||
|
using command_t = std::vector<std::string>;
|
||||||
|
using Error = std::runtime_error;
|
||||||
|
|
||||||
|
public:
|
||||||
|
~Child ();
|
||||||
|
Child () noexcept = default;
|
||||||
|
Child (const command_t& c) : command{c} { }
|
||||||
|
Child (command_t&& c) : command{std::move(c)} { }
|
||||||
|
|
||||||
|
Child& make_arguments ();
|
||||||
|
bool execute ();
|
||||||
|
private:
|
||||||
|
void redirect_std_if(std::string fn, fd_t std_fd);
|
||||||
|
void restore_std_if(fd_t std_fd);
|
||||||
|
private:
|
||||||
|
command_t command {};
|
||||||
|
ArgList arguments {};
|
||||||
|
|
||||||
|
fd_t files[3] = {-1, -1, -1};
|
||||||
|
fd_t sstd [3] = {-1, -1, -1};
|
||||||
|
std::string filenames[3] = {"", "", ""};
|
||||||
|
LogicOp logic {LogicOp::NONE};
|
||||||
|
bool pipe {false};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Sequencer {
|
||||||
|
public:
|
||||||
|
Sequencer& parse (const std::string& input);
|
||||||
|
Sequencer& execute ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool is_seperator (std::string& s) {
|
||||||
|
return (s == "&&" || s == "||") ? true : false;
|
||||||
|
}
|
||||||
|
bool is_pipe (std::string& s) {
|
||||||
|
return (s == "|") ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector <
|
||||||
|
std::vector <Child>
|
||||||
|
> seq_ {};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __sequencer_h__ */
|
Loading…
x
Reference in New Issue
Block a user