Renamed some functions and variables in SIGINT handling. README updated. Removed "using" directives in the headers. Input, output and error streams are now fields

of  Shell. stringToArgcArgv has its own namespace.
This commit is contained in:
Claudio Maggioni 2016-04-23 13:03:53 +02:00
parent f6c3135c27
commit 5d68248b18
15 changed files with 646 additions and 315 deletions

2
.gitignore vendored
View File

@ -2,7 +2,7 @@
build-* build-*
#internal test #internal test
msh-console-library/main.cpp #msh-console-library/main.cpp
# Autosaves # Autosaves
*.autosave *.autosave

View File

@ -1,8 +1,17 @@
# msh-console # Msh-console
Library that provides a bash-like interface for CLI C++ programs * included flag handling implemented with popt.h;
Library that provides a bash-like command line for C++ programs. Features included:
* creation of personalized commands in the same thread of the shell or in other threads;
* basic shell functionalities, such as execution of other programs in the system. This is implemented with execvp().
What is not actually included:
* advanced shell features, like:
* piping,
* redirection,
* scripting,
* command history.
### Credits ### Credits
Code based on "Write a Shell in C" - by Stephen Brennan (http://brennan.io/2015/01/16/write-a-shell-in-c/) Code based on "Write a Shell in C" tutorial by Stephen Brennan (http://brennan.io/2015/01/16/write-a-shell-in-c/).
The code has been slightly modified. The code has been slightly modified.
The code contains stringtoargcargv.cpp, a set of functions written by Bernhard Eder (http://web.archive.org/web/20121030075237/http://bbgen.net/blog/2011/06/string-to-argc-argv) for parsing a string into argc and argv. The code contains stringtoargcargv.cpp, a set of functions written by Bernhard Eder (http://web.archive.org/web/20121030075237/http://bbgen.net/blog/2011/06/string-to-argc-argv) for parsing a string into argc and argv.
### The library ### Compiling
The library can be compiled as shared library with the CMakeLists.txt file already in msh-console-library/. The library can be compiled as shared library with the CMakeLists.txt file already in msh-console-library/.

View File

@ -1,22 +1,38 @@
# msh-console dynamic library by praticamentetilde (Claudio Maggioni) # MIT License
# licensed with "The Unlicense" #
# Copyright (c) 2016 Claudio Maggioni
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
project(msh-console-library) project(msh-console-library)
cmake_minimum_required(VERSION 2.8) cmake_minimum_required(VERSION 2.8)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -lpopt") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -lpopt")
FILE(GLOB sources *.cpp) list(APPEND SRC_LIST "command.cpp" "commandexecutor.h" "commands.cpp" "datas.cpp" "shell.cpp" "stringtoargcargv.cpp"
FILE(GLOB headers *.h) "commandexecutor.cpp" "command.h" "data.cpp" "main.cpp" "shell.h" "stringtoargcargv.h")
set(lib OFF) set(lib ON) #off=debug demo with main.cpp, on=library
if(${lib}) if(${lib})
MESSAGE( STATUS "Building .so.1") MESSAGE(STATUS "Building .so.1")
#compile the library list(REMOVE_ITEM SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp) #remove demo file
# Create a library which includes the source listed.
# The extension is already found. Any number of sources could be listed here.
list(REMOVE_ITEM SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp)
add_library (mshconsole SHARED ${SRC_LIST}) add_library (mshconsole SHARED ${SRC_LIST})
else(${lib}) else(${lib})
MESSAGE( STATUS "Building exec (debug only)") MESSAGE(STATUS "Building exec (debug only)")
add_executable(${PROJECT_NAME} ${sources} ${headers}) add_executable(${PROJECT_NAME} ${SRC_LIST})
endif(${lib}) endif(${lib})

View File

@ -1,7 +1,31 @@
/**
MIT License
Copyright (c) 2016 Claudio Maggioni
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "shell.h" #include "shell.h"
namespace mshconsole { namespace mshconsole {
Command::Command(const string& n, int (*funcptr)(CommandExecutor* whoExecuted,const Datas& data, const vector<const char*> argv)) : Command::Command(const std::string& n, int (*funcptr)(CommandExecutor* whoExecuted,const Datas& data, const std::vector<const char*> argv)) :
name(n), funcCommand(funcptr), optionNum(0){ name(n), funcCommand(funcptr), optionNum(0){
checkObj(); checkObj();
} }
@ -16,7 +40,7 @@ namespace mshconsole {
optionNum++; optionNum++;
} }
int Command::execute(const struct Params& p, CommandExecutor* c){ int Command::execute(const struct Params& p, CommandExecutor* c, std::ostream& errorStream){
if(p.argc<=0) return -1; if(p.argc<=0) return -1;
// options a, b, c take integer arguments // options a, b, c take integer arguments
@ -46,32 +70,32 @@ namespace mshconsole {
// poptGetNextOpt returns -1 when the final argument has been parsed // poptGetNextOpt returns -1 when the final argument has been parsed
// otherwise an error occured // otherwise an error occured
if (val != -1) { if (val != -1) {
cerr << name << ": "; errorStream << name << ": ";
switch(val) { switch(val) {
case POPT_ERROR_NOARG: case POPT_ERROR_NOARG:
cerr << "argument missing for an option\n"; errorStream << "argument missing for an option\n";
return 1; return 1;
case POPT_ERROR_BADOPT: case POPT_ERROR_BADOPT:
cerr << "option not found\n"; errorStream << "option not found\n";
return 1; return 1;
case POPT_ERROR_BADNUMBER: case POPT_ERROR_BADNUMBER:
case POPT_ERROR_OVERFLOW: case POPT_ERROR_OVERFLOW:
cerr << "option could not be converted to number\n"; errorStream << "option could not be converted to number\n";
return 1; return 1;
default: default:
cerr << "unknown error in option processing\n"; errorStream << "unknown error in option processing\n";
return 1; return 1;
} }
} }
vector<const char*> nonOptionArgs; std::vector<const char*> nonOptionArgs;
while (poptPeekArg(pc) != NULL) while (poptPeekArg(pc) != NULL)
nonOptionArgs.push_back(const_cast<const char*>(poptGetArg(pc))); nonOptionArgs.push_back(const_cast<const char*>(poptGetArg(pc)));
return (*funcCommand)(c,datas,nonOptionArgs); return (*funcCommand)(c,datas,nonOptionArgs);
} }
const string& Command::getName(){ const std::string& Command::getName(){
return this->name; return this->name;
} }

View File

@ -1,7 +1,32 @@
/**
MIT License
Copyright (c) 2016 Claudio Maggioni
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef COMMAND_H #ifndef COMMAND_H
#define COMMAND_H #define COMMAND_H
#pragma once #pragma once
#include <string> #include <string>
#include <iostream>
#include <vector> #include <vector>
#include <unistd.h> #include <unistd.h>
extern "C"{ extern "C"{
@ -9,15 +34,12 @@ extern "C"{
} }
#include "commandexecutor.h" #include "commandexecutor.h"
using std::string;
using std::vector;
namespace mshconsole{ namespace mshconsole{
class Command class Command
{ {
const string name; const std::string name;
void checkObj(); void checkObj();
vector<poptOption> opts; std::vector<poptOption> opts;
static const struct poptOption POPT_TERMINATOR; static const struct poptOption POPT_TERMINATOR;
size_t optionNum; size_t optionNum;
@ -55,26 +77,26 @@ namespace mshconsole{
double getDouble() const; double getDouble() const;
}; };
class Datas : public vector<Data*>{ class Datas : public std::vector<Data*>{
public: public:
Data* getOptData(const string& optionName) const; Data* getOptData(const std::string& optionName) const;
Data* getOptData(char opt) const; Data* getOptData(char opt) const;
Data* operator[](const string& opt) const; Data* operator[](const std::string& opt) const;
}; };
class DuplicatedOptionException{}; class DuplicatedOptionException{};
class CommandNameNotValidException{}; class CommandNameNotValidException{};
class InvalidOptionException{}; class InvalidOptionException{};
Command(const string& n, int (*funcptr)(CommandExecutor* whoExecuted,const Datas& data, const vector<const char*> argv)); Command(const std::string& n, int (*funcptr)(CommandExecutor* whoExecuted,const Datas& data, const std::vector<const char*> argv));
Command(const Command&); Command(const Command&);
~Command(); ~Command();
const string& getName(); const std::string& getName();
int execute(const struct Params&, CommandExecutor*); int execute(const struct Params&, CommandExecutor*,std::ostream& errorStream=std::cerr);
void addOption(const poptOption& option); void addOption(const poptOption& option);
private: private:
int (*funcCommand)(CommandExecutor* whoExecuted,const Datas& data, const vector<const char*> argv); int (*funcCommand)(CommandExecutor* whoExecuted,const Datas& data, const std::vector<const char*> argv);
}; };
} }

View File

@ -1,3 +1,27 @@
/**
MIT License
Copyright (c) 2016 Claudio Maggioni
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "commandexecutor.h" #include "commandexecutor.h"
namespace mshconsole { namespace mshconsole {
CommandExecutor::CommandExecutor() {} CommandExecutor::CommandExecutor() {}

View File

@ -1,3 +1,27 @@
/**
MIT License
Copyright (c) 2016 Claudio Maggioni
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef COMMANDEXECUTOR_H #ifndef COMMANDEXECUTOR_H
#define COMMANDEXECUTOR_H #define COMMANDEXECUTOR_H
#pragma once #pragma once

View File

@ -1,3 +1,27 @@
/**
MIT License
Copyright (c) 2016 Claudio Maggioni
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "shell.h" #include "shell.h"
namespace mshconsole { namespace mshconsole {
Shell::Commands::Commands(Shell* s) : commands(), threadCommands() Shell::Commands::Commands(Shell* s) : commands(), threadCommands()

View File

@ -1,3 +1,27 @@
/**
MIT License
Copyright (c) 2016 Claudio Maggioni
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "command.h" #include "command.h"
namespace mshconsole{ namespace mshconsole{

View File

@ -1,7 +1,31 @@
/**
MIT License
Copyright (c) 2016 Claudio Maggioni
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "command.h" #include "command.h"
namespace mshconsole { namespace mshconsole {
Command::Data* Command::Datas::getOptData(const string& opt) const{ Command::Data* Command::Datas::getOptData(const std::string& opt) const{
for(size_t i=0; i<this->size(); i++){ for(size_t i=0; i<this->size(); i++){
if(opt == this->at(i)->of->longName) return this->at(i); if(opt == this->at(i)->of->longName) return this->at(i);
} }
@ -11,7 +35,7 @@ namespace mshconsole {
if(opt == this->at(i)->of->shortName) return this->at(i); if(opt == this->at(i)->of->shortName) return this->at(i);
} }
} }
Command::Data* Command::Datas::operator [](const string& opt) const{ Command::Data* Command::Datas::operator [](const std::string& opt) const{
if(opt.length()>2) return getOptData(opt); if(opt.length()>2) return getOptData(opt);
if(opt.length()==1) return getOptData(opt[0]); if(opt.length()==1) return getOptData(opt[0]);
else return nullptr; else return nullptr;

View File

@ -0,0 +1,75 @@
#include <cstdlib>
#include <cstring>
#include <iostream>
#include "../msh-console-library/shell.h"
#include "../msh-console-library/command.h"
using namespace std;
using namespace mshconsole;
static void setup(Shell *);
int cdExecute(CommandExecutor* whoExecuted,const Command::Datas& data, const vector<const char*> argv){
if (argv.size() == 0) {
cerr << "expected argument to \"cd\"\n";
} else {
int ret;
if (strcmp("~",argv[0])==0) {
ret = chdir(getenv("HOME"));
}
else {
ret = chdir(argv[0]);
}
if (ret != 0) {
cerr << "cd: error executing the command\n";
return ret;
}
}
return 0;
}
int exitExecute(CommandExecutor* whoExecuted,const Command::Datas& data, const vector<const char*> argv){
whoExecuted->exit();
return 0;
}
int helpExecute(CommandExecutor* whoExecuted,const Command::Datas& data, const vector<const char*> argv){
cout << data.getOptData('p')->getString() << "\n";
if(data.getOptData('h')->getInt()){
cout << "These are the further details." << endl;
return 0;
}
if(strcmp(data.getOptData('p')->getString(),"prova")==0){
cout << "prova\n";
return 0;
}
cout << "Snippet console generated with msh-console library.\nUse option -h for further details." << endl;
return 0;
}
/**
Little snippet for library use.
*/
int main(int argc, char **argv)
{
string c = "msh-console-test";
string ps = "[msh-console-test]:";
Shell mshConsoleTest(c, ps, cin,cout,cerr, &setup);
Command* help = new Command("help", &helpExecute);
struct poptOption h = {"help",'h',POPT_ARG_NONE,NULL,1000,"advance","advaced options"};
struct poptOption p = {"prova",'p',POPT_ARG_STRING,NULL,1000,"cose","cosecose"};
help->addOption(h);
help->addOption(p);
//add builtin commands
mshConsoleTest.addCmd(help);
mshConsoleTest.addCmd(new Command("exit", &exitExecute));
mshConsoleTest.addCmd(new Command("cd", &cdExecute));
mshConsoleTest.launch();
return EXIT_SUCCESS;
}
void setup(Shell *s){
cout << "Now entering in test shell...\n" << endl;
//s->executeCmd("stty -ctlecho");
}

View File

@ -1,81 +1,109 @@
/**
MIT License
Copyright (c) 2016 Claudio Maggioni
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "shell.h" #include "shell.h"
namespace mshconsole { namespace mshconsole {
string Shell::getPs() const const std::string& Shell::getPs() const{
{
return ps; return ps;
} }
void Shell::setPs(const string &value) void Shell::setPs(const std::string &value){
{
ps = value; ps = value;
} }
string Shell::getName() const const std::string& Shell::getName() const{
{
return name; return name;
} }
void Shell::setName(const string &value) void Shell::setName(const std::string &value){
{
name = value; name = value;
} }
Shell::Shell( string n, string ps,void (*s)(Shell*), void (*pss)(Shell*)) : cmds(this) Shell::Shell(std::string nam, std::string ps, std::istream &i, std::ostream &o, std::ostream &e,
{ void (*s)(Shell*), void (*pss)(Shell*)) : cmds(this), inputStream(i),
outputStream(o), errorStream(e){
shellSetup = s; shellSetup = s;
name = n; name = nam;
this->ps = ps; this->ps = ps;
shellPostSetup = pss; shellPostSetup = pss;
notLoop = false;
} }
int Shell::launch(){ int Shell::launch(){
//Forbid launching shell if it is already launched in other threads.
static bool notLoop=false;
if(notLoop) return -1;
notLoop = true;
//launch setup //Launch the shell setup.
if(notLoop) { if(shellSetup!=0) (*shellSetup)(this);
return -1;
}
if(shellSetup!=0) {
notLoop = true;
(*shellSetup)(this);
notLoop = false;
}
//launch loop //Run the shell loop.
string* line;
struct Params p;
int exitCode; int exitCode;
try{ try{
do { std::string* line=nullptr;
bool readSuccess; struct Params p;
do{
cout << ps << " "; while(1){
delete line;
outputStream << ps << " "; //Print the prompt.
//Try reading the line. if SIGINT is caught, print another time the prompt and retry.
try{ try{
line = read_line(); line = readLine();
readSuccess = true;
} }
catch (IsUndoingLineException){ catch (IsUndoingLineException){
cout << "\n"; outputStream << "\n";
readSuccess = false; continue;
} }
}while(!readSuccess);
p = split_line(line); //Try line conversion from string to argc and argv. stringToArgcArgv() throws a std::runtime_error
executeCmd(p); //exception if the string has unproper quotes.
} while (1); try{
p = splitLine(line);
}
catch(std::runtime_error e){
errorStream << name << ": error in line parsing\n";
errorStream << e.what() << "\n";
continue;
}
//Execute the command.
executeCmd(p);
};
} catch(CommandExecutor::ExitException c) { } catch(CommandExecutor::ExitException c) {
exitCode=c.getCode(); exitCode=c.getCode();
} }
//launch postSetup //Launch postSetup().
if(shellPostSetup!=0) { if(shellPostSetup!=0) shellPostSetup(this);
shellPostSetup(this);
} notLoop = false;
return exitCode; return exitCode;
} }
int Shell::launchCmd(const struct Params& p) int Shell::launchCmd(const struct Params& p){
{
using std::exit;
int status; int status;
pid_t pid = fork(); pid_t pid = fork();
@ -84,21 +112,23 @@ namespace mshconsole {
//execute threadCommand //execute threadCommand
int a; int a;
try { try {
a=cmds.launch(p, true); a=cmds.launch(p, true);
std::exit(EXIT_SUCCESS);
} }
catch (CommandNotFoundException){ catch (CommandNotFoundException){}
//execute bash command or program
if((a=execvp(p.argv[0], p.argv)) == -1) { //execute bash command or program
cerr << name <<": command " << p.argv[0] << " not found\n"; if((a=execvp(p.argv[0], p.argv)) == -1) {
} outputStream << name << ": execution failed. Errno is set to: "<<strerror(errno)<<"\n";
exit(EXIT_FAILURE); std::exit(EXIT_FAILURE);
} }
exit(EXIT_SUCCESS); else std::exit(EXIT_SUCCESS);
} }
else if (pid < 0) { else if (pid < 0) {
// Error forking // Error forking
cerr << name <<": error forking the process\n"; errorStream << name <<": error in process forking.\n";
} }
else { else {
// Parent process // Parent process
@ -110,34 +140,32 @@ namespace mshconsole {
return 1; return 1;
} }
int Shell::executeCmd(const struct Params& p) int Shell::executeCmd(const struct Params& p){
{ if (!p.argc) return 1; //Return if the line is empty.
if (!p.argc) { //empty line
return 1;
}
int ret;
try { try {
ret = cmds.launch(p, false); //Search in non-thread commands.
return cmds.launch(p, false);
} }
catch(CommandNotFoundException) { catch(CommandNotFoundException) {}
ret = launchCmd(p);
} //Execute the threadCommand or the executable.
return ret; return launchCmd(p);
} }
inline int Shell::executeCmd(const std::string &args){ inline int Shell::executeCmd(const std::string &args){
return executeCmd(split_line(&args)); return executeCmd(splitLine(&args));
} }
void Shell::EofHandler(int){ void Shell::SIGINTHandler(int,siginfo_t*, void *){
undoingLine = true; SIGINTRaised = true;
} }
void Shell::setShellSetup(void (*s)(Shell *)){ void Shell::setShellSetup(void (*s)(Shell *)){
shellSetup=s; shellSetup=s;
} }
void (*Shell::getShellSetup())(Shell*) { void (*Shell::getShellSetup())(Shell*) const{
return shellSetup; return shellSetup;
} }
@ -145,40 +173,46 @@ namespace mshconsole {
shellPostSetup=s; shellPostSetup=s;
} }
void (*Shell::getShellPostSetup())(Shell*) { void (*Shell::getShellPostSetup())(Shell*) const{
return shellPostSetup; return shellPostSetup;
} }
void setEofHandler(void (*funcptr)(int)){ struct sigaction* setHandler(unsigned signal, void (*funcptr)(int, siginfo_t*, void*)){
struct sigaction *sa = new struct sigaction(); struct sigaction* before = new struct sigaction();
sa->sa_handler = funcptr; struct sigaction sa;
sa->sa_flags = 0; // not SA_RESTART!; sa.sa_sigaction = funcptr;
sigaction(SIGINT, sa, NULL); sa.sa_flags = 0;
delete sa; sigaction(signal, &sa, before);
} }
string* Shell::read_line() void restoreHandler(unsigned signal, struct sigaction* before){
{ sigaction(signal, before, NULL);
string* buffer = new string(); delete before;
setEofHandler(EofHandler); }
getline(cin,*buffer); // get command
cin.clear(); // clear flags
if(undoingLine){ std::string* Shell::readLine(){
undoingLine=false; std::string* buffer = new std::string();
struct sigaction* beforeSIGINT = setHandler(SIGINT,SIGINTHandler);
getline(inputStream,*buffer); //Get line of input TODO: handle directional keys
inputStream.clear(); //Clear flags. This is needed for the detection of SIGINT
if(SIGINTRaised){
SIGINTRaised=false;
throw IsUndoingLineException(); throw IsUndoingLineException();
} }
restoreHandler(SIGINT, beforeSIGINT);
return buffer; return buffer;
} }
struct Params Shell::split_line(const string* line){ struct Params Shell::splitLine(const std::string* line){
struct Params p; struct Params p;
stringToArgcArgv(*line, &(p.argc), &(p.argv)); stringToArgcArgv(*line, &(p.argc), &(p.argv));
return p; return p;
} }
bool Shell::undoingLine = false; bool Shell::SIGINTRaised = false;
void Shell::addCmd(Command* cmd, bool isthread){ void Shell::addCmd(Command* cmd, bool isthread){
cmds.add(cmd, isthread); cmds.add(cmd, isthread);

View File

@ -1,3 +1,27 @@
/**
MIT License
Copyright (c) 2016 Claudio Maggioni
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef SHELL_H #ifndef SHELL_H
#define SHELL_H #define SHELL_H
#pragma once #pragma once
@ -7,20 +31,14 @@
#include <unistd.h> #include <unistd.h>
#include <string> #include <string>
#include <vector> #include <vector>
#include <sstream>
#include <cstddef> #include <cstddef>
#include <csignal> #include <csignal>
#include <cerrno>
#include "command.h" #include "command.h"
#include <wordexp.h> #include <wordexp.h>
#include "commandexecutor.h" #include "commandexecutor.h"
#include "stringtoargcargv.h" #include "stringtoargcargv.h"
using std::string; using namespace StringToArgcArgv;
using std::cin;
using std::cout;
using std::cerr;
using std::string;
using std::vector;
using std::istringstream;
namespace mshconsole { namespace mshconsole {
@ -29,8 +47,8 @@ namespace mshconsole {
class Commands class Commands
{ {
Shell* parent; Shell* parent;
vector<Command*> commands; //commands that work the same thread of the shell std::vector<Command*> commands; //commands that work the same thread of the shell
vector<Command*> threadCommands; //commands that work on a different thread std::vector<Command*> threadCommands; //commands that work on a different thread
public: public:
Commands(Shell*); Commands(Shell*);
@ -39,40 +57,47 @@ namespace mshconsole {
int launch(const struct Params& args, bool launchThread=false); int launch(const struct Params& args, bool launchThread=false);
}; };
static bool undoingLine; static bool SIGINTRaised;
Commands cmds; Commands cmds;
string ps; std::string ps;
string name; std::string name;
std::ostream& errorStream;
std::ostream& outputStream;
std::istream& inputStream;
void (*shellSetup)(Shell *); void (*shellSetup)(Shell *);
void (*shellPostSetup)(Shell *); void (*shellPostSetup)(Shell *);
bool notLoop;
int launchCmd(const struct Params& args); int launchCmd(const struct Params& args);
static void EofHandler(int); static void SIGINTHandler(int,siginfo_t *info, void *);
string* read_line(); std::string* readLine();
struct Params split_line(const string* line); struct Params splitLine(const std::string* line);
class IsUndoingLineException {}; class IsUndoingLineException {};
class CommandNotFoundException {}; class CommandNotFoundException {};
public: public:
Shell(string n="msh", string ps="MSH$", void (*s)(Shell*)=0, void (*pss)(Shell*)=0); Shell(std::string n, std::string ps, std::istream& inputStream, std::ostream& outputStream,
string getPs() const; std::ostream& errorStream, void (*s)(Shell*)=0, void (*pss)(Shell*)=0);
void setPs(const string &value); const std::string& getPs() const;
string getName() const; void setPs(const std::string &value);
void setName(const string &value); const std::string& getName() const;
void setName(const std::string &value);
int launch(); int launch();
void setShellSetup(void (*)(Shell *)); void setShellSetup(void (*)(Shell *));
void (*getShellSetup())(Shell*); void (*getShellSetup())(Shell*) const;
void setShellPostSetup(void (*)(Shell *)); void setShellPostSetup(void (*)(Shell *));
void (*getShellPostSetup())(Shell*); void (*getShellPostSetup())(Shell*) const;
//for in-shell commands //for in-shell commands
void addCmd(Command *cmd, bool isThread=false); void addCmd(Command *cmd, bool isThread=false);
size_t howManyCmds() const;
int executeCmd(const struct Params& args); int executeCmd(const struct Params& args);
int executeCmd(const string& args); int executeCmd(const std::string& args);
size_t howManyCmds() const;
}; };
extern struct sigaction* setHandler(unsigned signal, void (*funcptr)(int, siginfo_t*, void*));
extern void restoreHandler(unsigned signal, struct sigaction* before);
} }
#endif // SHELL_H #endif // SHELL_H

View File

@ -1,4 +1,4 @@
/* stringtoargcargv.cpp -- Parsing a string to std::vector<string> /** stringtoargcargv.cpp -- Parsing a string to std::vector<string>
Copyright (C) 2011 Bernhard Eder Copyright (C) 2011 Bernhard Eder
@ -22,171 +22,176 @@
*/ */
///The code is modified for the null-termination of argv
#include "stringtoargcargv.h" #include "stringtoargcargv.h"
/* namespace StringToArgcArgv{
* Usage: /*
* int argc; * Usage:
* char** argv; * int argc;
* stringToArgcArgv("foo bar", &argc, &argv); * char** argv;
*/ * stringToArgcArgv("foo bar", &argc, &argv);
void stringToArgcArgv(const std::string& str, int* argc, char*** argv) */
{ void stringToArgcArgv(const std::string& str, int* argc, char*** argv)
std::vector<std::string> args = parse(str); {
std::vector<std::string> args = parse(str);
*argv = (char**)std::malloc(args.size() * sizeof(char*)); *argv = (char**)std::malloc((args.size()+1) * sizeof(char*));
int i=0; int i=0;
for(std::vector<std::string>::iterator it = args.begin(); for(std::vector<std::string>::iterator it = args.begin();
it != args.end(); it != args.end();
++it, ++i) ++it, ++i)
{ {
std::string arg = *it; std::string arg = *it;
(*argv)[i] = (char*)std::malloc((arg.length()+1) * sizeof(char)); (*argv)[i] = (char*)std::malloc((arg.length()+1) * sizeof(char));
std::strcpy((*argv)[i], arg.c_str()); std::strcpy((*argv)[i], arg.c_str());
}
*argc = args.size();
}
std::vector<std::string> parse(const std::string& args)
{
std::stringstream ain(args); // used to iterate over input string
ain >> std::noskipws; // do not skip white spaces
std::vector<std::string> oargs; // output list of arguments
std::stringstream currentArg("");
currentArg >> std::noskipws;
// current state
enum State {
InArg, // currently scanning an argument
InArgQuote, // currently scanning an argument (which started with quotes)
OutOfArg // currently not scanning an argument
};
State currentState = OutOfArg;
char currentQuoteChar = '\0'; // to distinguish between ' and " quotations
// this allows to use "foo'bar"
char c;
while(!ain.eof() && (ain >> c)) { // iterate char by char
if(_isQuote(c)) {
switch(currentState) {
case OutOfArg:
currentArg.str(std::string());
case InArg:
currentState = InArgQuote;
currentQuoteChar = c;
break;
case InArgQuote:
if(c == currentQuoteChar)
currentState = InArg;
else
currentArg << c;
break;
} }
(*argv)[i]=NULL;
*argc = args.size();
} }
else if(_isWhitespace(c)) {
switch(currentState) { std::vector<std::string> parse(const std::string& args)
case InArg: {
oargs.push_back(currentArg.str()); std::stringstream ain(args); // used to iterate over input string
currentState = OutOfArg; ain >> std::noskipws; // do not skip white spaces
break; std::vector<std::string> oargs; // output list of arguments
case InArgQuote:
currentArg << c; std::stringstream currentArg("");
break; currentArg >> std::noskipws;
case OutOfArg:
// nothing // current state
break; enum State {
} InArg, // currently scanning an argument
} InArgQuote, // currently scanning an argument (which started with quotes)
else if(_isEscape(c)) { OutOfArg // currently not scanning an argument
switch(currentState) { };
case OutOfArg: State currentState = OutOfArg;
currentArg.str(std::string());
currentState = InArg; char currentQuoteChar = '\0'; // to distinguish between ' and " quotations
case InArg: // this allows to use "foo'bar"
case InArgQuote:
if(ain.eof()) char c;
{ while(!ain.eof() && (ain >> c)) { // iterate char by char
#ifdef WIN32
// Windows doesn't care about an escape character at the end. if(_isQuote(c)) {
// It just adds \ to the arg. switch(currentState) {
currentArg << c; case OutOfArg:
#else currentArg.str(std::string());
throw(std::runtime_error("Found Escape Character at end of file.")); case InArg:
#endif currentState = InArgQuote;
currentQuoteChar = c;
break;
case InArgQuote:
if(c == currentQuoteChar)
currentState = InArg;
else
currentArg << c;
break;
} }
else
{ }
#ifdef WIN32 else if(_isWhitespace(c)) {
// Windows only escapes the " character. switch(currentState) {
// Every other character is just printed and the \ is added itself. case InArg:
char c1 = c; oargs.push_back(currentArg.str());
ain >> c; currentState = OutOfArg;
if(c != '\"') break;
currentArg << c1; // only ignore \ when next char is " case InArgQuote:
ain.unget(); currentArg << c;
#else break;
ain >> c; case OutOfArg:
currentArg << c; // nothing
#endif break;
} }
break; }
else if(_isEscape(c)) {
switch(currentState) {
case OutOfArg:
currentArg.str(std::string());
currentState = InArg;
case InArg:
case InArgQuote:
if(ain.eof())
{
#ifdef WIN32
// Windows doesn't care about an escape character at the end.
// It just adds \ to the arg.
currentArg << c;
#else
throw(std::runtime_error("Found Escape Character at end of file."));
#endif
}
else
{
#ifdef WIN32
// Windows only escapes the " character.
// Every other character is just printed and the \ is added itself.
char c1 = c;
ain >> c;
if(c != '\"')
currentArg << c1; // only ignore \ when next char is "
ain.unget();
#else
ain >> c;
currentArg << c;
#endif
}
break;
}
}
else {
switch(currentState) {
case InArg:
case InArgQuote:
currentArg << c;
break;
case OutOfArg:
currentArg.str(std::string());
currentArg << c;
currentState = InArg;
break;
}
}
} }
if(currentState == InArg)
oargs.push_back(currentArg.str());
else if(currentState == InArgQuote)
throw(std::runtime_error("Starting quote has no ending quote."));
return oargs;
} }
else {
switch(currentState) {
case InArg:
case InArgQuote:
currentArg << c;
break;
case OutOfArg: bool _isQuote(char c)
currentArg.str(std::string()); {
currentArg << c; if(c == '\"')
currentState = InArg; return true;
break; else if(c == '\'')
} return true;
return false;
} }
}
if(currentState == InArg) bool _isEscape(char c)
oargs.push_back(currentArg.str()); {
else if(currentState == InArgQuote) if(c == '\\')
throw(std::runtime_error("Starting quote has no ending quote.")); return true;
return oargs; return false;
} }
bool _isQuote(char c) bool _isWhitespace(char c)
{ {
if(c == '\"') if(c == ' ')
return true; return true;
else if(c == '\'') else if(c == '\t')
return true; return true;
return false; return false;
} }
bool _isEscape(char c)
{
if(c == '\\')
return true;
return false;
}
bool _isWhitespace(char c)
{
if(c == ' ')
return true;
else if(c == '\t')
return true;
return false;
} }

View File

@ -1,8 +1,4 @@
#ifndef STRINGTOARGCARGV_H /** stringtoargcargv.cpp -- Parsing a string to std::vector<string>
#define STRINGTOARGCARGV_H
#pragma once
/* stringtoargcargv.cpp -- Parsing a string to std::vector<string>
Copyright (C) 2011 Bernhard Eder Copyright (C) 2011 Bernhard Eder
@ -26,6 +22,10 @@
*/ */
#ifndef STRINGTOARGCARGV_H
#define STRINGTOARGCARGV_H
#pragma once
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
@ -35,11 +35,12 @@
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
bool _isQuote(char c); namespace StringToArgcArgv{
bool _isEscape(char c); bool _isQuote(char c);
bool _isEscape(char c); bool _isEscape(char c);
bool _isWhitespace(char c); bool _isEscape(char c);
std::vector<std::string> parse(const std::string& args); bool _isWhitespace(char c);
void stringToArgcArgv(const std::string& str, int* argc, char*** argv); std::vector<std::string> parse(const std::string& args);
void stringToArgcArgv(const std::string& str, int* argc, char*** argv);
}
#endif //STRINGTOARGCARGV_H #endif //STRINGTOARGCARGV_H