1 |
16 |
gryzor |
/*
|
2 |
|
|
(c) Jose Tejada Gomez, 9th May 2013
|
3 |
|
|
You can use this file following the GNU GENERAL PUBLIC LICENSE version 3
|
4 |
|
|
Read the details of the license in:
|
5 |
|
|
http://www.gnu.org/licenses/gpl.txt
|
6 |
|
|
|
7 |
|
|
Send comments to: jose.tejada at ieee.org
|
8 |
|
|
|
9 |
|
|
*/
|
10 |
|
|
|
11 |
|
|
#include <string>
|
12 |
|
|
#include <vector>
|
13 |
|
|
#include <algorithm>
|
14 |
|
|
#include <cstdlib>
|
15 |
|
|
#include <iostream>
|
16 |
|
|
|
17 |
|
|
typedef std::vector<struct argument_t*> arg_vector_t;
|
18 |
|
|
|
19 |
|
|
struct argument_t {
|
20 |
23 |
gryzor |
protected:
|
21 |
|
|
int imin, imax;
|
22 |
|
|
float fmin, fmax;
|
23 |
|
|
bool limits;
|
24 |
|
|
template<typename A> bool pass( A a, A b, A x ) { return x>=a && x<=b; }
|
25 |
|
|
public:
|
26 |
16 |
gryzor |
std::string short_name, long_name;
|
27 |
17 |
gryzor |
typedef enum { integer, text, flag, real } arg_type;
|
28 |
16 |
gryzor |
arg_type type;
|
29 |
|
|
std::string description;
|
30 |
|
|
// possible states
|
31 |
|
|
int integer_value;
|
32 |
|
|
std::string string_value;
|
33 |
17 |
gryzor |
float real_value;
|
34 |
16 |
gryzor |
bool state, req;
|
35 |
23 |
gryzor |
|
36 |
|
|
bool parse_hex;
|
37 |
16 |
gryzor |
|
38 |
|
|
argument_t( arg_vector_t& av, const char* long_name, arg_type type,
|
39 |
17 |
gryzor |
const char* desc="", bool required=false )
|
40 |
16 |
gryzor |
: long_name(long_name), type( type ), description( desc ),
|
41 |
23 |
gryzor |
req(required), state(false), integer_value(0), real_value(0),
|
42 |
|
|
parse_hex(false), limits(false)
|
43 |
16 |
gryzor |
{
|
44 |
|
|
if( !this->long_name.empty() ) {
|
45 |
|
|
this->short_name = "-" + this->long_name.substr(0,1);
|
46 |
|
|
this->long_name = "--" + this->long_name;
|
47 |
|
|
}
|
48 |
|
|
av.push_back(this);
|
49 |
|
|
}
|
50 |
|
|
void set() { state=true; }
|
51 |
|
|
bool is_set() { return state; }
|
52 |
23 |
gryzor |
// range control
|
53 |
|
|
void setlimits( int min, int max ) {
|
54 |
|
|
if( type == integer ) {
|
55 |
|
|
imin = min;
|
56 |
|
|
imax = max;
|
57 |
|
|
limits = true;
|
58 |
|
|
}
|
59 |
|
|
else if( type == real ) {
|
60 |
|
|
fmin = (float) min;
|
61 |
|
|
fmax = (float) max;
|
62 |
|
|
limits = true;
|
63 |
|
|
}
|
64 |
|
|
else throw "Cannot apply integer limits to this type of input argument";
|
65 |
|
|
}
|
66 |
|
|
void setlimits( float min, float max ) {
|
67 |
|
|
if( type == real ) {
|
68 |
|
|
fmin = min;
|
69 |
|
|
fmax = max;
|
70 |
|
|
limits = true;
|
71 |
|
|
}
|
72 |
|
|
else throw "Cannot apply real limits to this type of input argument";
|
73 |
|
|
}
|
74 |
|
|
bool passlimits() {
|
75 |
|
|
if( !limits ) return true;
|
76 |
|
|
if( type == real ) return pass( fmin, fmax, real_value );
|
77 |
|
|
else if ( type == integer ) return pass( imin, imax, integer_value );
|
78 |
|
|
return false;
|
79 |
|
|
}
|
80 |
16 |
gryzor |
};
|
81 |
|
|
|
82 |
23 |
gryzor |
|
83 |
|
|
|
84 |
16 |
gryzor |
class Args {
|
85 |
|
|
arg_vector_t& legal_args;
|
86 |
|
|
argument_t* def_arg;
|
87 |
|
|
std::string program_name;
|
88 |
|
|
argument_t help_arg;
|
89 |
18 |
gryzor |
void clean_args() {
|
90 |
|
|
for( int j=0; j<legal_args.size(); j++ ) {
|
91 |
|
|
argument_t& a = *legal_args[j];
|
92 |
|
|
if( a.short_name=="-h" && a.long_name!="help" )
|
93 |
|
|
{ help_arg.short_name.clear(); break; } // remove -h for help if already used
|
94 |
|
|
}
|
95 |
|
|
}
|
96 |
|
|
void throw_error( std::string x ) /*throw const char**/ { throw x.c_str(); }
|
97 |
16 |
gryzor |
public:
|
98 |
|
|
Args( int argc, char *argv[], arg_vector_t& legal_args ) //throw const char *
|
99 |
|
|
: legal_args( legal_args ),
|
100 |
|
|
help_arg( legal_args, "help", argument_t::flag, "Display usage information")
|
101 |
18 |
gryzor |
{
|
102 |
|
|
clean_args(); // eliminate duplicated values
|
103 |
16 |
gryzor |
// look for default argument
|
104 |
|
|
def_arg=NULL;
|
105 |
|
|
for( int j=0; j<legal_args.size(); j++ ) {
|
106 |
|
|
if ( legal_args[j]->short_name.empty() && legal_args[j]->long_name.empty() )
|
107 |
|
|
if( def_arg==NULL ) def_arg = legal_args[j];
|
108 |
|
|
else throw "Cannot set more than one default argument.";
|
109 |
|
|
}
|
110 |
|
|
if( def_arg && def_arg->type!=argument_t::integer && def_arg->type!=argument_t::text )
|
111 |
23 |
gryzor |
throw "Default arguments can only be integer or text";
|
112 |
16 |
gryzor |
|
113 |
|
|
program_name = argv[0];
|
114 |
|
|
for( int k=1; k<argc; k++ ) {
|
115 |
|
|
bool matched=false;
|
116 |
|
|
for( int j=0; j<legal_args.size(); j++ ) {
|
117 |
|
|
argument_t& a = *legal_args[j];
|
118 |
|
|
if( a.long_name==argv[k] || a.short_name==argv[k] ) {
|
119 |
|
|
if( a.type == argument_t::flag ) { a.set(); matched=true; continue; }
|
120 |
|
|
if( a.type == argument_t::text ) {
|
121 |
|
|
k++;
|
122 |
|
|
if( k>=argc ) throw_error("Expecting input after "+a.long_name+" param");
|
123 |
|
|
a.string_value = argv[k];
|
124 |
|
|
a.set();
|
125 |
|
|
matched=true;
|
126 |
|
|
continue;
|
127 |
|
|
}
|
128 |
|
|
if( a.type == argument_t::integer ) {
|
129 |
|
|
k++;
|
130 |
|
|
if( k>=argc ) throw_error("Expecting input after "+a.long_name+" param");
|
131 |
23 |
gryzor |
if( !a.parse_hex )
|
132 |
|
|
a.integer_value = atoi(argv[k]);
|
133 |
|
|
else {
|
134 |
|
|
errno = 0;
|
135 |
|
|
a.integer_value = strtol( argv[k], NULL, 16 );
|
136 |
|
|
if( errno ) throw "Cannot parse hexadecimal argument";
|
137 |
|
|
}
|
138 |
|
|
if( !a.passlimits() ) throw_error("Argument "+a.long_name+" is not within its allowed range");
|
139 |
16 |
gryzor |
a.set();
|
140 |
|
|
matched=true;
|
141 |
|
|
continue;
|
142 |
|
|
}
|
143 |
17 |
gryzor |
if( a.type == argument_t::real ) {
|
144 |
|
|
k++;
|
145 |
|
|
if( k>=argc ) throw_error("Expecting input after "+a.long_name+" param");
|
146 |
|
|
a.real_value = atof(argv[k]);
|
147 |
23 |
gryzor |
if( !a.passlimits() ) throw_error("Argument "+a.long_name+" is not within its allowed range");
|
148 |
17 |
gryzor |
a.set();
|
149 |
|
|
matched=true;
|
150 |
|
|
continue;
|
151 |
|
|
}
|
152 |
16 |
gryzor |
}
|
153 |
|
|
}
|
154 |
|
|
if( !matched && def_arg!=NULL )
|
155 |
|
|
if( def_arg->state )
|
156 |
|
|
throw_error( "Unknown parameter " + std::string(argv[k] ));
|
157 |
|
|
else {
|
158 |
|
|
if( def_arg->type==argument_t::integer ) def_arg->integer_value=atoi(argv[k]);
|
159 |
|
|
if( def_arg->type==argument_t::text ) def_arg->string_value=argv[k];
|
160 |
|
|
def_arg->set();
|
161 |
|
|
}
|
162 |
|
|
}
|
163 |
|
|
if( help_arg.is_set() ) return; // do not perform more checks
|
164 |
|
|
// check that all required parameters are present
|
165 |
|
|
for( int j=0; j<legal_args.size(); j++ ) {
|
166 |
|
|
argument_t& a = *legal_args[j];
|
167 |
|
|
if( a.req && !a.state ) {
|
168 |
|
|
std::string pname;
|
169 |
|
|
if( !a.long_name.empty() ) pname=a.long_name;
|
170 |
|
|
else if( !a.short_name.empty() ) pname=a.short_name;
|
171 |
|
|
throw_error("Parameter "+pname+" is required.");
|
172 |
|
|
}
|
173 |
|
|
}
|
174 |
|
|
}
|
175 |
21 |
gryzor |
void check_ilegal_combinations( arg_vector_t& ilegal ) {
|
176 |
|
|
int count=0;
|
177 |
|
|
std::string names;
|
178 |
|
|
for( int k=0; k < ilegal.size(); k++ )
|
179 |
|
|
if( ilegal[k]->is_set() ) { count++; names += " " + ilegal[k]->long_name; }
|
180 |
|
|
if( count>1 ) throw_error( "Parameters" + names + " cannot be used together" );
|
181 |
|
|
}
|
182 |
17 |
gryzor |
bool help_request() { if( help_arg.is_set() ) show_help(); return help_arg.is_set(); }
|
183 |
16 |
gryzor |
std::string brackets( const argument_t& a, std::string s ) {
|
184 |
|
|
return a.req ? "<"+s+">" : "["+s+"]";
|
185 |
|
|
}
|
186 |
|
|
void show_help() {
|
187 |
|
|
std::cout << "Usage: " << program_name << " ";
|
188 |
|
|
if( def_arg!=NULL )
|
189 |
|
|
std::cout << brackets( *def_arg, def_arg->description.empty() ? "parameter " : def_arg->description);
|
190 |
|
|
std::cout << "\n";
|
191 |
|
|
for( int j=0; j<legal_args.size(); j++ ) {
|
192 |
|
|
argument_t& a = *legal_args[j];
|
193 |
|
|
if( a.long_name.empty() && a.long_name.empty() ) continue;
|
194 |
|
|
std::cout << "\t";
|
195 |
|
|
std::string aux;
|
196 |
|
|
if( !a.long_name.empty() ) aux = a.long_name;
|
197 |
18 |
gryzor |
if( !a.long_name.empty() && !a.short_name.empty() ) aux += " | ";
|
198 |
16 |
gryzor |
if( !a.short_name.empty() ) aux+= a.short_name;
|
199 |
|
|
std::cout << brackets( a, aux );
|
200 |
17 |
gryzor |
switch( a.type ) {
|
201 |
|
|
case argument_t::integer: std::cout << " followed by integer number. "; break;
|
202 |
|
|
case argument_t::real : std::cout << " followed by real number. "; break;
|
203 |
|
|
case argument_t::text : std::cout << " followed by string. "; break;
|
204 |
|
|
}
|
205 |
16 |
gryzor |
if( !a.description.empty() ) std::cout << ": " << a.description;
|
206 |
|
|
std::cout << "\n";
|
207 |
|
|
}
|
208 |
|
|
}
|
209 |
|
|
};
|