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-*
#internal test
msh-console-library/main.cpp
#msh-console-library/main.cpp
# Autosaves
*.autosave

View File

@ -1,8 +1,17 @@
# msh-console
Library that provides a bash-like interface for CLI C++ programs
# Msh-console
* 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
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 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/.

View File

@ -1,22 +1,38 @@
# msh-console dynamic library by praticamentetilde (Claudio Maggioni)
# licensed with "The Unlicense"
# 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.
project(msh-console-library)
cmake_minimum_required(VERSION 2.8)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -lpopt")
FILE(GLOB sources *.cpp)
FILE(GLOB headers *.h)
list(APPEND SRC_LIST "command.cpp" "commandexecutor.h" "commands.cpp" "datas.cpp" "shell.cpp" "stringtoargcargv.cpp"
"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})
MESSAGE( STATUS "Building .so.1")
#compile the library
# 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)
MESSAGE(STATUS "Building .so.1")
list(REMOVE_ITEM SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp) #remove demo file
add_library (mshconsole SHARED ${SRC_LIST})
else(${lib})
MESSAGE( STATUS "Building exec (debug only)")
add_executable(${PROJECT_NAME} ${sources} ${headers})
MESSAGE(STATUS "Building exec (debug only)")
add_executable(${PROJECT_NAME} ${SRC_LIST})
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"
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){
checkObj();
}
@ -16,7 +40,7 @@ namespace mshconsole {
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;
// options a, b, c take integer arguments
@ -46,32 +70,32 @@ namespace mshconsole {
// poptGetNextOpt returns -1 when the final argument has been parsed
// otherwise an error occured
if (val != -1) {
cerr << name << ": ";
errorStream << name << ": ";
switch(val) {
case POPT_ERROR_NOARG:
cerr << "argument missing for an option\n";
errorStream << "argument missing for an option\n";
return 1;
case POPT_ERROR_BADOPT:
cerr << "option not found\n";
errorStream << "option not found\n";
return 1;
case POPT_ERROR_BADNUMBER:
case POPT_ERROR_OVERFLOW:
cerr << "option could not be converted to number\n";
errorStream << "option could not be converted to number\n";
return 1;
default:
cerr << "unknown error in option processing\n";
errorStream << "unknown error in option processing\n";
return 1;
}
}
vector<const char*> nonOptionArgs;
std::vector<const char*> nonOptionArgs;
while (poptPeekArg(pc) != NULL)
nonOptionArgs.push_back(const_cast<const char*>(poptGetArg(pc)));
return (*funcCommand)(c,datas,nonOptionArgs);
}
const string& Command::getName(){
const std::string& Command::getName(){
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
#define COMMAND_H
#pragma once
#include <string>
#include <iostream>
#include <vector>
#include <unistd.h>
extern "C"{
@ -9,15 +34,12 @@ extern "C"{
}
#include "commandexecutor.h"
using std::string;
using std::vector;
namespace mshconsole{
class Command
{
const string name;
const std::string name;
void checkObj();
vector<poptOption> opts;
std::vector<poptOption> opts;
static const struct poptOption POPT_TERMINATOR;
size_t optionNum;
@ -55,26 +77,26 @@ namespace mshconsole{
double getDouble() const;
};
class Datas : public vector<Data*>{
class Datas : public std::vector<Data*>{
public:
Data* getOptData(const string& optionName) const;
Data* getOptData(const std::string& optionName) const;
Data* getOptData(char opt) const;
Data* operator[](const string& opt) const;
Data* operator[](const std::string& opt) const;
};
class DuplicatedOptionException{};
class CommandNameNotValidException{};
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 string& getName();
int execute(const struct Params&, CommandExecutor*);
const std::string& getName();
int execute(const struct Params&, CommandExecutor*,std::ostream& errorStream=std::cerr);
void addOption(const poptOption& option);
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"
namespace mshconsole {
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
#define COMMANDEXECUTOR_H
#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"
namespace mshconsole {
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"
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"
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++){
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);
}
}
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()==1) return getOptData(opt[0]);
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"
namespace mshconsole {
string Shell::getPs() const
{
const std::string& Shell::getPs() const{
return ps;
}
void Shell::setPs(const string &value)
{
void Shell::setPs(const std::string &value){
ps = value;
}
string Shell::getName() const
{
const std::string& Shell::getName() const{
return name;
}
void Shell::setName(const string &value)
{
void Shell::setName(const std::string &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;
name = n;
name = nam;
this->ps = ps;
shellPostSetup = pss;
notLoop = false;
}
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
if(notLoop) {
return -1;
}
if(shellSetup!=0) {
notLoop = true;
(*shellSetup)(this);
notLoop = false;
}
//Launch the shell setup.
if(shellSetup!=0) (*shellSetup)(this);
//launch loop
string* line;
struct Params p;
//Run the shell loop.
int exitCode;
try{
do {
bool readSuccess;
do{
cout << ps << " ";
std::string* line=nullptr;
struct Params p;
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{
line = read_line();
readSuccess = true;
line = readLine();
}
catch (IsUndoingLineException){
cout << "\n";
readSuccess = false;
outputStream << "\n";
continue;
}
}while(!readSuccess);
p = split_line(line);
executeCmd(p);
} while (1);
//Try line conversion from string to argc and argv. stringToArgcArgv() throws a std::runtime_error
//exception if the string has unproper quotes.
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) {
exitCode=c.getCode();
exitCode=c.getCode();
}
//launch postSetup
if(shellPostSetup!=0) {
shellPostSetup(this);
}
//Launch postSetup().
if(shellPostSetup!=0) shellPostSetup(this);
notLoop = false;
return exitCode;
}
int Shell::launchCmd(const struct Params& p)
{
using std::exit;
int Shell::launchCmd(const struct Params& p){
int status;
pid_t pid = fork();
@ -84,21 +112,23 @@ namespace mshconsole {
//execute threadCommand
int a;
try {
a=cmds.launch(p, true);
std::exit(EXIT_SUCCESS);
}
catch (CommandNotFoundException){
//execute bash command or program
if((a=execvp(p.argv[0], p.argv)) == -1) {
cerr << name <<": command " << p.argv[0] << " not found\n";
}
exit(EXIT_FAILURE);
catch (CommandNotFoundException){}
//execute bash command or program
if((a=execvp(p.argv[0], p.argv)) == -1) {
outputStream << name << ": execution failed. Errno is set to: "<<strerror(errno)<<"\n";
std::exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
else std::exit(EXIT_SUCCESS);
}
else if (pid < 0) {
// Error forking
cerr << name <<": error forking the process\n";
errorStream << name <<": error in process forking.\n";
}
else {
// Parent process
@ -110,34 +140,32 @@ namespace mshconsole {
return 1;
}
int Shell::executeCmd(const struct Params& p)
{
if (!p.argc) { //empty line
return 1;
}
int ret;
int Shell::executeCmd(const struct Params& p){
if (!p.argc) return 1; //Return if the line is empty.
try {
ret = cmds.launch(p, false);
//Search in non-thread commands.
return cmds.launch(p, false);
}
catch(CommandNotFoundException) {
ret = launchCmd(p);
}
return ret;
catch(CommandNotFoundException) {}
//Execute the threadCommand or the executable.
return launchCmd(p);
}
inline int Shell::executeCmd(const std::string &args){
return executeCmd(split_line(&args));
return executeCmd(splitLine(&args));
}
void Shell::EofHandler(int){
undoingLine = true;
void Shell::SIGINTHandler(int,siginfo_t*, void *){
SIGINTRaised = true;
}
void Shell::setShellSetup(void (*s)(Shell *)){
shellSetup=s;
}
void (*Shell::getShellSetup())(Shell*) {
void (*Shell::getShellSetup())(Shell*) const{
return shellSetup;
}
@ -145,40 +173,46 @@ namespace mshconsole {
shellPostSetup=s;
}
void (*Shell::getShellPostSetup())(Shell*) {
void (*Shell::getShellPostSetup())(Shell*) const{
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;
struct sigaction* setHandler(unsigned signal, void (*funcptr)(int, siginfo_t*, void*)){
struct sigaction* before = new struct sigaction();
struct sigaction sa;
sa.sa_sigaction = funcptr;
sa.sa_flags = 0;
sigaction(signal, &sa, before);
}
string* Shell::read_line()
{
string* buffer = new string();
setEofHandler(EofHandler);
getline(cin,*buffer); // get command
cin.clear(); // clear flags
void restoreHandler(unsigned signal, struct sigaction* before){
sigaction(signal, before, NULL);
delete before;
}
if(undoingLine){
undoingLine=false;
std::string* Shell::readLine(){
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();
}
restoreHandler(SIGINT, beforeSIGINT);
return buffer;
}
struct Params Shell::split_line(const string* line){
struct Params Shell::splitLine(const std::string* line){
struct Params p;
stringToArgcArgv(*line, &(p.argc), &(p.argv));
return p;
}
bool Shell::undoingLine = false;
bool Shell::SIGINTRaised = false;
void Shell::addCmd(Command* cmd, bool 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
#define SHELL_H
#pragma once
@ -7,20 +31,14 @@
#include <unistd.h>
#include <string>
#include <vector>
#include <sstream>
#include <cstddef>
#include <csignal>
#include <cerrno>
#include "command.h"
#include <wordexp.h>
#include "commandexecutor.h"
#include "stringtoargcargv.h"
using std::string;
using std::cin;
using std::cout;
using std::cerr;
using std::string;
using std::vector;
using std::istringstream;
using namespace StringToArgcArgv;
namespace mshconsole {
@ -29,8 +47,8 @@ namespace mshconsole {
class Commands
{
Shell* parent;
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*> commands; //commands that work the same thread of the shell
std::vector<Command*> threadCommands; //commands that work on a different thread
public:
Commands(Shell*);
@ -39,40 +57,47 @@ namespace mshconsole {
int launch(const struct Params& args, bool launchThread=false);
};
static bool undoingLine;
static bool SIGINTRaised;
Commands cmds;
string ps;
string name;
std::string ps;
std::string name;
std::ostream& errorStream;
std::ostream& outputStream;
std::istream& inputStream;
void (*shellSetup)(Shell *);
void (*shellPostSetup)(Shell *);
bool notLoop;
int launchCmd(const struct Params& args);
static void EofHandler(int);
string* read_line();
struct Params split_line(const string* line);
static void SIGINTHandler(int,siginfo_t *info, void *);
std::string* readLine();
struct Params splitLine(const std::string* line);
class IsUndoingLineException {};
class CommandNotFoundException {};
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);
Shell(std::string n, std::string ps, std::istream& inputStream, std::ostream& outputStream,
std::ostream& errorStream, void (*s)(Shell*)=0, void (*pss)(Shell*)=0);
const std::string& getPs() const;
void setPs(const std::string &value);
const std::string& getName() const;
void setName(const std::string &value);
int launch();
void setShellSetup(void (*)(Shell *));
void (*getShellSetup())(Shell*);
void (*getShellSetup())(Shell*) const;
void setShellPostSetup(void (*)(Shell *));
void (*getShellPostSetup())(Shell*);
void (*getShellPostSetup())(Shell*) const;
//for in-shell commands
void addCmd(Command *cmd, bool isThread=false);
size_t howManyCmds() const;
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

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
@ -22,171 +22,176 @@
*/
///The code is modified for the null-termination of argv
#include "stringtoargcargv.h"
/*
* Usage:
* int argc;
* char** argv;
* stringToArgcArgv("foo bar", &argc, &argv);
*/
void stringToArgcArgv(const std::string& str, int* argc, char*** argv)
{
std::vector<std::string> args = parse(str);
namespace StringToArgcArgv{
/*
* Usage:
* int argc;
* char** argv;
* stringToArgcArgv("foo bar", &argc, &argv);
*/
void stringToArgcArgv(const std::string& str, int* argc, char*** argv)
{
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;
for(std::vector<std::string>::iterator it = args.begin();
it != args.end();
++it, ++i)
{
std::string arg = *it;
(*argv)[i] = (char*)std::malloc((arg.length()+1) * sizeof(char));
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;
int i=0;
for(std::vector<std::string>::iterator it = args.begin();
it != args.end();
++it, ++i)
{
std::string arg = *it;
(*argv)[i] = (char*)std::malloc((arg.length()+1) * sizeof(char));
std::strcpy((*argv)[i], arg.c_str());
}
(*argv)[i]=NULL;
*argc = args.size();
}
else if(_isWhitespace(c)) {
switch(currentState) {
case InArg:
oargs.push_back(currentArg.str());
currentState = OutOfArg;
break;
case InArgQuote:
currentArg << c;
break;
case OutOfArg:
// nothing
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
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;
}
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
}
else if(_isWhitespace(c)) {
switch(currentState) {
case InArg:
oargs.push_back(currentArg.str());
currentState = OutOfArg;
break;
case InArgQuote:
currentArg << c;
break;
case OutOfArg:
// nothing
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:
currentArg.str(std::string());
currentArg << c;
currentState = InArg;
break;
}
bool _isQuote(char c)
{
if(c == '\"')
return true;
else if(c == '\'')
return true;
return false;
}
}
if(currentState == InArg)
oargs.push_back(currentArg.str());
else if(currentState == InArgQuote)
throw(std::runtime_error("Starting quote has no ending quote."));
bool _isEscape(char c)
{
if(c == '\\')
return true;
return oargs;
}
bool _isQuote(char c)
{
if(c == '\"')
return true;
else if(c == '\'')
return true;
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;
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
#define STRINGTOARGCARGV_H
#pragma once
/* stringtoargcargv.cpp -- Parsing a string to std::vector<string>
/** stringtoargcargv.cpp -- Parsing a string to std::vector<string>
Copyright (C) 2011 Bernhard Eder
@ -26,6 +22,10 @@
*/
#ifndef STRINGTOARGCARGV_H
#define STRINGTOARGCARGV_H
#pragma once
#include <iostream>
#include <sstream>
#include <stdexcept>
@ -35,11 +35,12 @@
#include <cstdlib>
#include <cstring>
bool _isQuote(char c);
bool _isEscape(char c);
bool _isEscape(char c);
bool _isWhitespace(char c);
std::vector<std::string> parse(const std::string& args);
void stringToArgcArgv(const std::string& str, int* argc, char*** argv);
namespace StringToArgcArgv{
bool _isQuote(char c);
bool _isEscape(char c);
bool _isEscape(char c);
bool _isWhitespace(char c);
std::vector<std::string> parse(const std::string& args);
void stringToArgcArgv(const std::string& str, int* argc, char*** argv);
}
#endif //STRINGTOARGCARGV_H