2159 lines
68 KiB
C++
2159 lines
68 KiB
C++
/*
|
|
This file is part of ezOptionParser. See MIT-LICENSE.
|
|
|
|
Copyright (C) 2011,2012,2014 Remik Ziemlinski <first d0t surname att gmail>
|
|
|
|
CHANGELOG
|
|
|
|
v0.0.0 20110505 rsz Created.
|
|
v0.1.0 20111006 rsz Added validator.
|
|
v0.1.1 20111012 rsz Fixed validation of ulonglong.
|
|
v0.1.2 20111126 rsz Allow flag names start with alphanumeric (previously, flag had to start with alpha).
|
|
v0.1.3 20120108 rsz Created work-around for unique id generation with IDGenerator that avoids retarded c++ translation unit linker errors with single-header static variables. Forced inline on all methods to please retard compiler and avoid multiple def errors.
|
|
v0.1.4 20120629 Enforced MIT license on all files.
|
|
v0.2.0 20121120 Added parseIndex to OptionGroup.
|
|
v0.2.1 20130506 Allow disabling doublespace of OPTIONS usage descriptions.
|
|
v0.2.2 20140504 Jose Santiago added compiler warning fixes.
|
|
Bruce Shankle added a crash fix in description printing.
|
|
*/
|
|
#ifndef EZ_OPTION_PARSER_H
|
|
#define EZ_OPTION_PARSER_H
|
|
|
|
#include <stdlib.h>
|
|
#include <vector>
|
|
#include <list>
|
|
#include <map>
|
|
#include <string>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <algorithm>
|
|
#include <limits>
|
|
#include <sstream>
|
|
#include <cstring>
|
|
|
|
namespace ez {
|
|
#define DEBUGLINE() printf("%s:%d\n", __FILE__, __LINE__);
|
|
|
|
/* ################################################################### */
|
|
template<typename T>
|
|
static T fromString(const std::string* s) {
|
|
std::istringstream stream (s->c_str());
|
|
T t;
|
|
stream >> t;
|
|
return t;
|
|
};
|
|
template<typename T>
|
|
static T fromString(const char* s) {
|
|
std::istringstream stream (s);
|
|
T t;
|
|
stream >> t;
|
|
return t;
|
|
};
|
|
/* ################################################################### */
|
|
static inline bool isdigit(const std::string & s, int i=0) {
|
|
int n = s.length();
|
|
for(; i < n; ++i)
|
|
switch(s[i]) {
|
|
case '0': case '1': case '2':
|
|
case '3': case '4': case '5':
|
|
case '6': case '7': case '8': case '9': break;
|
|
default: return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
/* ################################################################### */
|
|
static bool isdigit(const std::string * s, int i=0) {
|
|
int n = s->length();
|
|
for(; i < n; ++i)
|
|
switch(s->at(i)) {
|
|
case '0': case '1': case '2':
|
|
case '3': case '4': case '5':
|
|
case '6': case '7': case '8': case '9': break;
|
|
default: return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
/* ################################################################### */
|
|
/*
|
|
Compare strings for opts, so short opt flags come before long format flags.
|
|
For example, -d < --dimension < --dmn, and also lower come before upper. The default STL std::string compare doesn't do that.
|
|
*/
|
|
static bool CmpOptStringPtr(std::string * s1, std::string * s2) {
|
|
int c1,c2;
|
|
const char *s=s1->c_str();
|
|
for(c1=0; c1 < (long int)s1->size(); ++c1)
|
|
if (isalnum(s[c1])) // locale sensitive.
|
|
break;
|
|
|
|
s=s2->c_str();
|
|
for(c2=0; c2 < (long int)s2->size(); ++c2)
|
|
if (isalnum(s[c2]))
|
|
break;
|
|
|
|
// Test which has more symbols before its name.
|
|
if (c1 > c2)
|
|
return false;
|
|
else if (c1 < c2)
|
|
return true;
|
|
|
|
// Both have same number of symbols, so compare first letter.
|
|
char char1 = s1->at(c1);
|
|
char char2 = s2->at(c2);
|
|
char lo1 = tolower(char1);
|
|
char lo2 = tolower(char2);
|
|
|
|
if (lo1 != lo2)
|
|
return lo1 < lo2;
|
|
|
|
// Their case doesn't match, so find which is lower.
|
|
char up1 = isupper(char1);
|
|
char up2 = isupper(char2);
|
|
|
|
if (up1 && !up2)
|
|
return false;
|
|
else if (!up1 && up2)
|
|
return true;
|
|
|
|
return (s1->compare(*s2)<0);
|
|
};
|
|
/* ################################################################### */
|
|
/*
|
|
Makes a vector of strings from one string,
|
|
splitting at (and excluding) delimiter "token".
|
|
*/
|
|
static void SplitDelim( const std::string& s, const char token, std::vector<std::string*> * result) {
|
|
std::string::const_iterator i = s.begin();
|
|
std::string::const_iterator j = s.begin();
|
|
const std::string::const_iterator e = s.end();
|
|
|
|
while(i!=e) {
|
|
while(i!=e && *i++!=token);
|
|
std::string *newstr = new std::string(j, i);
|
|
if (newstr->at(newstr->size()-1) == token) newstr->erase(newstr->size()-1);
|
|
result->push_back(newstr);
|
|
j = i;
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
// Variant that uses deep copies and references instead of pointers (less efficient).
|
|
static void SplitDelim( const std::string& s, const char token, std::vector<std::string> & result) {
|
|
std::string::const_iterator i = s.begin();
|
|
std::string::const_iterator j = s.begin();
|
|
const std::string::const_iterator e = s.end();
|
|
|
|
while(i!=e) {
|
|
while(i!=e && *i++!=token);
|
|
std::string newstr(j, i);
|
|
if (newstr.at(newstr.size()-1) == token) newstr.erase(newstr.size()-1);
|
|
result.push_back(newstr);
|
|
j = i;
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
// Variant that uses list instead of vector for efficient insertion, etc.
|
|
static void SplitDelim( const std::string& s, const char token, std::list<std::string*> & result) {
|
|
std::string::const_iterator i = s.begin();
|
|
std::string::const_iterator j = s.begin();
|
|
const std::string::const_iterator e = s.end();
|
|
|
|
while(i!=e) {
|
|
while(i!=e && *i++!=token);
|
|
std::string *newstr = new std::string(j, i);
|
|
if (newstr->at(newstr->size()-1) == token) newstr->erase(newstr->size()-1);
|
|
result.push_back(newstr);
|
|
j = i;
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
static void ToU1(std::string ** strings, unsigned char * out, int n) {
|
|
for(int i=0; i < n; ++i) {
|
|
out[i] = (unsigned char)atoi(strings[i]->c_str());
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
static void ToS1(std::string ** strings, char * out, int n) {
|
|
for(int i=0; i < n; ++i) {
|
|
out[i] = (char)atoi(strings[i]->c_str());
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
static void ToU2(std::string ** strings, unsigned short * out, int n) {
|
|
for(int i=0; i < n; ++i) {
|
|
out[i] = (unsigned short)atoi(strings[i]->c_str());
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
static void ToS2(std::string ** strings, short * out, int n) {
|
|
for(int i=0; i < n; ++i) {
|
|
out[i] = (short)atoi(strings[i]->c_str());
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
static void ToS4(std::string ** strings, int * out, int n) {
|
|
for(int i=0; i < n; ++i) {
|
|
out[i] = atoi(strings[i]->c_str());
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
static void ToU4(std::string ** strings, unsigned int * out, int n) {
|
|
for(int i=0; i < n; ++i) {
|
|
out[i] = (unsigned int)strtoul(strings[i]->c_str(), NULL, 0);
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
static void ToS8(std::string ** strings, long long * out, int n) {
|
|
for(int i=0; i < n; ++i) {
|
|
std::stringstream ss(strings[i]->c_str());
|
|
ss >> out[i];
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
static void ToU8(std::string ** strings, unsigned long long * out, int n) {
|
|
for(int i=0; i < n; ++i) {
|
|
std::stringstream ss(strings[i]->c_str());
|
|
ss >> out[i];
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
static void ToF(std::string ** strings, float * out, int n) {
|
|
for(int i=0; i < n; ++i) {
|
|
out[i] = (float)atof(strings[i]->c_str());
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
static void ToD(std::string ** strings, double * out, int n) {
|
|
for(int i=0; i < n; ++i) {
|
|
out[i] = (double)atof(strings[i]->c_str());
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
static void StringsToInts(std::vector<std::string> & strings, std::vector<int> & out) {
|
|
for(int i=0; i < (long int)strings.size(); ++i) {
|
|
out.push_back(atoi(strings[i].c_str()));
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
static void StringsToInts(std::vector<std::string*> * strings, std::vector<int> * out) {
|
|
for(int i=0; i < (long int)strings->size(); ++i) {
|
|
out->push_back(atoi(strings->at(i)->c_str()));
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
static void StringsToLongs(std::vector<std::string> & strings, std::vector<long> & out) {
|
|
for(int i=0; i < (long int)strings.size(); ++i) {
|
|
out.push_back(atol(strings[i].c_str()));
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
static void StringsToLongs(std::vector<std::string*> * strings, std::vector<long> * out) {
|
|
for(int i=0; i < (long int)strings->size(); ++i) {
|
|
out->push_back(atol(strings->at(i)->c_str()));
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
static void StringsToULongs(std::vector<std::string> & strings, std::vector<unsigned long> & out) {
|
|
for(int i=0; i < (long int)strings.size(); ++i) {
|
|
out.push_back(strtoul(strings[i].c_str(),0,0));
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
static void StringsToULongs(std::vector<std::string*> * strings, std::vector<unsigned long> * out) {
|
|
for(int i=0; i < (long int)strings->size(); ++i) {
|
|
out->push_back(strtoul(strings->at(i)->c_str(),0,0));
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
static void StringsToFloats(std::vector<std::string> & strings, std::vector<float> & out) {
|
|
for(int i=0; i < (long int)strings.size(); ++i) {
|
|
out.push_back(atof(strings[i].c_str()));
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
static void StringsToFloats(std::vector<std::string*> * strings, std::vector<float> * out) {
|
|
for(int i=0; i < (long int)strings->size(); ++i) {
|
|
out->push_back(atof(strings->at(i)->c_str()));
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
static void StringsToDoubles(std::vector<std::string> & strings, std::vector<double> & out) {
|
|
for(int i=0; i < (long int)strings.size(); ++i) {
|
|
out.push_back(atof(strings[i].c_str()));
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
static void StringsToDoubles(std::vector<std::string*> * strings, std::vector<double> * out) {
|
|
for(int i=0; i < (long int)strings->size(); ++i) {
|
|
out->push_back(atof(strings->at(i)->c_str()));
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
static void StringsToStrings(std::vector<std::string*> * strings, std::vector<std::string> * out) {
|
|
for(int i=0; i < (long int)strings->size(); ++i) {
|
|
out->push_back( *strings->at(i) );
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
static void ToLowerASCII(std::string & s) {
|
|
int n = s.size();
|
|
int i=0;
|
|
char c;
|
|
for(; i < n; ++i) {
|
|
c = s[i];
|
|
if(c<='Z' && c>='A')
|
|
s[i] = c+32;
|
|
}
|
|
}
|
|
/* ################################################################### */
|
|
static char** CommandLineToArgvA(char* CmdLine, int* _argc) {
|
|
char** argv;
|
|
char* _argv;
|
|
unsigned long len;
|
|
unsigned long argc;
|
|
char a;
|
|
unsigned long i, j;
|
|
|
|
bool in_QM;
|
|
bool in_TEXT;
|
|
bool in_SPACE;
|
|
|
|
len = strlen(CmdLine);
|
|
i = ((len+2)/2)*sizeof(void*) + sizeof(void*);
|
|
|
|
argv = (char**)malloc(i + (len+2)*sizeof(char));
|
|
|
|
_argv = (char*)(((unsigned char*)argv)+i);
|
|
|
|
argc = 0;
|
|
argv[argc] = _argv;
|
|
in_QM = false;
|
|
in_TEXT = false;
|
|
in_SPACE = true;
|
|
i = 0;
|
|
j = 0;
|
|
|
|
while( (a = CmdLine[i]) ) {
|
|
if(in_QM) {
|
|
if( (a == '\"') ||
|
|
(a == '\'')) // rsz. Added single quote.
|
|
{
|
|
in_QM = false;
|
|
} else {
|
|
_argv[j] = a;
|
|
j++;
|
|
}
|
|
} else {
|
|
switch(a) {
|
|
case '\"':
|
|
case '\'': // rsz. Added single quote.
|
|
in_QM = true;
|
|
in_TEXT = true;
|
|
if(in_SPACE) {
|
|
argv[argc] = _argv+j;
|
|
argc++;
|
|
}
|
|
in_SPACE = false;
|
|
break;
|
|
case ' ':
|
|
case '\t':
|
|
case '\n':
|
|
case '\r':
|
|
if(in_TEXT) {
|
|
_argv[j] = '\0';
|
|
j++;
|
|
}
|
|
in_TEXT = false;
|
|
in_SPACE = true;
|
|
break;
|
|
default:
|
|
in_TEXT = true;
|
|
if(in_SPACE) {
|
|
argv[argc] = _argv+j;
|
|
argc++;
|
|
}
|
|
_argv[j] = a;
|
|
j++;
|
|
in_SPACE = false;
|
|
break;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
_argv[j] = '\0';
|
|
argv[argc] = NULL;
|
|
|
|
(*_argc) = argc;
|
|
return argv;
|
|
};
|
|
/* ################################################################### */
|
|
// Create unique ids with static and still allow single header that avoids multiple definitions linker error.
|
|
class ezOptionParserIDGenerator {
|
|
public:
|
|
static ezOptionParserIDGenerator& instance () { static ezOptionParserIDGenerator Generator; return Generator; }
|
|
short next () { return ++_id; }
|
|
private:
|
|
ezOptionParserIDGenerator() : _id(-1) {}
|
|
short _id;
|
|
};
|
|
/* ################################################################### */
|
|
/* Validate a value by checking:
|
|
- if as string, see if converted value is within datatype's limits,
|
|
- and see if falls within a desired range,
|
|
- or see if within set of given list of values.
|
|
|
|
If comparing with a range, the values list must contain one or two values. One value is required when comparing with <, <=, >, >=. Use two values when requiring a test such as <x<, <=x<, <x<=, <=x<=.
|
|
A regcomp/regexec based class could be created in the future if a need arises.
|
|
*/
|
|
class ezOptionValidator {
|
|
public:
|
|
inline ezOptionValidator(const char* _type, const char* _op=0, const char* list=0, bool _insensitive=false);
|
|
inline ezOptionValidator(char _type);
|
|
inline ezOptionValidator(char _type, char _op, const char* list, int _size);
|
|
inline ezOptionValidator(char _type, char _op, const unsigned char* list, int _size);
|
|
inline ezOptionValidator(char _type, char _op, const short* list, int _size);
|
|
inline ezOptionValidator(char _type, char _op, const unsigned short* list, int _size);
|
|
inline ezOptionValidator(char _type, char _op, const int* list, int _size);
|
|
inline ezOptionValidator(char _type, char _op, const unsigned int* list, int _size);
|
|
inline ezOptionValidator(char _type, char _op, const long long* list, int _size);
|
|
inline ezOptionValidator(char _type, char _op, const unsigned long long* list, int _size=0);
|
|
inline ezOptionValidator(char _type, char _op, const float* list, int _size);
|
|
inline ezOptionValidator(char _type, char _op, const double* list, int _size);
|
|
inline ezOptionValidator(char _type, char _op, const char** list, int _size, bool _insensitive);
|
|
inline ~ezOptionValidator();
|
|
|
|
inline bool isValid(const std::string * value);
|
|
inline void print();
|
|
inline void reset();
|
|
|
|
/* If value must be in custom range, use these comparison modes. */
|
|
enum OP { NOOP=0,
|
|
LT, /* value < list[0] */
|
|
LE, /* value <= list[0] */
|
|
GT, /* value > list[0] */
|
|
GE, /* value >= list[0] */
|
|
GTLT, /* list[0] < value < list[1] */
|
|
GELT, /* list[0] <= value < list[1] */
|
|
GELE, /* list[0] <= value <= list[1] */
|
|
GTLE, /* list[0] < value <= list[1] */
|
|
IN /* if value is in list */
|
|
};
|
|
|
|
enum TYPE { NOTYPE=0, S1, U1, S2, U2, S4, U4, S8, U8, F, D, T };
|
|
enum TYPE2 { NOTYPE2=0, INT8, UINT8, INT16, UINT16, INT32, UINT32, INT64, UINT64, FLOAT, DOUBLE, TEXT };
|
|
|
|
union {
|
|
unsigned char *u1;
|
|
char *s1;
|
|
unsigned short *u2;
|
|
short *s2;
|
|
unsigned int *u4;
|
|
int *s4;
|
|
unsigned long long *u8;
|
|
long long *s8;
|
|
float *f;
|
|
double *d;
|
|
std::string** t;
|
|
};
|
|
|
|
char op;
|
|
bool quiet;
|
|
short id;
|
|
char type;
|
|
int size;
|
|
bool insensitive;
|
|
};
|
|
/* ------------------------------------------------------------------- */
|
|
ezOptionValidator::~ezOptionValidator() {
|
|
reset();
|
|
};
|
|
/* ------------------------------------------------------------------- */
|
|
void ezOptionValidator::reset() {
|
|
#define CLEAR(TYPE,P) case TYPE: if (P) delete [] P; P = 0; break;
|
|
switch(type) {
|
|
CLEAR(S1,s1);
|
|
CLEAR(U1,u1);
|
|
CLEAR(S2,s2);
|
|
CLEAR(U2,u2);
|
|
CLEAR(S4,s4);
|
|
CLEAR(U4,u4);
|
|
CLEAR(S8,s8);
|
|
CLEAR(U8,u8);
|
|
CLEAR(F,f);
|
|
CLEAR(D,d);
|
|
case T:
|
|
for(int i=0; i < size; ++i)
|
|
delete t[i];
|
|
|
|
delete [] t;
|
|
t = 0;
|
|
break;
|
|
default: break;
|
|
}
|
|
|
|
size = 0;
|
|
op = NOOP;
|
|
type = NOTYPE;
|
|
};
|
|
/* ------------------------------------------------------------------- */
|
|
ezOptionValidator::ezOptionValidator(char _type) : s1(0), op(0), quiet(0), type(_type), size(0), insensitive(0) {
|
|
id = ezOptionParserIDGenerator::instance().next();
|
|
};
|
|
/* ------------------------------------------------------------------- */
|
|
ezOptionValidator::ezOptionValidator(char _type, char _op, const char* list, int _size) : s1(0), op(_op), quiet(0), type(_type), size(_size), insensitive(0) {
|
|
id = ezOptionParserIDGenerator::instance().next();
|
|
s1 = new char[size];
|
|
memcpy(s1, list, size);
|
|
};
|
|
/* ------------------------------------------------------------------- */
|
|
ezOptionValidator::ezOptionValidator(char _type, char _op, const unsigned char* list, int _size) : u1(0), op(_op), quiet(0), type(_type), size(_size), insensitive(0) {
|
|
id = ezOptionParserIDGenerator::instance().next();
|
|
u1 = new unsigned char[size];
|
|
memcpy(u1, list, size);
|
|
};
|
|
/* ------------------------------------------------------------------- */
|
|
ezOptionValidator::ezOptionValidator(char _type, char _op, const short* list, int _size) : s2(0), op(_op), quiet(0), type(_type), size(_size), insensitive(0) {
|
|
id = ezOptionParserIDGenerator::instance().next();
|
|
s2 = new short[size];
|
|
memcpy(s2, list, size*sizeof(short));
|
|
};
|
|
/* ------------------------------------------------------------------- */
|
|
ezOptionValidator::ezOptionValidator(char _type, char _op, const unsigned short* list, int _size) : u2(0), op(_op), quiet(0), type(_type), size(_size), insensitive(0) {
|
|
id = ezOptionParserIDGenerator::instance().next();
|
|
u2 = new unsigned short[size];
|
|
memcpy(u2, list, size*sizeof(unsigned short));
|
|
};
|
|
/* ------------------------------------------------------------------- */
|
|
ezOptionValidator::ezOptionValidator(char _type, char _op, const int* list, int _size) : s4(0), op(_op), quiet(0), type(_type), size(_size), insensitive(0) {
|
|
id = ezOptionParserIDGenerator::instance().next();
|
|
s4 = new int[size];
|
|
memcpy(s4, list, size*sizeof(int));
|
|
};
|
|
/* ------------------------------------------------------------------- */
|
|
ezOptionValidator::ezOptionValidator(char _type, char _op, const unsigned int* list, int _size) : u4(0), op(_op), quiet(0), type(_type), size(_size), insensitive(0) {
|
|
id = ezOptionParserIDGenerator::instance().next();
|
|
u4 = new unsigned int[size];
|
|
memcpy(u4, list, size*sizeof(unsigned int));
|
|
};
|
|
/* ------------------------------------------------------------------- */
|
|
ezOptionValidator::ezOptionValidator(char _type, char _op, const long long* list, int _size) : s8(0), op(_op), quiet(0), type(_type), size(_size), insensitive(0) {
|
|
id = ezOptionParserIDGenerator::instance().next();
|
|
s8 = new long long[size];
|
|
memcpy(s8, list, size*sizeof(long long));
|
|
};
|
|
/* ------------------------------------------------------------------- */
|
|
ezOptionValidator::ezOptionValidator(char _type, char _op, const unsigned long long* list, int _size) : u8(0), op(_op), quiet(0), type(_type), size(_size), insensitive(0) {
|
|
id = ezOptionParserIDGenerator::instance().next();
|
|
u8 = new unsigned long long[size];
|
|
memcpy(u8, list, size*sizeof(unsigned long long));
|
|
};
|
|
/* ------------------------------------------------------------------- */
|
|
ezOptionValidator::ezOptionValidator(char _type, char _op, const float* list, int _size) : f(0), op(_op), quiet(0), type(_type), size(_size), insensitive(0) {
|
|
id = ezOptionParserIDGenerator::instance().next();
|
|
f = new float[size];
|
|
memcpy(f, list, size*sizeof(float));
|
|
};
|
|
/* ------------------------------------------------------------------- */
|
|
ezOptionValidator::ezOptionValidator(char _type, char _op, const double* list, int _size) : d(0), op(_op), quiet(0), type(_type), size(_size), insensitive(0) {
|
|
id = ezOptionParserIDGenerator::instance().next();
|
|
d = new double[size];
|
|
memcpy(d, list, size*sizeof(double));
|
|
};
|
|
/* ------------------------------------------------------------------- */
|
|
ezOptionValidator::ezOptionValidator(char _type, char _op, const char** list, int _size, bool _insensitive) : t(0), op(_op), quiet(0), type(_type), size(_size), insensitive(_insensitive) {
|
|
id = ezOptionParserIDGenerator::instance().next();
|
|
t = new std::string*[size];
|
|
int i=0;
|
|
|
|
for(; i < size; ++i) {
|
|
t[i] = new std::string(list[i]);
|
|
}
|
|
};
|
|
/* ------------------------------------------------------------------- */
|
|
/* Less efficient but convenient ctor that parses strings to setup validator.
|
|
_type: s1, u1, s2, u2, ..., f, d, t
|
|
_op: lt, gt, ..., in
|
|
_list: comma-delimited string
|
|
*/
|
|
ezOptionValidator::ezOptionValidator(const char* _type, const char* _op, const char* _list, bool _insensitive) : t(0), quiet(0), type(0), size(0), insensitive(_insensitive) {
|
|
id = ezOptionParserIDGenerator::instance().next();
|
|
|
|
switch(_type[0]) {
|
|
case 'u':
|
|
switch(_type[1]) {
|
|
case '1': type = U1; break;
|
|
case '2': type = U2; break;
|
|
case '4': type = U4; break;
|
|
case '8': type = U8; break;
|
|
default: break;
|
|
}
|
|
break;
|
|
case 's':
|
|
switch(_type[1]) {
|
|
case '1': type = S1;
|
|
break;
|
|
case '2': type = S2; break;
|
|
case '4': type = S4; break;
|
|
case '8': type = S8; break;
|
|
default: break;
|
|
}
|
|
break;
|
|
case 'f': type = F; break;
|
|
case 'd': type = D; break;
|
|
case 't': type = T; break;
|
|
default:
|
|
if (!quiet)
|
|
std::cerr << "ERROR: Unknown validator datatype \"" << _type << "\".\n";
|
|
break;
|
|
}
|
|
|
|
int nop = 0;
|
|
if (_op != 0)
|
|
nop = strlen(_op);
|
|
|
|
switch(nop) {
|
|
case 0: op = NOOP; break;
|
|
case 2:
|
|
switch(_op[0]) {
|
|
case 'g':
|
|
switch(_op[1]) {
|
|
case 'e': op = GE; break;
|
|
default: op = GT; break;
|
|
}
|
|
break;
|
|
case 'i': op = IN;
|
|
break;
|
|
default:
|
|
switch(_op[1]) {
|
|
case 'e': op = LE; break;
|
|
default: op = LT; break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 4:
|
|
switch(_op[1]) {
|
|
case 'e':
|
|
switch(_op[3]) {
|
|
case 'e': op = GELE; break;
|
|
default: op = GELT; break;
|
|
}
|
|
break;
|
|
default:
|
|
switch(_op[3]) {
|
|
case 'e': op = GTLE; break;
|
|
default: op = GTLT; break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
if (!quiet)
|
|
std::cerr << "ERROR: Unknown validator operation \"" << _op << "\".\n";
|
|
break;
|
|
}
|
|
|
|
if (_list == 0) return;
|
|
// Create list of strings and then cast to native datatypes.
|
|
std::string unsplit(_list);
|
|
std::list<std::string*> split;
|
|
std::list<std::string*>::iterator it;
|
|
SplitDelim(unsplit, ',', split);
|
|
size = split.size();
|
|
std::string **strings = new std::string*[size];
|
|
|
|
int i = 0;
|
|
for(it = split.begin(); it != split.end(); ++it)
|
|
strings[i++] = *it;
|
|
|
|
if (insensitive)
|
|
for(i=0; i < size; ++i)
|
|
ToLowerASCII(*strings[i]);
|
|
|
|
#define FreeStrings() { \
|
|
for(i=0; i < size; ++i)\
|
|
delete strings[i];\
|
|
delete [] strings;\
|
|
}
|
|
|
|
#define ToArray(T,P,Y) case T: P = new Y[size]; To##T(strings, P, size); FreeStrings(); break;
|
|
switch(type) {
|
|
ToArray(S1,s1,char);
|
|
ToArray(U1,u1,unsigned char);
|
|
ToArray(S2,s2,short);
|
|
ToArray(U2,u2,unsigned short);
|
|
ToArray(S4,s4,int);
|
|
ToArray(U4,u4,unsigned int);
|
|
ToArray(S8,s8,long long);
|
|
ToArray(U8,u8,unsigned long long);
|
|
ToArray(F,f,float);
|
|
ToArray(D,d,double);
|
|
case T: t = strings; break; /* Don't erase strings array. */
|
|
default: break;
|
|
}
|
|
};
|
|
/* ------------------------------------------------------------------- */
|
|
void ezOptionValidator::print() {
|
|
printf("id=%d, op=%d, type=%d, size=%d, insensitive=%d\n", id, op, type, size, insensitive);
|
|
};
|
|
/* ------------------------------------------------------------------- */
|
|
bool ezOptionValidator::isValid(const std::string * valueAsString) {
|
|
if (valueAsString == 0) return false;
|
|
|
|
#define CHECKRANGE(E,T) {\
|
|
std::stringstream ss(valueAsString->c_str()); \
|
|
long long E##value; \
|
|
ss >> E##value; \
|
|
long long E##min = static_cast<long long>(std::numeric_limits<T>::min()); \
|
|
if (E##value < E##min) { \
|
|
if (!quiet) \
|
|
std::cerr << "ERROR: Invalid value " << E##value << " is less than datatype min " << E##min << ".\n"; \
|
|
return false; \
|
|
} \
|
|
\
|
|
long long E##max = static_cast<long long>(std::numeric_limits<T>::max()); \
|
|
if (E##value > E##max) { \
|
|
if (!quiet) \
|
|
std::cerr << "ERROR: Invalid value " << E##value << " is greater than datatype max " << E##max << ".\n"; \
|
|
return false; \
|
|
} \
|
|
}
|
|
// Check if within datatype limits.
|
|
if (type != T) {
|
|
switch(type) {
|
|
case S1: CHECKRANGE(S1,char); break;
|
|
case U1: CHECKRANGE(U1,unsigned char); break;
|
|
case S2: CHECKRANGE(S2,short); break;
|
|
case U2: CHECKRANGE(U2,unsigned short); break;
|
|
case S4: CHECKRANGE(S4,int); break;
|
|
case U4: CHECKRANGE(U4,unsigned int); break;
|
|
case S8: {
|
|
if ( (valueAsString->at(0) == '-') &&
|
|
isdigit(valueAsString,1) &&
|
|
(valueAsString->size() > 19) &&
|
|
(valueAsString->compare(1, 19, "9223372036854775808") > 0) ) {
|
|
if (!quiet)
|
|
std::cerr << "ERROR: Invalid value " << *valueAsString << " is less than datatype min -9223372036854775808.\n";
|
|
return false;
|
|
}
|
|
|
|
if (isdigit(valueAsString) &&
|
|
(valueAsString->size() > 18) &&
|
|
valueAsString->compare("9223372036854775807") > 0) {
|
|
if (!quiet)
|
|
std::cerr << "ERROR: Invalid value " << *valueAsString << " is greater than datatype max 9223372036854775807.\n";
|
|
return false;
|
|
}
|
|
} break;
|
|
case U8: {
|
|
if (valueAsString->compare("0") < 0) {
|
|
if (!quiet)
|
|
std::cerr << "ERROR: Invalid value " << *valueAsString << " is less than datatype min 0.\n";
|
|
return false;
|
|
}
|
|
|
|
if (isdigit(valueAsString) &&
|
|
(valueAsString->size() > 19) &&
|
|
valueAsString->compare("18446744073709551615") > 0) {
|
|
if (!quiet)
|
|
std::cerr << "ERROR: Invalid value " << *valueAsString << " is greater than datatype max 18446744073709551615.\n";
|
|
return false;
|
|
}
|
|
} break;
|
|
case F: {
|
|
double dmax = static_cast<double>(std::numeric_limits<float>::max());
|
|
double dvalue = atof(valueAsString->c_str());
|
|
double dmin = -dmax;
|
|
if (dvalue < dmin) {
|
|
if (!quiet) {
|
|
fprintf(stderr, "ERROR: Invalid value %g is less than datatype min %g.\n", dvalue, dmin);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (dvalue > dmax) {
|
|
if (!quiet)
|
|
std::cerr << "ERROR: Invalid value " << dvalue << " is greater than datatype max " << dmax << ".\n";
|
|
return false;
|
|
}
|
|
} break;
|
|
case D: {
|
|
long double ldmax = static_cast<long double>(std::numeric_limits<double>::max());
|
|
std::stringstream ss(valueAsString->c_str());
|
|
long double ldvalue;
|
|
ss >> ldvalue;
|
|
long double ldmin = -ldmax;
|
|
|
|
if (ldvalue < ldmin) {
|
|
if (!quiet)
|
|
std::cerr << "ERROR: Invalid value " << ldvalue << " is less than datatype min " << ldmin << ".\n";
|
|
return false;
|
|
}
|
|
|
|
if (ldvalue > ldmax) {
|
|
if (!quiet)
|
|
std::cerr << "ERROR: Invalid value " << ldvalue << " is greater than datatype max " << ldmax << ".\n";
|
|
return false;
|
|
}
|
|
} break;
|
|
case NOTYPE: default: break;
|
|
}
|
|
} else {
|
|
if (op == IN) {
|
|
int i=0;
|
|
if (insensitive) {
|
|
std::string valueAsStringLower(*valueAsString);
|
|
ToLowerASCII(valueAsStringLower);
|
|
for(; i < size; ++i) {
|
|
if (valueAsStringLower.compare(t[i]->c_str()) == 0)
|
|
return true;
|
|
}
|
|
} else {
|
|
for(; i < size; ++i) {
|
|
if (valueAsString->compare(t[i]->c_str()) == 0)
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Only check datatype limits, and return;
|
|
if (op == NOOP) return true;
|
|
|
|
#define VALIDATE(T, U, LIST) { \
|
|
/* Value string converted to true native type. */ \
|
|
std::stringstream ss(valueAsString->c_str());\
|
|
U v;\
|
|
ss >> v;\
|
|
/* Check if within list. */ \
|
|
if (op == IN) { \
|
|
T * last = LIST + size;\
|
|
return (last != std::find(LIST, last, v)); \
|
|
} \
|
|
\
|
|
/* Check if within user's custom range. */ \
|
|
T v0, v1; \
|
|
if (size > 0) { \
|
|
v0 = LIST[0]; \
|
|
} \
|
|
\
|
|
if (size > 1) { \
|
|
v1 = LIST[1]; \
|
|
} \
|
|
\
|
|
switch (op) {\
|
|
case LT:\
|
|
if (size > 0) {\
|
|
return v < v0;\
|
|
} else {\
|
|
std::cerr << "ERROR: No value given to validate if " << v << " < X.\n";\
|
|
return false;\
|
|
}\
|
|
break;\
|
|
case LE:\
|
|
if (size > 0) {\
|
|
return v <= v0;\
|
|
} else {\
|
|
std::cerr << "ERROR: No value given to validate if " << v << " <= X.\n";\
|
|
return false;\
|
|
}\
|
|
break;\
|
|
case GT:\
|
|
if (size > 0) {\
|
|
return v > v0;\
|
|
} else {\
|
|
std::cerr << "ERROR: No value given to validate if " << v << " > X.\n";\
|
|
return false;\
|
|
}\
|
|
break;\
|
|
case GE:\
|
|
if (size > 0) {\
|
|
return v >= v0;\
|
|
} else {\
|
|
std::cerr << "ERROR: No value given to validate if " << v << " >= X.\n";\
|
|
return false;\
|
|
}\
|
|
break;\
|
|
case GTLT:\
|
|
if (size > 1) {\
|
|
return (v0 < v) && (v < v1);\
|
|
} else {\
|
|
std::cerr << "ERROR: Missing values to validate if X1 < " << v << " < X2.\n";\
|
|
return false;\
|
|
}\
|
|
break;\
|
|
case GELT:\
|
|
if (size > 1) {\
|
|
return (v0 <= v) && (v < v1);\
|
|
} else {\
|
|
std::cerr << "ERROR: Missing values to validate if X1 <= " << v << " < X2.\n";\
|
|
return false;\
|
|
}\
|
|
break;\
|
|
case GELE:\
|
|
if (size > 1) {\
|
|
return (v0 <= v) && (v <= v1);\
|
|
} else {\
|
|
std::cerr << "ERROR: Missing values to validate if X1 <= " << v << " <= X2.\n";\
|
|
return false;\
|
|
}\
|
|
break;\
|
|
case GTLE:\
|
|
if (size > 1) {\
|
|
return (v0 < v) && (v <= v1);\
|
|
} else {\
|
|
std::cerr << "ERROR: Missing values to validate if X1 < " << v << " <= X2.\n";\
|
|
return false;\
|
|
}\
|
|
break;\
|
|
case NOOP: case IN: default: break;\
|
|
} \
|
|
}
|
|
|
|
switch(type) {
|
|
case U1: VALIDATE(unsigned char, int, u1); break;
|
|
case S1: VALIDATE(char, int, s1); break;
|
|
case U2: VALIDATE(unsigned short, int, u2); break;
|
|
case S2: VALIDATE(short, int, s2); break;
|
|
case U4: VALIDATE(unsigned int, unsigned int, u4); break;
|
|
case S4: VALIDATE(int, int, s4); break;
|
|
case U8: VALIDATE(unsigned long long, unsigned long long, u8); break;
|
|
case S8: VALIDATE(long long, long long, s8); break;
|
|
case F: VALIDATE(float, float, f); break;
|
|
case D: VALIDATE(double, double, d); break;
|
|
default: break;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
/* ################################################################### */
|
|
class OptionGroup {
|
|
public:
|
|
OptionGroup() : delim(0), expectArgs(0), isRequired(false), isSet(false) { }
|
|
|
|
~OptionGroup() {
|
|
int i;
|
|
for(i=0; i < (long int)flags.size(); ++i)
|
|
delete flags[i];
|
|
|
|
flags.clear();
|
|
parseIndex.clear();
|
|
clearArgs();
|
|
};
|
|
|
|
inline void clearArgs();
|
|
inline void getInt(int&);
|
|
inline void getLong(long&);
|
|
inline void getLongLong(long long&);
|
|
inline void getULong(unsigned long&);
|
|
inline void getULongLong(unsigned long long&);
|
|
inline void getFloat(float&);
|
|
inline void getDouble(double&);
|
|
inline void getString(std::string&);
|
|
inline void getInts(std::vector<int>&);
|
|
inline void getLongs(std::vector<long>&);
|
|
inline void getULongs(std::vector<unsigned long>&);
|
|
inline void getFloats(std::vector<float>&);
|
|
inline void getDoubles(std::vector<double>&);
|
|
inline void getStrings(std::vector<std::string>&);
|
|
inline void getMultiInts(std::vector< std::vector<int> >&);
|
|
inline void getMultiLongs(std::vector< std::vector<long> >&);
|
|
inline void getMultiULongs(std::vector< std::vector<unsigned long> >&);
|
|
inline void getMultiFloats(std::vector< std::vector<float> >&);
|
|
inline void getMultiDoubles(std::vector< std::vector<double> >&);
|
|
inline void getMultiStrings(std::vector< std::vector<std::string> >&);
|
|
|
|
// defaults value regardless of being set by user.
|
|
std::string defaults;
|
|
// If expects arguments, this will delimit arg list.
|
|
char delim;
|
|
// If not 0, then number of delimited args. -1 for arbitrary number.
|
|
int expectArgs;
|
|
// Descriptive help message shown in usage instructions for option.
|
|
std::string help;
|
|
// 0 or 1.
|
|
bool isRequired;
|
|
// A list of flags that denote this option, i.e. -d, --dimension.
|
|
std::vector< std::string* > flags;
|
|
// If was set (or found).
|
|
bool isSet;
|
|
// Lists of arguments, per flag instance, after splitting by delimiter.
|
|
std::vector< std::vector< std::string* > * > args;
|
|
// Index where each group was parsed from input stream to track order.
|
|
std::vector<int> parseIndex;
|
|
};
|
|
/* ################################################################### */
|
|
void OptionGroup::clearArgs() {
|
|
int i,j;
|
|
for(i=0; i < (long int)args.size(); ++i) {
|
|
for(j=0; j < (long int)args[i]->size(); ++j)
|
|
delete args[i]->at(j);
|
|
|
|
delete args[i];
|
|
}
|
|
|
|
args.clear();
|
|
isSet = false;
|
|
};
|
|
/* ################################################################### */
|
|
void OptionGroup::getInt(int & out) {
|
|
if (!isSet) {
|
|
if (defaults.empty())
|
|
out = 0;
|
|
else
|
|
out = atoi(defaults.c_str());
|
|
} else {
|
|
if (args.empty() || args[0]->empty())
|
|
out = 0;
|
|
else {
|
|
out = atoi(args[0]->at(0)->c_str());
|
|
}
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void OptionGroup::getLong(long & out) {
|
|
if (!isSet) {
|
|
if (defaults.empty())
|
|
out = 0;
|
|
else
|
|
out = atoi(defaults.c_str());
|
|
} else {
|
|
if (args.empty() || args[0]->empty())
|
|
out = 0;
|
|
else {
|
|
out = atol(args[0]->at(0)->c_str());
|
|
}
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void OptionGroup::getLongLong(long long & out) {
|
|
if (!isSet) {
|
|
if (defaults.empty())
|
|
out = 0;
|
|
else {
|
|
std::stringstream ss(defaults.c_str());
|
|
ss >> out;
|
|
}
|
|
} else {
|
|
if (args.empty() || args[0]->empty())
|
|
out = 0;
|
|
else {
|
|
std::stringstream ss(args[0]->at(0)->c_str());
|
|
ss >> out;
|
|
}
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void OptionGroup::getULong(unsigned long & out) {
|
|
if (!isSet) {
|
|
if (defaults.empty())
|
|
out = 0;
|
|
else
|
|
out = atoi(defaults.c_str());
|
|
} else {
|
|
if (args.empty() || args[0]->empty())
|
|
out = 0;
|
|
else {
|
|
out = strtoul(args[0]->at(0)->c_str(),0,0);
|
|
}
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void OptionGroup::getULongLong(unsigned long long & out) {
|
|
if (!isSet) {
|
|
if (defaults.empty())
|
|
out = 0;
|
|
else {
|
|
std::stringstream ss(defaults.c_str());
|
|
ss >> out;
|
|
}
|
|
} else {
|
|
if (args.empty() || args[0]->empty())
|
|
out = 0;
|
|
else {
|
|
std::stringstream ss(args[0]->at(0)->c_str());
|
|
ss >> out;
|
|
}
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void OptionGroup::getFloat(float & out) {
|
|
if (!isSet) {
|
|
if (defaults.empty())
|
|
out = 0.0;
|
|
else
|
|
out = (float)atof(defaults.c_str());
|
|
} else {
|
|
if (args.empty() || args[0]->empty())
|
|
out = 0.0;
|
|
else {
|
|
out = (float)atof(args[0]->at(0)->c_str());
|
|
}
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void OptionGroup::getDouble(double & out) {
|
|
if (!isSet) {
|
|
if (defaults.empty())
|
|
out = 0.0;
|
|
else
|
|
out = atof(defaults.c_str());
|
|
} else {
|
|
if (args.empty() || args[0]->empty())
|
|
out = 0.0;
|
|
else {
|
|
out = atof(args[0]->at(0)->c_str());
|
|
}
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void OptionGroup::getString(std::string & out) {
|
|
if (!isSet) {
|
|
out = defaults;
|
|
} else {
|
|
if (args.empty() || args[0]->empty())
|
|
out = "";
|
|
else {
|
|
out = *args[0]->at(0);
|
|
}
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void OptionGroup::getInts(std::vector<int> & out) {
|
|
if (!isSet) {
|
|
if (!defaults.empty()) {
|
|
std::vector< std::string > strings;
|
|
SplitDelim(defaults, delim, strings);
|
|
StringsToInts(strings, out);
|
|
}
|
|
} else {
|
|
if (!(args.empty() || args[0]->empty()))
|
|
StringsToInts(args[0], &out);
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void OptionGroup::getLongs(std::vector<long> & out) {
|
|
if (!isSet) {
|
|
if (!defaults.empty()) {
|
|
std::vector< std::string > strings;
|
|
SplitDelim(defaults, delim, strings);
|
|
StringsToLongs(strings, out);
|
|
}
|
|
} else {
|
|
if (!(args.empty() || args[0]->empty()))
|
|
StringsToLongs(args[0], &out);
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void OptionGroup::getULongs(std::vector<unsigned long> & out) {
|
|
if (!isSet) {
|
|
if (!defaults.empty()) {
|
|
std::vector< std::string > strings;
|
|
SplitDelim(defaults, delim, strings);
|
|
StringsToULongs(strings, out);
|
|
}
|
|
} else {
|
|
if (!(args.empty() || args[0]->empty()))
|
|
StringsToULongs(args[0], &out);
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void OptionGroup::getFloats(std::vector<float> & out) {
|
|
if (!isSet) {
|
|
if (!defaults.empty()) {
|
|
std::vector< std::string > strings;
|
|
SplitDelim(defaults, delim, strings);
|
|
StringsToFloats(strings, out);
|
|
}
|
|
} else {
|
|
if (!(args.empty() || args[0]->empty()))
|
|
StringsToFloats(args[0], &out);
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void OptionGroup::getDoubles(std::vector<double> & out) {
|
|
if (!isSet) {
|
|
if (!defaults.empty()) {
|
|
std::vector< std::string > strings;
|
|
SplitDelim(defaults, delim, strings);
|
|
StringsToDoubles(strings, out);
|
|
}
|
|
} else {
|
|
if (!(args.empty() || args[0]->empty()))
|
|
StringsToDoubles(args[0], &out);
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void OptionGroup::getStrings(std::vector<std::string>& out) {
|
|
if (!isSet) {
|
|
if (!defaults.empty()) {
|
|
SplitDelim(defaults, delim, out);
|
|
}
|
|
} else {
|
|
if (!(args.empty() || args[0]->empty()))
|
|
StringsToStrings(args[0], &out);
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void OptionGroup::getMultiInts(std::vector< std::vector<int> >& out) {
|
|
if (!isSet) {
|
|
if (!defaults.empty()) {
|
|
std::vector< std::string > strings;
|
|
SplitDelim(defaults, delim, strings);
|
|
if (out.size() < 1) out.resize(1);
|
|
StringsToInts(strings, out[0]);
|
|
}
|
|
} else {
|
|
if (!args.empty()) {
|
|
int n = args.size();
|
|
if ((long int)out.size() < n) out.resize(n);
|
|
for(int i=0; i < n; ++i) {
|
|
StringsToInts(args[i], &out[i]);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void OptionGroup::getMultiLongs(std::vector< std::vector<long> >& out) {
|
|
if (!isSet) {
|
|
if (!defaults.empty()) {
|
|
std::vector< std::string > strings;
|
|
SplitDelim(defaults, delim, strings);
|
|
if (out.size() < 1) out.resize(1);
|
|
StringsToLongs(strings, out[0]);
|
|
}
|
|
} else {
|
|
if (!args.empty()) {
|
|
int n = args.size();
|
|
if ((long int)out.size() < n) out.resize(n);
|
|
for(int i=0; i < n; ++i) {
|
|
StringsToLongs(args[i], &out[i]);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void OptionGroup::getMultiULongs(std::vector< std::vector<unsigned long> >& out) {
|
|
if (!isSet) {
|
|
if (!defaults.empty()) {
|
|
std::vector< std::string > strings;
|
|
SplitDelim(defaults, delim, strings);
|
|
if (out.size() < 1) out.resize(1);
|
|
StringsToULongs(strings, out[0]);
|
|
}
|
|
} else {
|
|
if (!args.empty()) {
|
|
int n = args.size();
|
|
if ((long int)out.size() < n) out.resize(n);
|
|
for(int i=0; i < n; ++i) {
|
|
StringsToULongs(args[i], &out[i]);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void OptionGroup::getMultiFloats(std::vector< std::vector<float> >& out) {
|
|
if (!isSet) {
|
|
if (!defaults.empty()) {
|
|
std::vector< std::string > strings;
|
|
SplitDelim(defaults, delim, strings);
|
|
if (out.size() < 1) out.resize(1);
|
|
StringsToFloats(strings, out[0]);
|
|
}
|
|
} else {
|
|
if (!args.empty()) {
|
|
int n = args.size();
|
|
if ((long int)out.size() < n) out.resize(n);
|
|
for(int i=0; i < n; ++i) {
|
|
StringsToFloats(args[i], &out[i]);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void OptionGroup::getMultiDoubles(std::vector< std::vector<double> >& out) {
|
|
if (!isSet) {
|
|
if (!defaults.empty()) {
|
|
std::vector< std::string > strings;
|
|
SplitDelim(defaults, delim, strings);
|
|
if (out.size() < 1) out.resize(1);
|
|
StringsToDoubles(strings, out[0]);
|
|
}
|
|
} else {
|
|
if (!args.empty()) {
|
|
int n = args.size();
|
|
if ((long int)out.size() < n) out.resize(n);
|
|
for(int i=0; i < n; ++i) {
|
|
StringsToDoubles(args[i], &out[i]);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void OptionGroup::getMultiStrings(std::vector< std::vector<std::string> >& out) {
|
|
if (!isSet) {
|
|
if (!defaults.empty()) {
|
|
std::vector< std::string > strings;
|
|
SplitDelim(defaults, delim, strings);
|
|
if (out.size() < 1) out.resize(1);
|
|
out[0] = strings;
|
|
}
|
|
} else {
|
|
if (!args.empty()) {
|
|
int n = args.size();
|
|
if ((long int)out.size() < n) out.resize(n);
|
|
|
|
for(int i=0; i < n; ++i) {
|
|
for(int j=0; j < (long int)args[i]->size(); ++j)
|
|
out[i].push_back( *args[i]->at(j) );
|
|
}
|
|
}
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
typedef std::map< int, ezOptionValidator* > ValidatorMap;
|
|
|
|
class ezOptionParser {
|
|
public:
|
|
// How to layout usage descriptions with the option flags.
|
|
enum Layout { ALIGN, INTERLEAVE, STAGGER };
|
|
|
|
inline ~ezOptionParser();
|
|
|
|
inline void add(const char * defaults, bool required, int expectArgs, char delim, const char * help, const char * flag1, ezOptionValidator* validator=0);
|
|
inline void add(const char * defaults, bool required, int expectArgs, char delim, const char * help, const char * flag1, const char * flag2, ezOptionValidator* validator=0);
|
|
inline void add(const char * defaults, bool required, int expectArgs, char delim, const char * help, const char * flag1, const char * flag2, const char * flag3, ezOptionValidator* validator=0);
|
|
inline void add(const char * defaults, bool required, int expectArgs, char delim, const char * help, const char * flag1, const char * flag2, const char * flag3, const char * flag4, ezOptionValidator* validator=0);
|
|
inline bool exportFile(const char * filename, bool all=false);
|
|
inline OptionGroup * get(const char * name);
|
|
inline void getUsage(std::string & usage, int width=80, Layout layout=ALIGN);
|
|
inline void getUsageDescriptions(std::string & usage, int width=80, Layout layout=STAGGER);
|
|
inline bool gotExpected(std::vector<std::string> & badOptions);
|
|
inline bool gotRequired(std::vector<std::string> & badOptions);
|
|
inline bool gotValid(std::vector<std::string> & badOptions, std::vector<std::string> & badArgs);
|
|
inline bool importFile(const char * filename, char comment='#');
|
|
inline int isSet(const char * name);
|
|
inline int isSet(std::string & name);
|
|
inline void parse(int argc, const char * argv[]);
|
|
inline void prettyPrint(std::string & out);
|
|
inline void reset();
|
|
inline void resetArgs();
|
|
|
|
// Insert extra empty line betwee each option's usage description.
|
|
char doublespace;
|
|
// General description in human language on what the user's tool does.
|
|
// It's the first section to get printed in the full usage message.
|
|
std::string overview;
|
|
// A synopsis of command and options usage to show expected order of input arguments.
|
|
// It's the second section to get printed in the full usage message.
|
|
std::string syntax;
|
|
// Example (third) section in usage message.
|
|
std::string example;
|
|
// Final section printed in usage message. For contact, copyrights, version info.
|
|
std::string footer;
|
|
// Map from an option to an Id of its parent group.
|
|
std::map< std::string, int > optionGroupIds;
|
|
// Unordered collection of the option groups.
|
|
std::vector< OptionGroup* > groups;
|
|
// Store unexpected args in input.
|
|
std::vector< std::string* > unknownArgs;
|
|
// List of args that occur left-most before first option flag.
|
|
std::vector< std::string* > firstArgs;
|
|
// List of args that occur after last right-most option flag and its args.
|
|
std::vector< std::string* > lastArgs;
|
|
// List of validators.
|
|
ValidatorMap validators;
|
|
// Maps group id to a validator index into vector of validators. Validator index is -1 if there is no validator for group.
|
|
std::map< int, int > groupValidators;
|
|
};
|
|
/* ################################################################### */
|
|
ezOptionParser::~ezOptionParser() {
|
|
reset();
|
|
}
|
|
/* ################################################################### */
|
|
void ezOptionParser::reset() {
|
|
this->doublespace = 1;
|
|
|
|
int i;
|
|
for(i=0; i < (long int)groups.size(); ++i)
|
|
delete groups[i];
|
|
groups.clear();
|
|
|
|
for(i=0; i < (long int)unknownArgs.size(); ++i)
|
|
delete unknownArgs[i];
|
|
unknownArgs.clear();
|
|
|
|
for(i=0; i < (long int)firstArgs.size(); ++i)
|
|
delete firstArgs[i];
|
|
firstArgs.clear();
|
|
|
|
for(i=0; i < (long int)lastArgs.size(); ++i)
|
|
delete lastArgs[i];
|
|
lastArgs.clear();
|
|
|
|
ValidatorMap::iterator it;
|
|
for(it = validators.begin(); it != validators.end(); ++it)
|
|
delete it->second;
|
|
|
|
validators.clear();
|
|
optionGroupIds.clear();
|
|
groupValidators.clear();
|
|
};
|
|
/* ################################################################### */
|
|
void ezOptionParser::resetArgs() {
|
|
int i;
|
|
for(i=0; i < (long int)groups.size(); ++i)
|
|
groups[i]->clearArgs();
|
|
|
|
for(i=0; i < (long int)unknownArgs.size(); ++i)
|
|
delete unknownArgs[i];
|
|
unknownArgs.clear();
|
|
|
|
for(i=0; i < (long int)firstArgs.size(); ++i)
|
|
delete firstArgs[i];
|
|
firstArgs.clear();
|
|
|
|
for(i=0; i < (long int)lastArgs.size(); ++i)
|
|
delete lastArgs[i];
|
|
lastArgs.clear();
|
|
};
|
|
/* ################################################################### */
|
|
void ezOptionParser::add(const char * defaults, bool required, int expectArgs, char delim, const char * help, const char * flag1, ezOptionValidator* validator) {
|
|
int id = this->groups.size();
|
|
OptionGroup * g = new OptionGroup;
|
|
g->defaults = defaults;
|
|
g->isRequired = required;
|
|
g->expectArgs = expectArgs;
|
|
g->delim = delim;
|
|
g->isSet = 0;
|
|
g->help = help;
|
|
std::string *f1 = new std::string(flag1);
|
|
g->flags.push_back( f1 );
|
|
this->optionGroupIds[flag1] = id;
|
|
this->groups.push_back(g);
|
|
|
|
if (validator) {
|
|
int vid = validator->id;
|
|
validators[vid] = validator;
|
|
groupValidators[id] = vid;
|
|
} else {
|
|
groupValidators[id] = -1;
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void ezOptionParser::add(const char * defaults, bool required, int expectArgs, char delim, const char * help, const char * flag1, const char * flag2, ezOptionValidator* validator) {
|
|
int id = this->groups.size();
|
|
OptionGroup * g = new OptionGroup;
|
|
g->defaults = defaults;
|
|
g->isRequired = required;
|
|
g->expectArgs = expectArgs;
|
|
g->delim = delim;
|
|
g->isSet = 0;
|
|
g->help = help;
|
|
std::string *f1 = new std::string(flag1);
|
|
g->flags.push_back( f1 );
|
|
std::string *f2 = new std::string(flag2);
|
|
g->flags.push_back( f2 );
|
|
this->optionGroupIds[flag1] = id;
|
|
this->optionGroupIds[flag2] = id;
|
|
|
|
this->groups.push_back(g);
|
|
|
|
if (validator) {
|
|
int vid = validator->id;
|
|
validators[vid] = validator;
|
|
groupValidators[id] = vid;
|
|
} else {
|
|
groupValidators[id] = -1;
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void ezOptionParser::add(const char * defaults, bool required, int expectArgs, char delim, const char * help, const char * flag1, const char * flag2, const char * flag3, ezOptionValidator* validator) {
|
|
int id = this->groups.size();
|
|
OptionGroup * g = new OptionGroup;
|
|
g->defaults = defaults;
|
|
g->isRequired = required;
|
|
g->expectArgs = expectArgs;
|
|
g->delim = delim;
|
|
g->isSet = 0;
|
|
g->help = help;
|
|
std::string *f1 = new std::string(flag1);
|
|
g->flags.push_back( f1 );
|
|
std::string *f2 = new std::string(flag2);
|
|
g->flags.push_back( f2 );
|
|
std::string *f3 = new std::string(flag3);
|
|
g->flags.push_back( f3 );
|
|
this->optionGroupIds[flag1] = id;
|
|
this->optionGroupIds[flag2] = id;
|
|
this->optionGroupIds[flag3] = id;
|
|
|
|
this->groups.push_back(g);
|
|
|
|
if (validator) {
|
|
int vid = validator->id;
|
|
validators[vid] = validator;
|
|
groupValidators[id] = vid;
|
|
} else {
|
|
groupValidators[id] = -1;
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void ezOptionParser::add(const char * defaults, bool required, int expectArgs, char delim, const char * help, const char * flag1, const char * flag2, const char * flag3, const char * flag4, ezOptionValidator* validator) {
|
|
int id = this->groups.size();
|
|
OptionGroup * g = new OptionGroup;
|
|
g->defaults = defaults;
|
|
g->isRequired = required;
|
|
g->expectArgs = expectArgs;
|
|
g->delim = delim;
|
|
g->isSet = 0;
|
|
g->help = help;
|
|
std::string *f1 = new std::string(flag1);
|
|
g->flags.push_back( f1 );
|
|
std::string *f2 = new std::string(flag2);
|
|
g->flags.push_back( f2 );
|
|
std::string *f3 = new std::string(flag3);
|
|
g->flags.push_back( f3 );
|
|
std::string *f4 = new std::string(flag4);
|
|
g->flags.push_back( f4 );
|
|
this->optionGroupIds[flag1] = id;
|
|
this->optionGroupIds[flag2] = id;
|
|
this->optionGroupIds[flag3] = id;
|
|
this->optionGroupIds[flag4] = id;
|
|
|
|
this->groups.push_back(g);
|
|
|
|
if (validator) {
|
|
int vid = validator->id;
|
|
validators[vid] = validator;
|
|
groupValidators[id] = vid;
|
|
} else {
|
|
groupValidators[id] = -1;
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
bool ezOptionParser::exportFile(const char * filename, bool all) {
|
|
int i;
|
|
std::string out;
|
|
bool quote;
|
|
|
|
// Export the first args, except the program name, so start from 1.
|
|
for(i=1; i < (long int)firstArgs.size(); ++i) {
|
|
quote = ((firstArgs[i]->find_first_of(" \t") != std::string::npos) && (firstArgs[i]->find_first_of("\'\"") == std::string::npos));
|
|
|
|
if (quote)
|
|
out.append("\"");
|
|
|
|
out.append(*firstArgs[i]);
|
|
if (quote)
|
|
out.append("\"");
|
|
|
|
out.append(" ");
|
|
}
|
|
|
|
if (firstArgs.size() > 1)
|
|
out.append("\n");
|
|
|
|
std::vector<std::string* > stringPtrs(groups.size());
|
|
int m;
|
|
int n = groups.size();
|
|
for(i=0; i < n; ++i) {
|
|
stringPtrs[i] = groups[i]->flags[0];
|
|
}
|
|
|
|
OptionGroup *g;
|
|
// Sort first flag of each group with other groups.
|
|
std::sort(stringPtrs.begin(), stringPtrs.end(), CmpOptStringPtr);
|
|
for(i=0; i < n; ++i) {
|
|
g = get(stringPtrs[i]->c_str());
|
|
if (g->isSet || all) {
|
|
if (!g->isSet || g->args.empty()) {
|
|
if (!g->defaults.empty()) {
|
|
out.append(*stringPtrs[i]);
|
|
out.append(" ");
|
|
quote = ((g->defaults.find_first_of(" \t") != std::string::npos) && (g->defaults.find_first_of("\'\"") == std::string::npos));
|
|
if (quote)
|
|
out.append("\"");
|
|
|
|
out.append(g->defaults);
|
|
if (quote)
|
|
out.append("\"");
|
|
|
|
out.append("\n");
|
|
}
|
|
} else {
|
|
int n = g->args.size();
|
|
for(int j=0; j < n; ++j) {
|
|
out.append(*stringPtrs[i]);
|
|
out.append(" ");
|
|
m = g->args[j]->size();
|
|
|
|
for(int k=0; k < m; ++k) {
|
|
quote = ( (*g->args[j]->at(k)).find_first_of(" \t") != std::string::npos );
|
|
if (quote)
|
|
out.append("\"");
|
|
|
|
out.append(*g->args[j]->at(k));
|
|
if (quote)
|
|
out.append("\"");
|
|
|
|
if ((g->delim) && ((k+1) != m))
|
|
out.append(1,g->delim);
|
|
}
|
|
out.append("\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Export the last args.
|
|
for(i=0; i < (long int)lastArgs.size(); ++i) {
|
|
quote = ( lastArgs[i]->find_first_of(" \t") != std::string::npos );
|
|
if (quote)
|
|
out.append("\"");
|
|
|
|
out.append(*lastArgs[i]);
|
|
if (quote)
|
|
out.append("\"");
|
|
|
|
out.append(" ");
|
|
}
|
|
|
|
std::ofstream file(filename);
|
|
if (!file.is_open())
|
|
return false;
|
|
|
|
file << out;
|
|
file.close();
|
|
|
|
return true;
|
|
};
|
|
/* ################################################################### */
|
|
// Does not overwrite current options.
|
|
// Returns true if file was read successfully.
|
|
// So if this is used before parsing CLI, then option values will reflect
|
|
// this file, but if used after parsing CLI, then values will contain
|
|
// both CLI values and file's values.
|
|
//
|
|
// Comment lines are allowed if prefixed with #.
|
|
// Strings should be quoted as usual.
|
|
bool ezOptionParser::importFile(const char * filename, char comment) {
|
|
std::ifstream file (filename, std::ios::in | std::ios::ate);
|
|
if (!file.is_open())
|
|
return false;
|
|
|
|
// Read entire file contents.
|
|
std::ifstream::pos_type size = file.tellg();
|
|
char * memblock = new char[(int)size+1]; // Add one for end of string.
|
|
file.seekg (0, std::ios::beg);
|
|
file.read (memblock, size);
|
|
memblock[size] = '\0';
|
|
file.close();
|
|
|
|
// Find comment lines.
|
|
std::list<std::string*> lines;
|
|
std::string memblockstring(memblock);
|
|
delete[] memblock;
|
|
SplitDelim(memblockstring, '\n', lines);
|
|
int i,j,n;
|
|
std::list<std::string*>::iterator iter;
|
|
std::vector<int> sq, dq; // Single and double quote indices.
|
|
std::vector<int>::iterator lo; // For searching quote indices.
|
|
size_t pos;
|
|
const char *str;
|
|
std::string *line;
|
|
// Find all single and double quotes to correctly handle comment tokens.
|
|
for(iter=lines.begin(); iter != lines.end(); ++iter) {
|
|
line = *iter;
|
|
str = line->c_str();
|
|
n = line->size();
|
|
sq.clear();
|
|
dq.clear();
|
|
if (n) {
|
|
// If first char is comment, then erase line and continue.
|
|
pos = line->find_first_not_of(" \t\r");
|
|
if ((pos==std::string::npos) || (line->at(pos)==comment)) {
|
|
line->erase();
|
|
continue;
|
|
} else {
|
|
// Erase whitespace prefix.
|
|
line->erase(0,pos);
|
|
n = line->size();
|
|
}
|
|
|
|
if (line->at(0)=='"')
|
|
dq.push_back(0);
|
|
|
|
if (line->at(0)=='\'')
|
|
sq.push_back(0);
|
|
} else { // Empty line.
|
|
continue;
|
|
}
|
|
|
|
for(i=1; i < n; ++i) {
|
|
if ( (str[i]=='"') && (str[i-1]!='\\') )
|
|
dq.push_back(i);
|
|
else if ( (str[i]=='\'') && (str[i-1]!='\\') )
|
|
sq.push_back(i);
|
|
}
|
|
// Scan for comments, and when found, check bounds of quotes.
|
|
// Start with second char because already checked first char.
|
|
for(i=1; i < n; ++i) {
|
|
if ( (line->at(i)==comment) && (line->at(i-1)!='\\') ) {
|
|
// If within open/close quote pair, then not real comment.
|
|
if (sq.size()) {
|
|
lo = std::lower_bound(sq.begin(), sq.end(), i);
|
|
// All start of strings will be even indices, closing quotes is odd indices.
|
|
j = (int)(lo-sq.begin());
|
|
if ( (j % 2) == 0) { // Even implies comment char not in quote pair.
|
|
// Erase from comment char to end of line.
|
|
line->erase(i);
|
|
break;
|
|
}
|
|
} else if (dq.size()) {
|
|
// Repeat tests for double quotes.
|
|
lo = std::lower_bound(dq.begin(), dq.end(), i);
|
|
j = (int)(lo-dq.begin());
|
|
if ( (j % 2) == 0) {
|
|
line->erase(i);
|
|
break;
|
|
}
|
|
} else {
|
|
// Not in quotes.
|
|
line->erase(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string cmd;
|
|
// Convert list to string without newlines to simulate commandline.
|
|
for(iter=lines.begin(); iter != lines.end(); ++iter) {
|
|
if (! (*iter)->empty()) {
|
|
cmd.append(**iter);
|
|
cmd.append(" ");
|
|
}
|
|
}
|
|
|
|
// Now parse as if from command line.
|
|
int argc=0;
|
|
char** argv = CommandLineToArgvA((char*)cmd.c_str(), &argc);
|
|
|
|
// Parse.
|
|
parse(argc, (const char**)argv);
|
|
if (argv) free(argv);
|
|
for(iter=lines.begin(); iter != lines.end(); ++iter)
|
|
delete *iter;
|
|
|
|
return true;
|
|
};
|
|
/* ################################################################### */
|
|
int ezOptionParser::isSet(const char * name) {
|
|
std::string sname(name);
|
|
|
|
if (this->optionGroupIds.count(sname)) {
|
|
return this->groups[this->optionGroupIds[sname]]->isSet;
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
/* ################################################################### */
|
|
int ezOptionParser::isSet(std::string & name) {
|
|
if (this->optionGroupIds.count(name)) {
|
|
return this->groups[this->optionGroupIds[name]]->isSet;
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
/* ################################################################### */
|
|
OptionGroup * ezOptionParser::get(const char * name) {
|
|
if (optionGroupIds.count(name)) {
|
|
return groups[optionGroupIds[name]];
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
/* ################################################################### */
|
|
void ezOptionParser::getUsage(std::string & usage, int width, Layout layout) {
|
|
|
|
usage.append(overview);
|
|
usage.append("\n\n");
|
|
usage.append("USAGE: ");
|
|
usage.append(syntax);
|
|
usage.append("\n\nOPTIONS:\n\n");
|
|
getUsageDescriptions(usage, width, layout);
|
|
|
|
if (!example.empty()) {
|
|
usage.append("EXAMPLES:\n\n");
|
|
usage.append(example);
|
|
}
|
|
|
|
if (!footer.empty()) {
|
|
usage.append(footer);
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
// Creates 2 column formatted help descriptions for each option flag.
|
|
void ezOptionParser::getUsageDescriptions(std::string & usage, int width, Layout layout) {
|
|
// Sort each flag list amongst each group.
|
|
int i;
|
|
// Store index of flag groups before sort for easy lookup later.
|
|
std::map<std::string*, int> stringPtrToIndexMap;
|
|
std::vector<std::string* > stringPtrs(groups.size());
|
|
|
|
for(i=0; i < (long int)groups.size(); ++i) {
|
|
std::sort(groups[i]->flags.begin(), groups[i]->flags.end(), CmpOptStringPtr);
|
|
stringPtrToIndexMap[groups[i]->flags[0]] = i;
|
|
stringPtrs[i] = groups[i]->flags[0];
|
|
}
|
|
|
|
size_t j, k;
|
|
std::string opts;
|
|
std::vector<std::string> sortedOpts;
|
|
// Sort first flag of each group with other groups.
|
|
std::sort(stringPtrs.begin(), stringPtrs.end(), CmpOptStringPtr);
|
|
for(i=0; i < (long int)groups.size(); ++i) {
|
|
//printf("DEBUG:%d: %d %d %s\n", __LINE__, i, stringPtrToIndexMap[stringPtrs[i]], stringPtrs[i]->c_str());
|
|
k = stringPtrToIndexMap[stringPtrs[i]];
|
|
opts.clear();
|
|
for(j=0; j < groups[k]->flags.size()-1; ++j) {
|
|
opts.append(*groups[k]->flags[j]);
|
|
opts.append(", ");
|
|
|
|
if ((long int)opts.size() > width)
|
|
opts.append("\n");
|
|
}
|
|
// The last flag. No need to append comma anymore.
|
|
opts.append( *groups[k]->flags[j] );
|
|
|
|
if (groups[k]->expectArgs) {
|
|
opts.append(" ARG");
|
|
|
|
if (groups[k]->delim) {
|
|
opts.append("1[");
|
|
opts.append(1, groups[k]->delim);
|
|
opts.append("ARGn]");
|
|
}
|
|
}
|
|
|
|
sortedOpts.push_back(opts);
|
|
}
|
|
|
|
// Each option group will use this to build multiline help description.
|
|
std::list<std::string*> desc;
|
|
// Number of whitespaces from start of line to description (interleave layout) or
|
|
// gap between flag names and description (align, stagger layouts).
|
|
int gutter = 3;
|
|
|
|
// Find longest opt flag string to set column start for help usage descriptions.
|
|
int maxlen=0;
|
|
if (layout == ALIGN) {
|
|
for(i=0; i < (long int)groups.size(); ++i) {
|
|
if (maxlen < (long int)sortedOpts[i].size())
|
|
maxlen = sortedOpts[i].size();
|
|
}
|
|
}
|
|
|
|
// The amount of space remaining on a line for help text after flags.
|
|
int helpwidth;
|
|
std::list<std::string*>::iterator cIter, insertionIter;
|
|
size_t pos;
|
|
for(i=0; i < (long int)groups.size(); ++i) {
|
|
k = stringPtrToIndexMap[stringPtrs[i]];
|
|
|
|
if (layout == STAGGER)
|
|
maxlen = sortedOpts[i].size();
|
|
|
|
int pad = gutter + maxlen;
|
|
helpwidth = width - pad;
|
|
|
|
// All the following split-fu could be optimized by just using substring (offset, length) tuples, but just to get it done, we'll do some not-too expensive string copying.
|
|
SplitDelim(groups[k]->help, '\n', desc);
|
|
// Split lines longer than allowable help width.
|
|
for(insertionIter=desc.begin(), cIter=insertionIter++;
|
|
cIter != desc.end();
|
|
cIter=insertionIter++) {
|
|
if ((long int)((*cIter)->size()) > helpwidth) {
|
|
// Get pointer to next string to insert new strings before it.
|
|
std::string *rem = *cIter;
|
|
// Remove this line and add back in pieces.
|
|
desc.erase(cIter);
|
|
// Loop until remaining string is short enough.
|
|
while ((long int)rem->size() > helpwidth) {
|
|
// Find whitespace to split before helpwidth.
|
|
if (rem->at(helpwidth) == ' ') {
|
|
// If word ends exactly at helpwidth, then split after it.
|
|
pos = helpwidth;
|
|
} else {
|
|
// Otherwise, split occurs midword, so find whitespace before this word.
|
|
pos = rem->rfind(" ", helpwidth);
|
|
}
|
|
// Insert split string.
|
|
desc.insert(insertionIter, new std::string(*rem, 0, pos));
|
|
// Now skip any whitespace to start new line.
|
|
pos = rem->find_first_not_of(' ', pos);
|
|
rem->erase(0, pos);
|
|
}
|
|
|
|
if (rem->size())
|
|
desc.insert(insertionIter, rem);
|
|
else
|
|
delete rem;
|
|
}
|
|
}
|
|
|
|
usage.append(sortedOpts[i]);
|
|
if (layout != INTERLEAVE)
|
|
// Add whitespace between option names and description.
|
|
usage.append(pad - sortedOpts[i].size(), ' ');
|
|
else {
|
|
usage.append("\n");
|
|
usage.append(gutter, ' ');
|
|
}
|
|
|
|
if (desc.size() > 0) { // Crash fix by Bruce Shankle.
|
|
// First line already padded above (before calling SplitDelim) after option flag names.
|
|
cIter = desc.begin();
|
|
usage.append(**cIter);
|
|
usage.append("\n");
|
|
// Now inject the pad for each line.
|
|
for(++cIter; cIter != desc.end(); ++cIter) {
|
|
usage.append(pad, ' ');
|
|
usage.append(**cIter);
|
|
usage.append("\n");
|
|
}
|
|
|
|
if (this->doublespace) usage.append("\n");
|
|
|
|
for(cIter=desc.begin(); cIter != desc.end(); ++cIter)
|
|
delete *cIter;
|
|
|
|
desc.clear();
|
|
}
|
|
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
bool ezOptionParser::gotExpected(std::vector<std::string> & badOptions) {
|
|
int i,j;
|
|
|
|
for(i=0; i < (long int)groups.size(); ++i) {
|
|
OptionGroup *g = groups[i];
|
|
// If was set, ensure number of args is correct.
|
|
if (g->isSet) {
|
|
if ((g->expectArgs != 0) && g->args.empty()) {
|
|
badOptions.push_back(*g->flags[0]);
|
|
continue;
|
|
}
|
|
|
|
for(j=0; j < (long int)g->args.size(); ++j) {
|
|
if ((g->expectArgs != -1) && (g->expectArgs != (long int)g->args[j]->size()))
|
|
badOptions.push_back(*g->flags[0]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return badOptions.empty();
|
|
};
|
|
/* ################################################################### */
|
|
bool ezOptionParser::gotRequired(std::vector<std::string> & badOptions) {
|
|
int i;
|
|
|
|
for(i=0; i < (long int)groups.size(); ++i) {
|
|
OptionGroup *g = groups[i];
|
|
// Simple case when required but user never set it.
|
|
if (g->isRequired && (!g->isSet)) {
|
|
badOptions.push_back(*g->flags[0]);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return badOptions.empty();
|
|
};
|
|
/* ################################################################### */
|
|
bool ezOptionParser::gotValid(std::vector<std::string> & badOptions, std::vector<std::string> & badArgs) {
|
|
int groupid, validatorid;
|
|
std::map< int, int >::iterator it;
|
|
|
|
for(it = groupValidators.begin(); it != groupValidators.end(); ++it) {
|
|
groupid = it->first;
|
|
validatorid = it->second;
|
|
if (validatorid < 0) continue;
|
|
|
|
OptionGroup *g = groups[groupid];
|
|
ezOptionValidator *v = validators[validatorid];
|
|
bool nextgroup = false;
|
|
|
|
for (int i = 0; i < (long int)g->args.size(); ++i) {
|
|
if (nextgroup) break;
|
|
std::vector< std::string* > * args = g->args[i];
|
|
for (int j = 0; j < (long int)args->size(); ++j) {
|
|
if (!v->isValid(args->at(j))) {
|
|
badOptions.push_back(*g->flags[0]);
|
|
badArgs.push_back(*args->at(j));
|
|
nextgroup = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return badOptions.empty();
|
|
};
|
|
/* ################################################################### */
|
|
void ezOptionParser::parse(int argc, const char * argv[]) {
|
|
if (argc < 1) return;
|
|
|
|
/*
|
|
std::map<std::string,int>::iterator it;
|
|
for ( it=optionGroupIds.begin() ; it != optionGroupIds.end(); it++ )
|
|
std::cout << (*it).first << " => " << (*it).second << std::endl;
|
|
*/
|
|
|
|
int i, k, firstOptIndex=0, lastOptIndex=0;
|
|
std::string s;
|
|
OptionGroup *g;
|
|
|
|
for(i=0; i < argc; ++i) {
|
|
s = argv[i];
|
|
|
|
if (optionGroupIds.count(s))
|
|
break;
|
|
}
|
|
|
|
firstOptIndex = i;
|
|
|
|
if (firstOptIndex == argc) {
|
|
// No flags encountered, so set last args.
|
|
this->firstArgs.push_back(new std::string(argv[0]));
|
|
|
|
for(k=1; k < argc; ++k)
|
|
this->lastArgs.push_back(new std::string(argv[k]));
|
|
|
|
return;
|
|
}
|
|
|
|
// Store initial args before opts appear.
|
|
for(k=0; k < i; ++k) {
|
|
this->firstArgs.push_back(new std::string(argv[k]));
|
|
}
|
|
|
|
for(; i < argc; ++i) {
|
|
s = argv[i];
|
|
|
|
if (optionGroupIds.count(s)) {
|
|
k = optionGroupIds[s];
|
|
g = groups[k];
|
|
g->isSet = 1;
|
|
g->parseIndex.push_back(i);
|
|
|
|
if (g->expectArgs) {
|
|
// Read ahead to get args.
|
|
++i;
|
|
if (i >= argc) return;
|
|
g->args.push_back(new std::vector<std::string*>);
|
|
SplitDelim(argv[i], g->delim, g->args.back());
|
|
}
|
|
lastOptIndex = i;
|
|
}
|
|
}
|
|
|
|
// Scan for unknown opts/arguments.
|
|
for(i=firstOptIndex; i <= lastOptIndex; ++i) {
|
|
s = argv[i];
|
|
|
|
if (optionGroupIds.count(s)) {
|
|
k = optionGroupIds[s];
|
|
g = groups[k];
|
|
if (g->expectArgs) {
|
|
// Read ahead for args and skip them.
|
|
++i;
|
|
}
|
|
} else {
|
|
unknownArgs.push_back(new std::string(argv[i]));
|
|
}
|
|
}
|
|
|
|
if ( lastOptIndex >= (argc-1) ) return;
|
|
|
|
// Store final args without flags.
|
|
for(k=lastOptIndex + 1; k < argc; ++k) {
|
|
this->lastArgs.push_back(new std::string(argv[k]));
|
|
}
|
|
};
|
|
/* ################################################################### */
|
|
void ezOptionParser::prettyPrint(std::string & out) {
|
|
char tmp[256];
|
|
int i,j,k;
|
|
|
|
out += "First Args:\n";
|
|
for(i=0; i < (long int)firstArgs.size(); ++i) {
|
|
sprintf(tmp, "%d: %s\n", i+1, firstArgs[i]->c_str());
|
|
out += tmp;
|
|
}
|
|
|
|
// Sort the option flag names.
|
|
int n = groups.size();
|
|
std::vector<std::string* > stringPtrs(n);
|
|
for(i=0; i < n; ++i) {
|
|
stringPtrs[i] = groups[i]->flags[0];
|
|
}
|
|
|
|
// Sort first flag of each group with other groups.
|
|
std::sort(stringPtrs.begin(), stringPtrs.end(), CmpOptStringPtr);
|
|
|
|
out += "\nOptions:\n";
|
|
OptionGroup *g;
|
|
for(i=0; i < n; ++i) {
|
|
g = get(stringPtrs[i]->c_str());
|
|
out += "\n";
|
|
// The flag names:
|
|
for(j=0; j < (long int)g->flags.size()-1; ++j) {
|
|
sprintf(tmp, "%s, ", g->flags[j]->c_str());
|
|
out += tmp;
|
|
}
|
|
sprintf(tmp, "%s:\n", g->flags.back()->c_str());
|
|
out += tmp;
|
|
|
|
if (g->isSet) {
|
|
if (g->expectArgs) {
|
|
if (g->args.empty()) {
|
|
sprintf(tmp, "%s (default)\n", g->defaults.c_str());
|
|
out += tmp;
|
|
} else {
|
|
for(k=0; k < (long int)g->args.size(); ++k) {
|
|
for(j=0; j < (long int)g->args[k]->size()-1; ++j) {
|
|
sprintf(tmp, "%s%c", g->args[k]->at(j)->c_str(), g->delim);
|
|
out += tmp;
|
|
}
|
|
sprintf(tmp, "%s\n", g->args[k]->back()->c_str());
|
|
out += tmp;
|
|
}
|
|
}
|
|
} else { // Set but no args expected.
|
|
sprintf(tmp, "Set\n");
|
|
out += tmp;
|
|
}
|
|
} else {
|
|
sprintf(tmp, "Not set\n");
|
|
out += tmp;
|
|
}
|
|
}
|
|
|
|
out += "\nLast Args:\n";
|
|
for(i=0; i < (long int)lastArgs.size(); ++i) {
|
|
sprintf(tmp, "%d: %s\n", i+1, lastArgs[i]->c_str());
|
|
out += tmp;
|
|
}
|
|
|
|
out += "\nUnknown Args:\n";
|
|
for(i=0; i < (long int)unknownArgs.size(); ++i) {
|
|
sprintf(tmp, "%d: %s\n", i+1, unknownArgs[i]->c_str());
|
|
out += tmp;
|
|
}
|
|
};
|
|
}
|
|
/* ################################################################### */
|
|
#endif /* EZ_OPTION_PARSER_H */
|