diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..65b9522 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,9 @@ +project(msh-console-test) +cmake_minimum_required(VERSION 2.8) +aux_source_directory(. SRC_LIST) + +# Recurse into the subdirectories. This does not actually +# cause another cmake executable to run. The same process will walk through +# the project's entire directory structure. +add_subdirectory (msh-console-library) +add_subdirectory (snippet) diff --git a/msh-console-library/CMakeLists.txt b/msh-console-library/CMakeLists.txt new file mode 100644 index 0000000..84414f7 --- /dev/null +++ b/msh-console-library/CMakeLists.txt @@ -0,0 +1,13 @@ +# msh-console dynamic library by praticamentetilde (Claudio Maggioni) +# licensed with "The Unlicense" +project(msh-console-library) +cmake_minimum_required(VERSION 2.8) + +# Create a library which includes the source listed. +# The extension is already found. Any number of sources could be listed here. +SET(CMAKE_CXX_FLAGS "-std=c++11") +add_library (msh-console command.cpp commandexecutor.cpp commandexecutor.h command.h commands.cpp shell.cpp shell.h) + +# Make sure the compiler can find include files for the library +# when other libraries or executables link to this library +target_include_directories (msh-console PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/msh-console-library/command.cpp b/msh-console-library/command.cpp new file mode 100644 index 0000000..93f0b7e --- /dev/null +++ b/msh-console-library/command.cpp @@ -0,0 +1,34 @@ +#include "shell.h" + +Command::Command(const string& n, int (*funcptr)(const vector* args, CommandExecutor*)) : name(n) , funcCommand(funcptr){ + checkObj(); + numCom++; +} + +int Command::execute(const vector* args, CommandExecutor* ciao){ + if(args==nullptr) return -1; + return (*funcCommand)(args, ciao); +} + +string Command::getName(){ + return this->name; +} + +Command::Command(const Command& old) : name(old.name) , funcCommand(old.funcCommand){ + checkObj(); + numCom++; +} + +void Command::checkObj(){ + for(unsigned int i=0; i'a'&&name[i]<'z')||(name[i]>'A'&&name[i]<'Z'))){ + throw CommandNameNotValidException(); + } + } +} + +Command::~Command(){ + numCom--; +} + +unsigned int Command::numCom = 0; diff --git a/msh-console-library/command.h b/msh-console-library/command.h new file mode 100644 index 0000000..ddf56a9 --- /dev/null +++ b/msh-console-library/command.h @@ -0,0 +1,28 @@ +#ifndef COMMAND_H +#define COMMAND_H +#include +#include +#include "shell.h" +#include "commandexecutor.h" + +using std::string; +using std::vector; + +class Command +{ + const string name; + static unsigned int numCom; + int (*funcCommand)(const vector*,CommandExecutor*); + void checkObj(); + +public: + Command(const string& n, int (*funcptr)(const vector* args, CommandExecutor*)); + Command(const Command&); + ~Command(); + string getName(); + int execute(const vector*, CommandExecutor* ciao); +}; + +class CommandNameNotValidException{}; + +#endif // COMMAND_H diff --git a/msh-console-library/commandexecutor.cpp b/msh-console-library/commandexecutor.cpp new file mode 100644 index 0000000..978d62f --- /dev/null +++ b/msh-console-library/commandexecutor.cpp @@ -0,0 +1,6 @@ +#include "commandexecutor.h" + +CommandExecutor::CommandExecutor() +{ + +} diff --git a/msh-console-library/commandexecutor.h b/msh-console-library/commandexecutor.h new file mode 100644 index 0000000..5a54499 --- /dev/null +++ b/msh-console-library/commandexecutor.h @@ -0,0 +1,15 @@ +#ifndef COMMANDEXECUTOR_H +#define COMMANDEXECUTOR_H +#include +#include + +class CommandExecutor +{ +public: + virtual int executeCmd(std::vector* args) = 0; + virtual int executeCmd(const std::string& args) = 0; + virtual size_t howManyCmds() const = 0; + CommandExecutor(); +}; + +#endif // COMMANDEXECUTOR_H diff --git a/msh-console-library/commands.cpp b/msh-console-library/commands.cpp new file mode 100644 index 0000000..23ad5c2 --- /dev/null +++ b/msh-console-library/commands.cpp @@ -0,0 +1,26 @@ +#include "shell.h" + +Shell::Commands::Commands(Shell* s) : commands(), threadCommands() +{ + parent = s; +} + +void Shell::Commands::add(Command* cmd, bool isthread){ + if(!isthread) commands.push_back(cmd); + else threadCommands.push_back(cmd); +} + +size_t Shell::Commands::howMany() const{ + return commands.size(); +} + +int Shell::Commands::launch(const vector* args, bool launchThread){ + for(unsigned int i=0; i<(launchThread ? threadCommands.size() : commands.size()); i++){ + if((launchThread ? threadCommands[i]->getName() : commands[i]->getName())==args->operator [](0)){ + return (launchThread ? threadCommands[i]->execute(args, parent) : commands[i]->execute(args, parent)); + } + } + throw CommandNotFoundException(); +} + + diff --git a/msh-console-library/shell.cpp b/msh-console-library/shell.cpp new file mode 100644 index 0000000..d97c065 --- /dev/null +++ b/msh-console-library/shell.cpp @@ -0,0 +1,196 @@ +#include "shell.h" + +string Shell::getPs() const +{ + return ps; +} + +void Shell::setPs(const string &value) +{ + ps = value; +} + +string Shell::getName() const +{ + return name; +} + +void Shell::setName(const string &value) +{ + name = value; +} + +Shell::Shell( string n, string ps,void (*s)(Shell*), void (*pss)(Shell*)) : cmds(this) +{ + shellSetup = s; + name = n; + this->ps = ps; + shellPostSetup = pss; + notLoop = false; +} + +void Shell::launch(){ + //launch setup + if(notLoop) { + throw ShellLaunchInSetupException(); + return; + } + if(shellSetup!=0) { + notLoop = true; + (*shellSetup)(this); + notLoop = false; + } + //launch loop + string* line; + vector* args; + int status; + do { + bool readSuccess; + do{ + cout << ps << " "; + try{ + line = read_line(); + readSuccess = true; + } + catch (IsUndoingLineException){ + cout << "\n"; + readSuccess = false; + } + }while(!readSuccess); + args = split_line(line); + status = executeCmd(args); + delete args; + } while (status); + if(shellPostSetup!=0) { + shellPostSetup(this); + } +} + +int Shell::launchCmd(vector* args) +{ + int status; + + pid_t pid = fork(); + if (pid == 0) { + //child process + + //execute threadCommand + int a; + try { + a=cmds.launch(args, true); + } + catch (CommandNotFoundException){ + //execute bash command or program + vector argv(args->size() + 1); + size_t i; + for (i = 0; i != args->size()-1; ++i) + { + argv[i] = &(args->operator[](i)[0]); + } + argv[i] = NULL; + if((a=execvp(argv[0], argv.data())) == -1) { + cerr << name <<": command " << args->operator [](0) << " not found\n"; + } + exit(EXIT_FAILURE); + } + exit(EXIT_SUCCESS); + } + else if (pid < 0) { + // Error forking + cerr << name <<": error forking the process\n"; + } + else { + // Parent process + do { + //wait until child finished + waitpid(pid, &status, WUNTRACED); + } while (!WIFEXITED(status) && !WIFSIGNALED(status)); + } + return 1; +} + +int Shell::executeCmd(vector* args) +{ + if (args->operator [](0) == "\0") { + // An empty command was entered. + return 1; + } + int ret; + try { + ret = cmds.launch(args, false); + } + catch(CommandNotFoundException) { + ret = launchCmd(args); + } + return ret; +} + +inline int Shell::executeCmd(const std::string &args){ + return executeCmd(split_line(&args)); +} + +void Shell::EofHandler(int){ + undoingLine = true; +} + +void Shell::setShellSetup(void (*s)(Shell *)){ + shellSetup=s; +} + +void (*Shell::getShellSetup())(Shell*) { + return shellSetup; +} + +void Shell::setShellPostSetup(void (*s)(Shell *)){ + shellPostSetup=s; +} + +void (*Shell::getShellPostSetup())(Shell*) { + return shellPostSetup; +} + + +void setEofHandler(void (*funcptr)(int)){ + struct sigaction *sa = new struct sigaction(); + sa->sa_handler = funcptr; + sa->sa_flags = 0; // not SA_RESTART!; + sigaction(SIGINT, sa, NULL); + delete sa; +} + +string* Shell::read_line() +{ + string* buffer = new string(); + setEofHandler(EofHandler); + getline(cin,*buffer); // get command + cin.clear(); // clear flags + + if(undoingLine){ + undoingLine=false; + throw IsUndoingLineException(); + } + return buffer; +} + +vector* Shell::split_line(const string* line) +{ + vector* tokens = new vector(); + string ln = *line; + istringstream is(ln); + int i; + for(i=0; getline(is, ln, ' '); i++){ + tokens->push_back(ln); + } + tokens->push_back("\0"); + return tokens; +} + +bool Shell::undoingLine = false; + +void Shell::addCmd(Command* cmd, bool isthread){ + cmds.add(cmd, isthread); +} + +size_t Shell::howManyCmds() const{ + return cmds.howMany(); +} diff --git a/msh-console-library/shell.h b/msh-console-library/shell.h new file mode 100644 index 0000000..192a0d8 --- /dev/null +++ b/msh-console-library/shell.h @@ -0,0 +1,75 @@ +#ifndef SHELL_H +#define SHELL_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "command.h" +//#include "commands.h" +#include "commandexecutor.h" +using std::string; +using std::cin; +using std::cout; +using std::cerr; +using std::string; +using std::vector; +using std::istringstream; +using std::nullptr_t; + +class Shell : public CommandExecutor +{ + class Commands + { + Shell* parent; + vector commands; //commands that work the same thread of the shell + vector threadCommands; //commands that work on a different thread + + public: + Commands(Shell*); + void add(Command*, bool isthread=false); + size_t howMany() const; + int launch(const vector* args, bool launchThread=false); + }; + static bool undoingLine; + Commands cmds; + string ps; + string name; + void (*shellSetup)(Shell *); + void (*shellPostSetup)(Shell *); + bool notLoop; + + int launchCmd(vector* args); + static void EofHandler(int); + string* read_line(); + vector* split_line(const string* line); + + class IsUndoingLineException {}; + +public: + Shell(string n="msh", string ps="MSH$", void (*s)(Shell*)=0, void (*pss)(Shell*)=0); + string getPs() const; + void setPs(const string &value); + string getName() const; + void setName(const string &value); + void launch(); + void setShellSetup(void (*)(Shell *)); + void (*getShellSetup())(Shell*); + void setShellPostSetup(void (*)(Shell *)); + void (*getShellPostSetup())(Shell*); + //for in-shell commands + void addCmd(Command *cmd, bool isThread=false); + size_t howManyCmds() const; + int executeCmd(vector* args); + int executeCmd(const string& args); + + //excepsetions + class CommandNotFoundException{}; + class ShellLaunchInSetupException{}; +}; + +#endif // SHELL_H diff --git a/snippet/CMakeLists.txt b/snippet/CMakeLists.txt new file mode 100644 index 0000000..dfc4390 --- /dev/null +++ b/snippet/CMakeLists.txt @@ -0,0 +1,9 @@ +# snippet for showing how the library works + +project(msh-console-library-snippet) +cmake_minimum_required(VERSION 2.8) + +SET(CMAKE_CXX_FLAGS "-std=c++11") +add_executable (../../build-msh-console/msh-console-test main.cpp cmds.cpp) +target_link_libraries (../../build-msh-console/msh-console-test LINK_PUBLIC msh-console) + diff --git a/snippet/cmds.cpp b/snippet/cmds.cpp new file mode 100644 index 0000000..4641550 --- /dev/null +++ b/snippet/cmds.cpp @@ -0,0 +1,28 @@ +/** + builtin commands +*/ + +#include "cmds.h" + +using namespace std; + +int Cmds::cdExecute(const vector* args, CommandExecutor*){ + if (args->operator[](1) == "\0") { + cerr << "expected argument to \"cd\"\n"; + } else { + if (chdir(args->operator[](1).c_str()) != 0) { + cerr << "error"; + } + } + return 1; +} + +int Cmds::exitExecute(const vector*, CommandExecutor*){ + std::exit(EXIT_SUCCESS); +} + +int Cmds::helpExecute(const vector*, CommandExecutor*){ + cout << " info" << endl; + return 1; +} + diff --git a/snippet/cmds.h b/snippet/cmds.h new file mode 100644 index 0000000..04b5cbe --- /dev/null +++ b/snippet/cmds.h @@ -0,0 +1,17 @@ +#ifndef CMDS_H +#define CMDS_H +#include "../msh-console-library/commandexecutor.h" +#include +#include +#include +#include +using std::string; +using std::vector; + +namespace Cmds { + int cdExecute(const vector* args, CommandExecutor*); + int exitExecute(const vector*, CommandExecutor*); + int helpExecute(const vector*, CommandExecutor*); +} + +#endif // CMDS_H diff --git a/snippet/main.cpp b/snippet/main.cpp new file mode 100644 index 0000000..22879ce --- /dev/null +++ b/snippet/main.cpp @@ -0,0 +1,32 @@ +/** + Little snippet for library use. +*/ + +#include +#include +#include "../msh-console-library/shell.h" +#include "../msh-console-library/command.h" +#include "cmds.h" +using namespace std; +using namespace Cmds; + +static void setup(Shell *); + +int main(int argc, char **argv) +{ + string c = "msh-console-test"; + string ps = "[msh-console-test]:"; + Shell mshConsoleTest(c, ps, &setup); + + //add builtin commands + mshConsoleTest.addCmd(new Command("cd", &cdExecute)); + mshConsoleTest.addCmd(new Command("exit", &exitExecute)); + mshConsoleTest.addCmd(new Command("help", &helpExecute)); + mshConsoleTest.launch(); + return EXIT_SUCCESS; +} + +void setup(Shell *s){ + cout << "Now entering in test shell...\n" << endl; + s->executeCmd("stty -ctlecho"); +}