Options handling rewritten with usage of popt.h. fixed #1 with stringtoargcargv.cpp. The command arguments are now passed as struct mshconsole::Params. Readme
updated in oreder to mention stringtoargcargv.cpp.
This commit is contained in:
parent
709a2e6c1c
commit
f6c3135c27
14 changed files with 425 additions and 272 deletions
|
@ -3,11 +3,6 @@ Library that provides a bash-like interface for CLI C++ programs
|
||||||
### 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" - 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 library
|
### The library
|
||||||
The library can be compiled as shared library with the CMakeLists.txt file already in msh-console-library/, or it can be compiled with these commands:
|
The library can be compiled as shared library with the CMakeLists.txt file already in msh-console-library/.
|
||||||
|
|
||||||
```
|
|
||||||
cd msh-console-library
|
|
||||||
g++ -Wall -fPIC -std=c++11 -c command.cpp commands.cpp shell.cpp commandexecutor.cpp
|
|
||||||
g++ -shared -Wl,-soname,libmshconsole.so.1.0.0 -o libmshconsole.so.1.0.0 commandexecutor.o command.o commands.o shell.o
|
|
||||||
```
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
project(msh-console-library)
|
project(msh-console-library)
|
||||||
cmake_minimum_required(VERSION 2.8)
|
cmake_minimum_required(VERSION 2.8)
|
||||||
|
|
||||||
SET(CMAKE_CXX_FLAGS "-std=c++11")
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -lpopt")
|
||||||
FILE(GLOB sources *.cpp)
|
FILE(GLOB sources *.cpp)
|
||||||
FILE(GLOB headers *.h)
|
FILE(GLOB headers *.h)
|
||||||
|
|
||||||
|
|
|
@ -1,137 +1,77 @@
|
||||||
#include "shell.h"
|
#include "shell.h"
|
||||||
namespace mshconsole {
|
namespace mshconsole {
|
||||||
|
|
||||||
Command::Command(const string& n, int (*funcptr)(CommandExecutor* whoExecuted,const Datas& data, const vector<char*> argv)) :
|
Command::Command(const string& n, int (*funcptr)(CommandExecutor* whoExecuted,const Datas& data, const vector<const char*> argv)) :
|
||||||
name(n), funcCommand(funcptr){
|
name(n), funcCommand(funcptr), optionNum(0){
|
||||||
checkObj();
|
checkObj();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Command::addOption(Option *a){
|
void Command::addOption(const poptOption& a){
|
||||||
if(a->getOptionName().length()==1){
|
if(a.arg!=NULL&&a.arg!=nullptr&&a.arg!=0) throw InvalidOptionException{};
|
||||||
shortOpts.push_back(a);
|
for(int i=0; i<opts.size(); i++)
|
||||||
}
|
if(strcmp((opts[i].longName),(a.longName))==0||
|
||||||
else longOpts.push_back(a);
|
opts[i].shortName==a.shortName)
|
||||||
|
throw DuplicatedOptionException();
|
||||||
|
opts.push_back(a);
|
||||||
|
optionNum++;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Command::execute(const vector<string>* args, CommandExecutor* c){
|
int Command::execute(const struct Params& p, CommandExecutor* c){
|
||||||
if(args==nullptr) return -1;
|
if(p.argc<=0) return -1;
|
||||||
|
|
||||||
//filling longData
|
// options a, b, c take integer arguments
|
||||||
Datas longData;
|
// options f and g take no arguments
|
||||||
for(size_t i=0; i<longOpts.size(); i++){
|
poptContext pc;
|
||||||
longData.push_back(new Data(longOpts[i]));
|
opts.push_back(POPT_TERMINATOR);
|
||||||
|
|
||||||
|
Datas datas;
|
||||||
|
for(int i=0; i<optionNum; i++){
|
||||||
|
Data* tmp = new Data(&opts[i]);
|
||||||
|
opts[i].arg=&(tmp->d);
|
||||||
|
datas.push_back(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
//filling string for getopt and shortData
|
// pc is the context for all popt-related functions
|
||||||
string getoptarg;
|
pc = poptGetContext(NULL, p.argc, const_cast<const char**>(p.argv), opts.data(), 0);
|
||||||
Datas shortData;
|
poptSetOtherOptionHelp(pc, "[ARG...]");
|
||||||
for(size_t i=0; i<shortOpts.size(); i++){
|
if (p.argc < 2 && opts.size()==0) {
|
||||||
shortData.push_back(new Data(shortOpts[i]));
|
poptPrintUsage(pc, stderr, 0);
|
||||||
getoptarg.append(1,shortOpts[i]->getOptionName()[0]);
|
return 1;
|
||||||
}
|
|
||||||
getoptarg+=" -:";
|
|
||||||
|
|
||||||
//creating argv as char** for getopt compatibility
|
|
||||||
vector<char *> argv(args->size());
|
|
||||||
size_t i;
|
|
||||||
for (i = 0; i < args->size(); ++i)
|
|
||||||
{
|
|
||||||
argv[i] = const_cast<char*>(&(args->operator[](i)[0]));
|
|
||||||
}
|
|
||||||
int argc=i;
|
|
||||||
|
|
||||||
//TODO: quotes for long options
|
|
||||||
try{
|
|
||||||
int c;
|
|
||||||
opterr=0;
|
|
||||||
optind=1;
|
|
||||||
while ((c = getopt (argc, argv.data(), getoptarg.c_str())) != -1){
|
|
||||||
if((c>='a'&&c<='z')||(c>='A'&&c>='Z')){
|
|
||||||
for(size_t i=0; i<shortOpts.size(); i++){
|
|
||||||
if(c==shortOpts[i]->getOptionName()[0]){
|
|
||||||
shortData[i]->set(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(c=='-'){
|
|
||||||
string l(optarg);
|
|
||||||
bool getout = false;
|
|
||||||
try{
|
|
||||||
for(size_t i=0; i<longOpts.size(); i++){
|
|
||||||
if(l.find(longOpts[i]->getOptionName()+"=")==0){
|
|
||||||
if(!(longOpts[i]->hasFlag(Option::STRING))){
|
|
||||||
throw NotRightArgException();
|
|
||||||
}
|
|
||||||
longData[i]->set(l.substr(longOpts[i]->getOptionName().size()+1,l.size()));
|
|
||||||
getout = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(l==longOpts[i]->getOptionName()){
|
|
||||||
longData[i]->set(true);
|
|
||||||
getout=true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(Data::MultipleDefinitionException){
|
|
||||||
getout=false;
|
|
||||||
}
|
|
||||||
if(!getout) throw OptionNotParsedCorrectlyException(static_cast<string>("--") + optarg);
|
|
||||||
}
|
|
||||||
else if(c=='?'){
|
|
||||||
if (isprint (optopt))
|
|
||||||
fprintf (stderr, "Unknown option `-%c'.\n", optopt);
|
|
||||||
else
|
|
||||||
fprintf (stderr, "Unknown option character `\\x%x'.\n",optopt);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else //abort();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//demo of all options for debug (code to check)
|
|
||||||
/*
|
|
||||||
for(size_t i=0; i<longOpts.size(); i++){
|
|
||||||
cout << longOpts[i]->getOptionName() << ": ";
|
|
||||||
LongData* now = data[i];
|
|
||||||
if(now->getState()==LongData::States::STRING) cout << now->getLongData();
|
|
||||||
else cout << now->getBoolState();
|
|
||||||
cout << "\n";
|
|
||||||
}
|
|
||||||
for(size_t i=0; i<shortOpts.size(); i++){
|
|
||||||
cout << shortOptions[i] << ": " << sLongData[i] << "\n";
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
catch(OptionNotParsedCorrectlyException e){
|
|
||||||
cout << "Error parsing option " << e.getOptarg() << "\n";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
catch(NotRightArgException){
|
|
||||||
cout << "Error on options argument type\n";
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//making one big Data vector
|
// process options and handle each val returned
|
||||||
shortOpts.insert(
|
int val;
|
||||||
shortOpts.end(),
|
while ((val = poptGetNextOpt(pc)) >= 0);
|
||||||
std::make_move_iterator(longOpts.begin()),
|
|
||||||
std::make_move_iterator(longOpts.end())
|
|
||||||
);
|
|
||||||
|
|
||||||
//deleting options and command name from argv
|
// poptGetNextOpt returns -1 when the final argument has been parsed
|
||||||
argv.erase(argv.begin()); //argv[0] = command name
|
// otherwise an error occured
|
||||||
for(int i=0; i<argv.size(); i++){
|
if (val != -1) {
|
||||||
if(argv[0][0]=='-'){
|
cerr << name << ": ";
|
||||||
argv.erase(argv.begin()+i);
|
switch(val) {
|
||||||
i--;
|
case POPT_ERROR_NOARG:
|
||||||
|
cerr << "argument missing for an option\n";
|
||||||
|
return 1;
|
||||||
|
case POPT_ERROR_BADOPT:
|
||||||
|
cerr << "option not found\n";
|
||||||
|
return 1;
|
||||||
|
case POPT_ERROR_BADNUMBER:
|
||||||
|
case POPT_ERROR_OVERFLOW:
|
||||||
|
cerr << "option could not be converted to number\n";
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
cerr << "unknown error in option processing\n";
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (*funcCommand)(c,shortData, argv);
|
vector<const char*> nonOptionArgs;
|
||||||
|
while (poptPeekArg(pc) != NULL)
|
||||||
|
nonOptionArgs.push_back(const_cast<const char*>(poptGetArg(pc)));
|
||||||
|
|
||||||
|
return (*funcCommand)(c,datas,nonOptionArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
string Command::getName(){
|
const string& Command::getName(){
|
||||||
return this->name;
|
return this->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,5 +86,5 @@ namespace mshconsole {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const string Command::Data::EMPTY("");
|
const struct poptOption Command::POPT_TERMINATOR = {NULL,'\0',POPT_ARG_NONE,NULL,-1,NULL,NULL};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
#ifndef COMMAND_H
|
#ifndef COMMAND_H
|
||||||
#define COMMAND_H
|
#define COMMAND_H
|
||||||
|
#pragma once
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
//#include "shell.h"
|
#include <unistd.h>
|
||||||
#include "option.h"
|
extern "C"{
|
||||||
|
#include <popt.h>
|
||||||
|
}
|
||||||
#include "commandexecutor.h"
|
#include "commandexecutor.h"
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
|
@ -12,71 +15,67 @@ using std::vector;
|
||||||
namespace mshconsole{
|
namespace mshconsole{
|
||||||
class Command
|
class Command
|
||||||
{
|
{
|
||||||
class OptionNotParsedCorrectlyException {
|
|
||||||
string optarg;
|
|
||||||
public:
|
|
||||||
OptionNotParsedCorrectlyException(const string& s) : optarg(s) {}
|
|
||||||
string getOptarg(){
|
|
||||||
return optarg;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CommandNameNotValidException {};
|
|
||||||
class NotRightArgException {};
|
|
||||||
const string name;
|
const string name;
|
||||||
void checkObj();
|
void checkObj();
|
||||||
vector<Option*> longOpts;
|
vector<poptOption> opts;
|
||||||
vector<Option*> shortOpts;
|
static const struct poptOption POPT_TERMINATOR;
|
||||||
|
size_t optionNum;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class Data{
|
class Data{
|
||||||
public:
|
public:
|
||||||
enum States{
|
union Arg{
|
||||||
STRING=4,
|
int i;
|
||||||
TRUE=2,
|
char* s;
|
||||||
FALSE=1
|
long l;
|
||||||
|
float f;
|
||||||
|
double d;
|
||||||
|
};
|
||||||
|
enum Types{
|
||||||
|
STRING,
|
||||||
|
INT,
|
||||||
|
LONG,
|
||||||
|
FLOAT,
|
||||||
|
DOUBLE
|
||||||
};
|
};
|
||||||
class MultipleDefinitionException{};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
string sData;
|
union Arg d;
|
||||||
const Option* of;
|
poptOption* of;
|
||||||
enum States state;
|
Data(poptOption* o) : of(o), d() {}
|
||||||
static const string EMPTY;
|
friend class mshconsole::Command;
|
||||||
bool isSDataValorized;
|
|
||||||
Data(const Option* o) : sData(), state(Data::FALSE), isSDataValorized(false), of(o) {}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void set(bool b);
|
const union Arg& getArg() const;
|
||||||
void set(const string& s);
|
enum Types getType() const;
|
||||||
enum States getState() const;
|
int getInt() const;
|
||||||
const string& getData() const;
|
char* getString() const;
|
||||||
bool getBoolState() const;
|
long getLong() const;
|
||||||
friend class Command;
|
float getFloat() const;
|
||||||
friend class Datas;
|
double getDouble() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Datas : public vector<Data*>{
|
class Datas : public vector<Data*>{
|
||||||
public:
|
public:
|
||||||
Data* getOptData(const string& optionName) const;
|
Data* getOptData(const string& optionName) const;
|
||||||
|
Data* getOptData(char opt) const;
|
||||||
|
Data* operator[](const string& opt) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
Command(const string& n, int (*funcptr)(CommandExecutor* whoExecuted,const Datas& data, const vector<char*> argv));
|
class DuplicatedOptionException{};
|
||||||
|
class CommandNameNotValidException{};
|
||||||
|
class InvalidOptionException{};
|
||||||
|
|
||||||
|
Command(const string& n, int (*funcptr)(CommandExecutor* whoExecuted,const Datas& data, const vector<const char*> argv));
|
||||||
Command(const Command&);
|
Command(const Command&);
|
||||||
~Command();
|
~Command();
|
||||||
string getName();
|
const string& getName();
|
||||||
int execute(const vector<string>*, CommandExecutor*);
|
int execute(const struct Params&, CommandExecutor*);
|
||||||
void addOption(Option* a);
|
void addOption(const poptOption& option);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int (*funcCommand)(CommandExecutor* whoExecuted,const Datas& data, const vector<char*> argv);
|
int (*funcCommand)(CommandExecutor* whoExecuted,const Datas& data, const vector<const char*> argv);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // COMMAND_H
|
#endif // COMMAND_H
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#ifndef COMMANDEXECUTOR_H
|
#ifndef COMMANDEXECUTOR_H
|
||||||
#define COMMANDEXECUTOR_H
|
#define COMMANDEXECUTOR_H
|
||||||
|
#pragma once
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -16,12 +17,16 @@ namespace mshconsole{
|
||||||
ExitException(int c=0) : code(c){}
|
ExitException(int c=0) : code(c){}
|
||||||
};
|
};
|
||||||
public:
|
public:
|
||||||
virtual int executeCmd(std::vector<std::string>* args) = 0;
|
virtual int executeCmd(const struct Params& args) = 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);
|
||||||
CommandExecutor();
|
CommandExecutor();
|
||||||
};
|
};
|
||||||
|
struct Params{
|
||||||
|
int argc;
|
||||||
|
char** argv;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // COMMANDEXECUTOR_H
|
#endif // COMMANDEXECUTOR_H
|
||||||
|
|
|
@ -14,10 +14,10 @@ namespace mshconsole {
|
||||||
return commands.size();
|
return commands.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::Commands::launch(const vector<string>* args, bool launchThread){
|
int Shell::Commands::launch(const struct Params& p, bool launchThread){
|
||||||
for(unsigned int i=0; i<(launchThread ? threadCommands.size() : commands.size()); i++){
|
for(unsigned int i=0; i<(launchThread ? threadCommands.size() : commands.size()); i++){
|
||||||
if((launchThread ? threadCommands[i]->getName() : commands[i]->getName())==args->operator [](0)){
|
if((launchThread ? threadCommands[i]->getName() : commands[i]->getName())==p.argv[0]){
|
||||||
return (launchThread ? threadCommands[i]->execute(args, parent) : commands[i]->execute(args, parent));
|
return (launchThread ? threadCommands[i]->execute(p, parent) : commands[i]->execute(p, parent));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw CommandNotFoundException();
|
throw CommandNotFoundException();
|
||||||
|
|
|
@ -1,30 +1,40 @@
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
|
|
||||||
namespace mshconsole{
|
namespace mshconsole{
|
||||||
void Command::Data::set(bool b){
|
inline const union Command::Data::Arg& Command::Data::getArg() const{
|
||||||
if(isSDataValorized) throw MultipleDefinitionException();
|
return d;
|
||||||
b ? state=Data::TRUE : state=Data::FALSE;
|
|
||||||
isSDataValorized=true;
|
|
||||||
}
|
}
|
||||||
void Command::Data::set(const string& s){
|
enum Command::Data::Types Command::Data::getType() const{
|
||||||
if(isSDataValorized) throw MultipleDefinitionException();
|
if(of->argInfo & POPT_ARG_INT || of->argInfo & POPT_ARG_VAL || of->argInfo == POPT_ARG_NONE){
|
||||||
state=Data::STRING;
|
return INT;
|
||||||
sData=s;
|
|
||||||
isSDataValorized=true;
|
|
||||||
}
|
|
||||||
enum Command::Data::States Command::Data::getState() const{
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
const string& Command::Data::getData() const{
|
|
||||||
if(state!=Data::STRING) return EMPTY;
|
|
||||||
else return sData;
|
|
||||||
}
|
|
||||||
bool Command::Data::getBoolState() const{
|
|
||||||
return state!=Data::FALSE;
|
|
||||||
}
|
|
||||||
Command::Data* Command::Datas::getOptData(const string& opt) const{
|
|
||||||
for(size_t i=0; i<size(); i++){
|
|
||||||
if(operator[](i)->of->getOptionName()==opt) return operator[](i);
|
|
||||||
}
|
}
|
||||||
|
else if(of->argInfo & POPT_ARG_STRING){
|
||||||
|
return STRING;
|
||||||
|
}
|
||||||
|
else if(of->argInfo & POPT_ARG_LONG){
|
||||||
|
return LONG;
|
||||||
|
}
|
||||||
|
else if(of->argInfo & POPT_ARG_FLOAT){
|
||||||
|
return FLOAT;
|
||||||
|
}
|
||||||
|
else if(of->argInfo & POPT_ARG_DOUBLE){
|
||||||
|
return DOUBLE;
|
||||||
|
}
|
||||||
|
//else return NULL;
|
||||||
|
}
|
||||||
|
int Command::Data::getInt() const{
|
||||||
|
return d.i;
|
||||||
|
}
|
||||||
|
char* Command::Data::getString() const{
|
||||||
|
return d.i ? d.s : const_cast<char*>("");
|
||||||
|
}
|
||||||
|
long Command::Data::getLong() const{
|
||||||
|
return d.l;
|
||||||
|
}
|
||||||
|
float Command::Data::getFloat() const{
|
||||||
|
return d.f;
|
||||||
|
}
|
||||||
|
double Command::Data::getDouble() const{
|
||||||
|
return d.d;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
19
msh-console-library/datas.cpp
Normal file
19
msh-console-library/datas.cpp
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#include "command.h"
|
||||||
|
|
||||||
|
namespace mshconsole {
|
||||||
|
Command::Data* Command::Datas::getOptData(const string& opt) const{
|
||||||
|
for(size_t i=0; i<this->size(); i++){
|
||||||
|
if(opt == this->at(i)->of->longName) return this->at(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Command::Data* Command::Datas::getOptData(char opt) const{
|
||||||
|
for(size_t i=0; i<this->size(); i++){
|
||||||
|
if(opt == this->at(i)->of->shortName) return this->at(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Command::Data* Command::Datas::operator [](const string& opt) const{
|
||||||
|
if(opt.length()>2) return getOptData(opt);
|
||||||
|
if(opt.length()==1) return getOptData(opt[0]);
|
||||||
|
else return nullptr;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,17 +0,0 @@
|
||||||
#include "option.h"
|
|
||||||
|
|
||||||
namespace mshconsole{
|
|
||||||
Option::Option(const string &s, States b): optionName(s), want(b){
|
|
||||||
if(optionName.length()<1){
|
|
||||||
throw OptionNameEmptyException{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const string& Option::getOptionName() const{
|
|
||||||
return optionName;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Option::hasFlag(enum States s) const{
|
|
||||||
return want & s;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
#include <string>
|
|
||||||
using std::string;
|
|
||||||
|
|
||||||
namespace mshconsole{
|
|
||||||
class Option{
|
|
||||||
string optionName;
|
|
||||||
class OptionNameEmptyException{};
|
|
||||||
public:
|
|
||||||
enum States{
|
|
||||||
STRING=2,
|
|
||||||
BOOL=1
|
|
||||||
};
|
|
||||||
private:
|
|
||||||
enum States want;
|
|
||||||
public:
|
|
||||||
Option(const string& s, enum States b);
|
|
||||||
const string& getOptionName() const;
|
|
||||||
bool hasFlag(enum States s) const;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -43,7 +43,7 @@ namespace mshconsole {
|
||||||
|
|
||||||
//launch loop
|
//launch loop
|
||||||
string* line;
|
string* line;
|
||||||
vector<string>* args;
|
struct Params p;
|
||||||
int exitCode;
|
int exitCode;
|
||||||
try{
|
try{
|
||||||
do {
|
do {
|
||||||
|
@ -59,10 +59,9 @@ namespace mshconsole {
|
||||||
readSuccess = false;
|
readSuccess = false;
|
||||||
}
|
}
|
||||||
}while(!readSuccess);
|
}while(!readSuccess);
|
||||||
args = split_line(line);
|
p = split_line(line);
|
||||||
executeCmd(args);
|
executeCmd(p);
|
||||||
delete args;
|
} while (1);
|
||||||
} while (1);
|
|
||||||
} catch(CommandExecutor::ExitException c) {
|
} catch(CommandExecutor::ExitException c) {
|
||||||
exitCode=c.getCode();
|
exitCode=c.getCode();
|
||||||
}
|
}
|
||||||
|
@ -74,7 +73,7 @@ namespace mshconsole {
|
||||||
return exitCode;
|
return exitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::launchCmd(vector<string>* args)
|
int Shell::launchCmd(const struct Params& p)
|
||||||
{
|
{
|
||||||
using std::exit;
|
using std::exit;
|
||||||
int status;
|
int status;
|
||||||
|
@ -86,19 +85,12 @@ namespace mshconsole {
|
||||||
//execute threadCommand
|
//execute threadCommand
|
||||||
int a;
|
int a;
|
||||||
try {
|
try {
|
||||||
a=cmds.launch(args, true);
|
a=cmds.launch(p, true);
|
||||||
}
|
}
|
||||||
catch (CommandNotFoundException){
|
catch (CommandNotFoundException){
|
||||||
//execute bash command or program
|
//execute bash command or program
|
||||||
vector<char *> argv(args->size() + 1);
|
if((a=execvp(p.argv[0], p.argv)) == -1) {
|
||||||
size_t i;
|
cerr << name <<": command " << p.argv[0] << " not found\n";
|
||||||
for (i = 0; i != args->size()-1; ++i)
|
|
||||||
{
|
|
||||||
argv[i] = &(args->operator[](i)[0]);
|
|
||||||
}
|
|
||||||
argv[i] = NULL;
|
|
||||||
if((a=execvp(argv[0], argv.data())) == -1) {
|
|
||||||
cerr << name <<": command " << args->operator [](0) << " not found\n";
|
|
||||||
}
|
}
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
@ -118,18 +110,17 @@ namespace mshconsole {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::executeCmd(vector<string>* args)
|
int Shell::executeCmd(const struct Params& p)
|
||||||
{
|
{
|
||||||
if (args->operator [](0) == "\0") {
|
if (!p.argc) { //empty line
|
||||||
// An empty command was entered.
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
int ret;
|
int ret;
|
||||||
try {
|
try {
|
||||||
ret = cmds.launch(args, false);
|
ret = cmds.launch(p, false);
|
||||||
}
|
}
|
||||||
catch(CommandNotFoundException) {
|
catch(CommandNotFoundException) {
|
||||||
ret = launchCmd(args);
|
ret = launchCmd(p);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -181,17 +172,10 @@ namespace mshconsole {
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<string>* Shell::split_line(const string* line)
|
struct Params Shell::split_line(const string* line){
|
||||||
{
|
struct Params p;
|
||||||
vector<string>* tokens = new vector<string>();
|
stringToArgcArgv(*line, &(p.argc), &(p.argv));
|
||||||
string ln = *line;
|
return p;
|
||||||
istringstream is(ln);
|
|
||||||
int i;
|
|
||||||
for(i=0; getline(is, ln, ' '); i++){
|
|
||||||
tokens->push_back(ln);
|
|
||||||
}
|
|
||||||
tokens->push_back("\0");
|
|
||||||
return tokens;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Shell::undoingLine = false;
|
bool Shell::undoingLine = false;
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
#ifndef SHELL_H
|
#ifndef SHELL_H
|
||||||
#define SHELL_H
|
#define SHELL_H
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <cstdlib>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cstring>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <sstream>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
//#include "commands.h"
|
#include <wordexp.h>
|
||||||
#include "commandexecutor.h"
|
#include "commandexecutor.h"
|
||||||
|
#include "stringtoargcargv.h"
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::cin;
|
using std::cin;
|
||||||
using std::cout;
|
using std::cout;
|
||||||
|
@ -35,7 +36,7 @@ namespace mshconsole {
|
||||||
Commands(Shell*);
|
Commands(Shell*);
|
||||||
void add(Command*, bool isthread=false);
|
void add(Command*, bool isthread=false);
|
||||||
size_t howMany() const;
|
size_t howMany() const;
|
||||||
int launch(const vector<string>* args, bool launchThread=false);
|
int launch(const struct Params& args, bool launchThread=false);
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool undoingLine;
|
static bool undoingLine;
|
||||||
|
@ -46,10 +47,10 @@ namespace mshconsole {
|
||||||
void (*shellPostSetup)(Shell *);
|
void (*shellPostSetup)(Shell *);
|
||||||
bool notLoop;
|
bool notLoop;
|
||||||
|
|
||||||
int launchCmd(vector<string>* args);
|
int launchCmd(const struct Params& args);
|
||||||
static void EofHandler(int);
|
static void EofHandler(int);
|
||||||
string* read_line();
|
string* read_line();
|
||||||
vector<string>* split_line(const string* line);
|
struct Params split_line(const string* line);
|
||||||
|
|
||||||
class IsUndoingLineException {};
|
class IsUndoingLineException {};
|
||||||
class CommandNotFoundException {};
|
class CommandNotFoundException {};
|
||||||
|
@ -69,7 +70,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);
|
||||||
size_t howManyCmds() const;
|
size_t howManyCmds() const;
|
||||||
int executeCmd(vector<string>* args);
|
int executeCmd(const struct Params& args);
|
||||||
int executeCmd(const string& args);
|
int executeCmd(const string& args);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
192
msh-console-library/stringtoargcargv.cpp
Normal file
192
msh-console-library/stringtoargcargv.cpp
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
/* stringtoargcargv.cpp -- Parsing a string to std::vector<string>
|
||||||
|
|
||||||
|
Copyright (C) 2011 Bernhard Eder
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any damages
|
||||||
|
arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must not
|
||||||
|
claim that you wrote the original software. If you use this software
|
||||||
|
in a product, an acknowledgment in the product documentation would be
|
||||||
|
appreciated but is not required.
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
misrepresented as being the original software.
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
|
||||||
|
Bernhard Eder blog_at_bbgen.net
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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);
|
||||||
|
|
||||||
|
*argv = (char**)std::malloc(args.size() * 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
45
msh-console-library/stringtoargcargv.h
Normal file
45
msh-console-library/stringtoargcargv.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#ifndef STRINGTOARGCARGV_H
|
||||||
|
#define STRINGTOARGCARGV_H
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/* stringtoargcargv.cpp -- Parsing a string to std::vector<string>
|
||||||
|
|
||||||
|
Copyright (C) 2011 Bernhard Eder
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any damages
|
||||||
|
arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must not
|
||||||
|
claim that you wrote the original software. If you use this software
|
||||||
|
in a product, an acknowledgment in the product documentation would be
|
||||||
|
appreciated but is not required.
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
misrepresented as being the original software.
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
|
||||||
|
Bernhard Eder blog_at_bbgen.net
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
|
||||||
|
#endif //STRINGTOARGCARGV_H
|
Loading…
Reference in a new issue