Browse Source

Initial commit

Interactive and batch mode bare implementation
master
Christos Houtouridis 5 years ago
commit
8f36da884a
4 changed files with 345 additions and 0 deletions
  1. +4
    -0
      .gitignore
  2. +32
    -0
      src/Snel.cpp
  3. +197
    -0
      src/sequencer.cpp
  4. +112
    -0
      src/sequencer.h

+ 4
- 0
.gitignore View File

@@ -0,0 +1,4 @@
*Debug/*
*.cproject
*.project
*.settings/*

+ 32
- 0
src/Snel.cpp View 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
- 0
src/sequencer.cpp View 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
- 0
src/sequencer.h View 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…
Cancel
Save