Added pipe support for threaded commands via the pipe() function. README updated.
This commit is contained in:
parent
40f2b2ee6b
commit
025e95c020
10 changed files with 174 additions and 67 deletions
|
@ -3,10 +3,10 @@ Library that provides a bash-like command line for C++ programs. Features includ
|
||||||
* included flag handling implemented with popt.h;
|
* included flag handling implemented with popt.h;
|
||||||
* creation of personalized commands in the same thread of the shell or in other threads;
|
* 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().
|
* basic shell functionalities, such as execution of other programs in the system. This is implemented with execvp().
|
||||||
|
* piping, implemented with <unistd.h> pipe() function. Code based on http://stackoverflow.com/a/5207730.
|
||||||
|
|
||||||
What is not actually included:
|
What is not actually included:
|
||||||
* advanced shell features, like:
|
* advanced shell features, like:
|
||||||
* piping,
|
|
||||||
* redirection,
|
* redirection,
|
||||||
* scripting,
|
* scripting,
|
||||||
* command history.
|
* command history.
|
||||||
|
|
|
@ -34,5 +34,6 @@ if(${lib})
|
||||||
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)")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
|
||||||
add_executable(${PROJECT_NAME} ${SRC_LIST})
|
add_executable(${PROJECT_NAME} ${SRC_LIST})
|
||||||
endif(${lib})
|
endif(${lib})
|
||||||
|
|
|
@ -43,8 +43,6 @@ namespace mshconsole {
|
||||||
int Command::execute(const struct Params& p, CommandExecutor* c, std::ostream& errorStream){
|
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 f and g take no arguments
|
|
||||||
poptContext pc;
|
poptContext pc;
|
||||||
opts.push_back(POPT_TERMINATOR);
|
opts.push_back(POPT_TERMINATOR);
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ namespace mshconsole{
|
||||||
ExitException(int c=0) : code(c){}
|
ExitException(int c=0) : code(c){}
|
||||||
};
|
};
|
||||||
public:
|
public:
|
||||||
virtual int executeCmd(const struct Params& args) = 0;
|
virtual int executeCmd(const struct Params& args, bool isPipe=false) = 0;
|
||||||
virtual int executeCmd(const std::string& args) = 0;
|
virtual int executeCmd(const std::string& args) = 0;
|
||||||
virtual size_t howManyCmds() const = 0;
|
virtual size_t howManyCmds() const = 0;
|
||||||
void exit(int code=0);
|
void exit(int code=0);
|
||||||
|
|
|
@ -46,6 +46,14 @@ namespace mshconsole {
|
||||||
}
|
}
|
||||||
throw CommandNotFoundException();
|
throw CommandNotFoundException();
|
||||||
}
|
}
|
||||||
|
bool Shell::Commands::found(const std::string& s, bool launchThread){
|
||||||
|
for(unsigned int i=0; i<(launchThread ? threadCommands.size() : commands.size()); i++){
|
||||||
|
if((launchThread ? threadCommands[i]->getName() : commands[i]->getName())==s){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ int main(int argc, char **argv)
|
||||||
Command cd("cd", &cdExecute);
|
Command cd("cd", &cdExecute);
|
||||||
|
|
||||||
//add builtin commands
|
//add builtin commands
|
||||||
mshConsoleTest.addCmd(&help);
|
mshConsoleTest.addCmd(&help, true);
|
||||||
mshConsoleTest.addCmd(&cd);
|
mshConsoleTest.addCmd(&cd);
|
||||||
mshConsoleTest.addCmd(&exit);
|
mshConsoleTest.addCmd(&exit);
|
||||||
mshConsoleTest.launch();
|
mshConsoleTest.launch();
|
||||||
|
|
|
@ -61,7 +61,7 @@ namespace mshconsole {
|
||||||
//Run the shell loop.
|
//Run the shell loop.
|
||||||
int exitCode;
|
int exitCode;
|
||||||
std::string* line=nullptr;
|
std::string* line=nullptr;
|
||||||
struct Params p;
|
std::vector<std::string> p;
|
||||||
|
|
||||||
try{
|
try{
|
||||||
while(1){
|
while(1){
|
||||||
|
@ -88,15 +88,11 @@ namespace mshconsole {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Execute the command.
|
//Execute the pipe.
|
||||||
executeCmd(p);
|
executePipe(p);
|
||||||
|
|
||||||
//Free p
|
|
||||||
deleteParams(p);
|
|
||||||
};
|
};
|
||||||
} catch(CommandExecutor::ExitException c) {
|
} catch(CommandExecutor::ExitException c) {
|
||||||
delete line;
|
delete line;
|
||||||
deleteParams(p);
|
|
||||||
exitCode=c.getCode();
|
exitCode=c.getCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,6 +103,103 @@ namespace mshconsole {
|
||||||
return exitCode;
|
return exitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Shell::executePipe(std::vector<std::string>& argv)
|
||||||
|
{
|
||||||
|
int newPipe[2], oldPipe[2], status;
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
std::vector<std::vector<std::string>> argvs;
|
||||||
|
|
||||||
|
size_t c=0, i=0;
|
||||||
|
for(; i<argv.size(); i++){
|
||||||
|
if(argv[i]=="|"){ //Pipe reached
|
||||||
|
if(c!=i){
|
||||||
|
//Write the command in the pipe std::vector
|
||||||
|
argvs.push_back(std::vector<std::string>(argv.begin()+c, argv.begin()+i));
|
||||||
|
}
|
||||||
|
c=i+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(c!=i){
|
||||||
|
//Write the last command in the pipe std::vector
|
||||||
|
argvs.push_back(std::vector<std::string>(argv.begin()+c, argv.end()));
|
||||||
|
}
|
||||||
|
if(argvs.size()==1){
|
||||||
|
struct Params p;
|
||||||
|
vectorToArgcArgv(argvs[0], &p.argc, &p.argv);
|
||||||
|
return executeCmd(p,false);
|
||||||
|
}
|
||||||
|
for(size_t i=0; i<argvs.size(); i++){
|
||||||
|
if(cmds.found(std::string(argvs[i][0]),false)){
|
||||||
|
errorStream << name << ": non-thread command " << argv[0] << " can't be launched from a pipe" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(size_t i=0; i<argvs.size(); i++) /* For each command */
|
||||||
|
{
|
||||||
|
struct Params p;
|
||||||
|
vectorToArgcArgv(argvs[i], &p.argc, &p.argv);
|
||||||
|
|
||||||
|
/* If there still are commands to be executed */
|
||||||
|
if(i < argvs.size()-1)
|
||||||
|
{
|
||||||
|
pipe(newPipe); /* just create a pipe */
|
||||||
|
}
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
|
||||||
|
if(pid == 0) /* Child */
|
||||||
|
{
|
||||||
|
/* If there is a previous command */
|
||||||
|
if(i > 0)
|
||||||
|
{
|
||||||
|
close(oldPipe[1]);
|
||||||
|
dup2(oldPipe[0], 0); //redirect stdin to input of the pipe
|
||||||
|
close(oldPipe[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If there still are commands to be executed */
|
||||||
|
if(i < argvs.size()-1)
|
||||||
|
{
|
||||||
|
close(newPipe[0]);
|
||||||
|
dup2(newPipe[1], 1); //redirect stdout to output of the pipe
|
||||||
|
close(newPipe[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Execute
|
||||||
|
exit(executeCmd(p,true));
|
||||||
|
}
|
||||||
|
else if (pid < 0) {
|
||||||
|
// Error forking
|
||||||
|
errorStream << name <<": error in process forking.\n";
|
||||||
|
}
|
||||||
|
else /* Father */
|
||||||
|
{
|
||||||
|
/* If there is a previous command */
|
||||||
|
if(i > 0)
|
||||||
|
{
|
||||||
|
close(oldPipe[0]);
|
||||||
|
close(oldPipe[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* do we have a next command? */
|
||||||
|
if(i < argvs.size()-1)
|
||||||
|
{
|
||||||
|
oldPipe[0] = newPipe[0];
|
||||||
|
oldPipe[1] = newPipe[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* wait for last command process? */
|
||||||
|
if(i == argvs.size()-1)
|
||||||
|
{
|
||||||
|
waitpid(pid, &status, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deleteParams(p.argc,p.argv);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int Shell::launchCmd(const struct Params& p){
|
int Shell::launchCmd(const struct Params& p){
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
|
@ -146,21 +239,29 @@ namespace mshconsole {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::executeCmd(const struct Params& p){
|
int Shell::executeCmd(const struct Params& p, bool isPipe){
|
||||||
if (!p.argc) return 1; //Return if the line is empty.
|
if (!p.argc) return 1; //Return if the line is empty.
|
||||||
|
|
||||||
try {
|
if(isPipe){
|
||||||
//Search in non-thread commands.
|
if(cmds.found(p.argv[0],false)){
|
||||||
return cmds.launch(p, false);
|
errorStream << name << ": non-thread command " << p.argv[0] << " can't be launched from a pipe" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
try {
|
||||||
|
//Search in non-thread commands.
|
||||||
|
return cmds.launch(p, false);
|
||||||
|
}
|
||||||
|
catch(CommandNotFoundException) {}
|
||||||
}
|
}
|
||||||
catch(CommandNotFoundException) {}
|
|
||||||
|
|
||||||
//Execute the threadCommand or the executable.
|
//Execute the threadCommand or the executable.
|
||||||
return launchCmd(p);
|
return launchCmd(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int Shell::executeCmd(const std::string &args){
|
inline int Shell::executeCmd(const std::string &args){
|
||||||
return executeCmd(splitLine(&args));
|
std::vector<std::string>a =splitLine(&args);
|
||||||
|
return executePipe(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shell::SIGINTHandler(int,siginfo_t*, void *){
|
void Shell::SIGINTHandler(int,siginfo_t*, void *){
|
||||||
|
@ -217,10 +318,8 @@ namespace mshconsole {
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Params Shell::splitLine(const std::string* line){
|
std::vector<std::string> Shell::splitLine(const std::string* line){
|
||||||
struct Params p;
|
return parseStringToVector(*line);
|
||||||
stringToArgcArgv(*line, &(p.argc), &(p.argv));
|
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shell::deleteParams(struct Params p){
|
void Shell::deleteParams(struct Params p){
|
||||||
|
|
|
@ -55,6 +55,7 @@ namespace mshconsole {
|
||||||
void add(Command*, bool isthread=false);
|
void add(Command*, bool isthread=false);
|
||||||
size_t howMany() const;
|
size_t howMany() const;
|
||||||
int launch(const struct Params& args, bool launchThread=false);
|
int launch(const struct Params& args, bool launchThread=false);
|
||||||
|
bool found(const std::string& args, bool launchThread=false);
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool SIGINTRaised;
|
static bool SIGINTRaised;
|
||||||
|
@ -70,7 +71,8 @@ namespace mshconsole {
|
||||||
int launchCmd(const struct Params& args);
|
int launchCmd(const struct Params& args);
|
||||||
static void SIGINTHandler(int,siginfo_t *info, void *);
|
static void SIGINTHandler(int,siginfo_t *info, void *);
|
||||||
std::string* readLine();
|
std::string* readLine();
|
||||||
struct Params splitLine(const std::string* line);
|
std::vector<std::string> splitLine(const std::string* line);
|
||||||
|
int executePipe(std::vector<std::string>& argv);
|
||||||
void deleteParams(struct Params p);
|
void deleteParams(struct Params p);
|
||||||
|
|
||||||
class IsUndoingLineException {};
|
class IsUndoingLineException {};
|
||||||
|
@ -92,7 +94,7 @@ namespace mshconsole {
|
||||||
//for in-shell commands
|
//for in-shell commands
|
||||||
void addCmd(Command *cmd, bool isThread=false);
|
void addCmd(Command *cmd, bool isThread=false);
|
||||||
|
|
||||||
int executeCmd(const struct Params& args);
|
int executeCmd(const struct Params& args, bool isPipe=false);
|
||||||
int executeCmd(const std::string& args);
|
int executeCmd(const std::string& args);
|
||||||
size_t howManyCmds() const;
|
size_t howManyCmds() const;
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,30 +27,28 @@
|
||||||
#include "stringtoargcargv.h"
|
#include "stringtoargcargv.h"
|
||||||
|
|
||||||
namespace StringToArgcArgv{
|
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 = new char*[args.size()+1]();
|
static inline bool _isQuote(char c);
|
||||||
|
static inline bool _isEscape(char c);
|
||||||
|
static inline bool _isPipe(char c);
|
||||||
|
static inline bool _isWhitespace(char c);
|
||||||
|
|
||||||
int i=0;
|
void vectorToArgcArgv(const std::vector<std::string>& args, int* argc, char*** argv){
|
||||||
for(std::vector<std::string>::iterator it = args.begin();
|
*argv = new char*[args.size()+1]();
|
||||||
|
int i=0;
|
||||||
|
for(std::vector<std::string>::const_iterator it = args.begin();
|
||||||
it != args.end();
|
it != args.end();
|
||||||
++it, ++i)
|
++it, ++i){
|
||||||
{
|
std::string arg = *it;
|
||||||
std::string arg = *it;
|
(*argv)[i] = new char[arg.length()+1]();
|
||||||
(*argv)[i] = new char[arg.length()+1]();
|
std::strcpy((*argv)[i], arg.c_str());
|
||||||
std::strcpy((*argv)[i], arg.c_str());
|
}
|
||||||
}
|
(*argv)[i]=NULL;
|
||||||
(*argv)[i]=NULL;
|
*argc = args.size();
|
||||||
|
}
|
||||||
|
|
||||||
*argc = args.size();
|
void stringToArgcArgv(const std::string& str, int* argc, char*** argv){
|
||||||
|
return vectorToArgcArgv(parseStringToVector(str),argc,argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void freeString(int& argc, char**& argv){
|
void freeString(int& argc, char**& argv){
|
||||||
|
@ -60,7 +58,7 @@ namespace StringToArgcArgv{
|
||||||
delete[] argv;
|
delete[] argv;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> parse(const std::string& args)
|
std::vector<std::string> parseStringToVector(const std::string& args)
|
||||||
{
|
{
|
||||||
std::stringstream ain(args); // used to iterate over input string
|
std::stringstream ain(args); // used to iterate over input string
|
||||||
ain >> std::noskipws; // do not skip white spaces
|
ain >> std::noskipws; // do not skip white spaces
|
||||||
|
@ -101,19 +99,21 @@ namespace StringToArgcArgv{
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else if(_isWhitespace(c)) {
|
else if(_isWhitespace(c) || _isPipe(c)) {
|
||||||
switch(currentState) {
|
switch(currentState) {
|
||||||
case InArg:
|
case InArg:
|
||||||
oargs.push_back(currentArg.str());
|
oargs.push_back(currentArg.str());
|
||||||
currentState = OutOfArg;
|
if(_isPipe(c)) oargs.push_back("|");
|
||||||
break;
|
currentState = OutOfArg;
|
||||||
|
break;
|
||||||
case InArgQuote:
|
case InArgQuote:
|
||||||
currentArg << c;
|
currentArg << c;
|
||||||
break;
|
break;
|
||||||
case OutOfArg:
|
case OutOfArg:
|
||||||
// nothing
|
if(_isPipe(c)) oargs.push_back("|");
|
||||||
break;
|
// nothing
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if(_isEscape(c)) {
|
else if(_isEscape(c)) {
|
||||||
switch(currentState) {
|
switch(currentState) {
|
||||||
|
@ -174,8 +174,7 @@ namespace StringToArgcArgv{
|
||||||
return oargs;
|
return oargs;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isQuote(char c)
|
static inline bool _isQuote(char c){
|
||||||
{
|
|
||||||
if(c == '\"')
|
if(c == '\"')
|
||||||
return true;
|
return true;
|
||||||
else if(c == '\'')
|
else if(c == '\'')
|
||||||
|
@ -184,16 +183,14 @@ namespace StringToArgcArgv{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isEscape(char c)
|
static inline bool _isEscape(char c){
|
||||||
{
|
|
||||||
if(c == '\\')
|
if(c == '\\')
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isWhitespace(char c)
|
static inline bool _isWhitespace(char c){
|
||||||
{
|
|
||||||
if(c == ' ')
|
if(c == ' ')
|
||||||
return true;
|
return true;
|
||||||
else if(c == '\t')
|
else if(c == '\t')
|
||||||
|
@ -201,4 +198,9 @@ namespace StringToArgcArgv{
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool _isPipe(char c){
|
||||||
|
if(c == '|') return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,11 +36,8 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
namespace StringToArgcArgv{
|
namespace StringToArgcArgv{
|
||||||
bool _isQuote(char c);
|
void vectorToArgcArgv(const std::vector<std::string>& args, int* argc, char*** argv);
|
||||||
bool _isEscape(char c);
|
std::vector<std::string> parseStringToVector(const std::string& args);
|
||||||
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);
|
void stringToArgcArgv(const std::string& str, int* argc, char*** argv);
|
||||||
void freeString(int& argc, char**& argv);
|
void freeString(int& argc, char**& argv);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue