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;
|
||||
* 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().
|
||||
* piping, implemented with <unistd.h> pipe() function. Code based on http://stackoverflow.com/a/5207730.
|
||||
|
||||
What is not actually included:
|
||||
* advanced shell features, like:
|
||||
* piping,
|
||||
* redirection,
|
||||
* scripting,
|
||||
* command history.
|
||||
|
|
|
@ -34,5 +34,6 @@ if(${lib})
|
|||
add_library (mshconsole SHARED ${SRC_LIST})
|
||||
else(${lib})
|
||||
MESSAGE(STATUS "Building exec (debug only)")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
|
||||
add_executable(${PROJECT_NAME} ${SRC_LIST})
|
||||
endif(${lib})
|
||||
|
|
|
@ -43,8 +43,6 @@ namespace mshconsole {
|
|||
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
|
||||
// options f and g take no arguments
|
||||
poptContext pc;
|
||||
opts.push_back(POPT_TERMINATOR);
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace mshconsole{
|
|||
ExitException(int c=0) : code(c){}
|
||||
};
|
||||
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 size_t howManyCmds() const = 0;
|
||||
void exit(int code=0);
|
||||
|
|
|
@ -46,6 +46,14 @@ namespace mshconsole {
|
|||
}
|
||||
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);
|
||||
|
||||
//add builtin commands
|
||||
mshConsoleTest.addCmd(&help);
|
||||
mshConsoleTest.addCmd(&help, true);
|
||||
mshConsoleTest.addCmd(&cd);
|
||||
mshConsoleTest.addCmd(&exit);
|
||||
mshConsoleTest.launch();
|
||||
|
|
|
@ -61,7 +61,7 @@ namespace mshconsole {
|
|||
//Run the shell loop.
|
||||
int exitCode;
|
||||
std::string* line=nullptr;
|
||||
struct Params p;
|
||||
std::vector<std::string> p;
|
||||
|
||||
try{
|
||||
while(1){
|
||||
|
@ -88,15 +88,11 @@ namespace mshconsole {
|
|||
continue;
|
||||
}
|
||||
|
||||
//Execute the command.
|
||||
executeCmd(p);
|
||||
|
||||
//Free p
|
||||
deleteParams(p);
|
||||
//Execute the pipe.
|
||||
executePipe(p);
|
||||
};
|
||||
} catch(CommandExecutor::ExitException c) {
|
||||
delete line;
|
||||
deleteParams(p);
|
||||
exitCode=c.getCode();
|
||||
}
|
||||
|
||||
|
@ -107,6 +103,103 @@ namespace mshconsole {
|
|||
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 status;
|
||||
|
||||
|
@ -146,21 +239,29 @@ namespace mshconsole {
|
|||
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(isPipe){
|
||||
if(cmds.found(p.argv[0],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) {}
|
||||
|
||||
}
|
||||
//Execute the threadCommand or the executable.
|
||||
return launchCmd(p);
|
||||
}
|
||||
|
||||
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 *){
|
||||
|
@ -217,10 +318,8 @@ namespace mshconsole {
|
|||
return buffer;
|
||||
}
|
||||
|
||||
struct Params Shell::splitLine(const std::string* line){
|
||||
struct Params p;
|
||||
stringToArgcArgv(*line, &(p.argc), &(p.argv));
|
||||
return p;
|
||||
std::vector<std::string> Shell::splitLine(const std::string* line){
|
||||
return parseStringToVector(*line);
|
||||
}
|
||||
|
||||
void Shell::deleteParams(struct Params p){
|
||||
|
|
|
@ -55,6 +55,7 @@ namespace mshconsole {
|
|||
void add(Command*, bool isthread=false);
|
||||
size_t howMany() const;
|
||||
int launch(const struct Params& args, bool launchThread=false);
|
||||
bool found(const std::string& args, bool launchThread=false);
|
||||
};
|
||||
|
||||
static bool SIGINTRaised;
|
||||
|
@ -70,7 +71,8 @@ namespace mshconsole {
|
|||
int launchCmd(const struct Params& args);
|
||||
static void SIGINTHandler(int,siginfo_t *info, void *);
|
||||
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);
|
||||
|
||||
class IsUndoingLineException {};
|
||||
|
@ -92,7 +94,7 @@ namespace mshconsole {
|
|||
//for in-shell commands
|
||||
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);
|
||||
size_t howManyCmds() const;
|
||||
};
|
||||
|
|
|
@ -27,32 +27,30 @@
|
|||
#include "stringtoargcargv.h"
|
||||
|
||||
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);
|
||||
|
||||
static inline bool _isQuote(char c);
|
||||
static inline bool _isEscape(char c);
|
||||
static inline bool _isPipe(char c);
|
||||
static inline bool _isWhitespace(char c);
|
||||
|
||||
void vectorToArgcArgv(const std::vector<std::string>& args, int* argc, char*** argv){
|
||||
*argv = new char*[args.size()+1]();
|
||||
|
||||
int i=0;
|
||||
for(std::vector<std::string>::iterator it = args.begin();
|
||||
for(std::vector<std::string>::const_iterator it = args.begin();
|
||||
it != args.end();
|
||||
++it, ++i)
|
||||
{
|
||||
++it, ++i){
|
||||
std::string arg = *it;
|
||||
(*argv)[i] = new char[arg.length()+1]();
|
||||
std::strcpy((*argv)[i], arg.c_str());
|
||||
}
|
||||
(*argv)[i]=NULL;
|
||||
|
||||
*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){
|
||||
for(int i=0; i<argc; i++){
|
||||
delete[] argv[i];
|
||||
|
@ -60,7 +58,7 @@ namespace StringToArgcArgv{
|
|||
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
|
||||
ain >> std::noskipws; // do not skip white spaces
|
||||
|
@ -101,16 +99,18 @@ namespace StringToArgcArgv{
|
|||
}
|
||||
|
||||
}
|
||||
else if(_isWhitespace(c)) {
|
||||
else if(_isWhitespace(c) || _isPipe(c)) {
|
||||
switch(currentState) {
|
||||
case InArg:
|
||||
oargs.push_back(currentArg.str());
|
||||
if(_isPipe(c)) oargs.push_back("|");
|
||||
currentState = OutOfArg;
|
||||
break;
|
||||
case InArgQuote:
|
||||
currentArg << c;
|
||||
break;
|
||||
case OutOfArg:
|
||||
if(_isPipe(c)) oargs.push_back("|");
|
||||
// nothing
|
||||
break;
|
||||
}
|
||||
|
@ -174,8 +174,7 @@ namespace StringToArgcArgv{
|
|||
return oargs;
|
||||
}
|
||||
|
||||
bool _isQuote(char c)
|
||||
{
|
||||
static inline bool _isQuote(char c){
|
||||
if(c == '\"')
|
||||
return true;
|
||||
else if(c == '\'')
|
||||
|
@ -184,16 +183,14 @@ namespace StringToArgcArgv{
|
|||
return false;
|
||||
}
|
||||
|
||||
bool _isEscape(char c)
|
||||
{
|
||||
static inline bool _isEscape(char c){
|
||||
if(c == '\\')
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _isWhitespace(char c)
|
||||
{
|
||||
static inline bool _isWhitespace(char c){
|
||||
if(c == ' ')
|
||||
return true;
|
||||
else if(c == '\t')
|
||||
|
@ -201,4 +198,9 @@ namespace StringToArgcArgv{
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool _isPipe(char c){
|
||||
if(c == '|') return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,11 +36,8 @@
|
|||
#include <cstring>
|
||||
|
||||
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 vectorToArgcArgv(const std::vector<std::string>& args, int* argc, char*** argv);
|
||||
std::vector<std::string> parseStringToVector(const std::string& args);
|
||||
void stringToArgcArgv(const std::string& str, int* argc, char*** argv);
|
||||
void freeString(int& argc, char**& argv);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue