1 #include "configuration_manager.hh"
6 #define MAX_LINE_LENGTH 80
11 * This constructor makes a new configuration_manager ready to use.
12 *\arg name Name of the application as mentioned in the
13 * "Use: <appname> ..." help message.
15 configuration_manager::configuration_manager(string name
){
21 *\brief Add a new configuration value to be searched for.
22 *\param shortname A character for the short form.
23 * For example the o in -o
24 *\param longname The double dash longname. For example
25 * output_file in --output_file=
26 *\param description A detailed description of the value.
27 *\param status Pointer to an integer. Will be set to 1 if arg found.
28 *\target Pointer to string to put the value in.
29 *\placeholder A placeholder for the documentation.
30 * For example <filename> in -f<filename>
32 void configuration_manager::add_option_value (const string
& shortname
,
33 const string
& longname
,
34 const string
& description
,
37 const string
& placeholder
,
38 const bool & allow_cmdline
,
39 const bool & allow_conffile
42 if(status
!=NULL
) if (target
!=NULL
)
43 option_values
.insert(option_values
.end(),
44 opt_value_t(shortname
,
59 *\brief Add a new configuration switch to be searched for.
60 *\param shortname A character for the short form.
61 * For example the h in -h
62 *\param longname The double dash longname. For example
64 *\param description A detailed description of the value.
65 *\param status Pointer to an integer. Will be set to 1 if arg found.
66 *\param allow_cmdline Specifies whether the switch is acceptable on the
68 *\param allow_conffile Specifies whether the switch is acceptable
69 * in a configuration file.
71 void configuration_manager::add_option_switch (const string
& shortname
,
72 const string
& longname
,
73 const string
& description
,
75 const bool & allow_cmdline
,
76 const bool & allow_conffile
80 option_switches
.insert(option_switches
.end(),
81 opt_switch_t(shortname
,
91 *\brief Add an accepted argument to the argument reader.
92 *\param placeholder Something like "<input-file>".
93 *\param description Text describing the argument.
94 *\param status A pointer to a status variable. Will be set to 0 by default.
95 * If the argument is filled in, it will be set to 1.
96 *\param target A pointer to a c++ string where the argument goes to.
98 *\note Arguments are filled in the order of adding them to the
100 * There would be no other way to determine the order.
102 void configuration_manager::add_argument(const string
& description
,
105 const string
& placeholder
){
109 if (target
!=NULL
) if(status
!=NULL
)
110 cmd_args
.insert(cmd_args
.end(),cmd_arg_t(placeholder
,
118 *\Read in the args passed to main().
119 *\returns empty vector on success or the error messages to be output.
121 vector
<string
> configuration_manager::read_args(int argc
, char ** args
){
123 vector
<string
> messages
;
126 // First we make a C++ string vector out of the **argv.
127 for (char ** akt
=args
+1; *akt
;akt
++) argv
.insert(argv
.end(),string(*akt
));
129 // Counter for the free command line parameters.
130 unsigned int free_parms_count
=0;
132 // Loop through the command line arguments
133 for (unsigned int arg_no
=0;arg_no
<argv
.size();arg_no
++){
137 // Look for long parameters.
138 if ((argv
[arg_no
].substr(0,2)=="--")&&(free_parms_count
==0)){
139 argstring
=argv
[arg_no
].substr(2);
141 // Look for long switches.
142 for (unsigned int switch_no
=0;switch_no
<option_switches
.size();switch_no
++){
143 string switch_name
=option_switches
[switch_no
].longname
;
144 if (argstring
.compare(0,switch_name
.length(),switch_name
,0,switch_name
.length())==0){
145 string value
=analyse_string(argstring
);
146 bool result
=analyse_bool(value
);
147 if(option_switches
[switch_no
].allow_cmdline
==true){
148 *(option_switches
[switch_no
].status
)=result
;
150 messages
.insert(messages
.end(),"Switch \"--"
151 +option_switches
[switch_no
].longname
152 +"\" is not allowed on the command line!");
159 // Look for long values.
160 for (unsigned int value_no
=0;value_no
<option_values
.size();value_no
++){
161 string value_name
=option_values
[value_no
].longname
;
162 if (argstring
.compare(0,value_name
.length(),value_name
,0,value_name
.length())==0){
163 string value
=analyse_string(argstring
);
164 if(option_values
[value_no
].allow_cmdline
==true){
165 *(option_values
[value_no
].status
)=1;
166 *(option_values
[value_no
].target
)=value
;
168 messages
.insert(messages
.end(),"Option value \"--"
169 +option_values
[value_no
].longname
170 +"\" is not allowed on the command line!");
177 messages
.insert(messages
.begin(),"Unknown option: --"+argstring
+"!");
182 // Look for short parameters
183 if ((argv
[arg_no
].substr(0,1)=="-")&&(free_parms_count
==0)&&(!found
)){ // Short parameters
185 argstring
=argv
[arg_no
].substr(1); // Reassign, with one more character now
186 for (unsigned int pos
=0;pos
< argstring
.length();pos
++){
187 bool short_found
=false;
189 // First, find short switches
190 for (unsigned int switch_no
=0;switch_no
<option_switches
.size();switch_no
++){
191 string short_name
=option_switches
[switch_no
].shortname
;
192 if (short_name
.compare(0,short_name
.length(),argstring
,pos
,short_name
.length())==0){
193 if(option_switches
[switch_no
].allow_cmdline
==true){
194 *(option_switches
[switch_no
].status
)=1;
196 messages
.insert(messages
.end(),"Switch \"-"
197 +option_switches
[switch_no
].shortname
198 +"\" is not allowed on the command line!");
201 pos
+=short_name
.length()-1;
206 // Now, find short values
208 for (unsigned int value_no
=0;value_no
<option_values
.size();value_no
++){
209 string short_name
=option_values
[value_no
].shortname
;
211 if (short_name
.compare(0,short_name
.length(),argstring
,pos
,short_name
.length())==0){
212 if (pos
==argstring
.length()-1){ // Last character, where's the value?
213 if (arg_no
<argv
.size()-1){ // Take next complete argument as value!
214 *(option_values
[value_no
].status
)=1;
215 *(option_values
[value_no
].target
)=argv
[arg_no
+1];
216 arg_no
+=1; // Consume the next argument
218 } else { // No argument left!
219 messages
.insert(messages
.begin(),"Missing value for -"+short_name
+"!");
223 if(option_values
[value_no
].allow_cmdline
==true){
224 *(option_values
[value_no
].status
)=1;
225 *(option_values
[value_no
].target
)=argstring
.substr(pos
+1);
227 messages
.insert(messages
.end(),"Option value \"-"
228 +option_values
[value_no
].shortname
229 +"\" is not allowed on the command line!");
231 pos
=argstring
.length(); // Do not analyse other characters.
238 if (!short_found
) messages
.insert(messages
.begin(),"Unknown Option: -"+argstring
.substr(pos
,1)+"!");
242 if (!found
) { // Must be a free form parameter...
243 if (free_parms_count
<cmd_args
.size()){ // Space available
244 *(cmd_args
[free_parms_count
].target
)=argv
[arg_no
];
245 *(cmd_args
[free_parms_count
].status
)=1;
247 } else { // No more space for free form parameters!
248 messages
.insert(messages
.begin(),"Too many arguments!");
252 if (!messages
.empty()){
253 messages
.insert(messages
.begin(),"Error!");
254 messages
.insert(messages
.begin(),"");
261 *\brief Extract a value from a configuration file line or
262 * a command line argument
264 string
configuration_manager::analyse_string(const string
& line
){
266 unsigned int pos
=line
.find("=");
267 if (pos
!=string::npos
) result
=line
.substr(pos
+1);
272 *\brief Extract a boolean value out of a string, defaulting to true
274 * Used for commandline switches
276 bool configuration_manager::analyse_bool(const string
& data
){
277 if ((data
.find("false",0)!=string::npos
)||
278 (data
.find("0",0)!=string::npos
)||
279 (data
.find("no",0)!=string::npos
)) return false;
285 *\brief Extract a boolean value out of a string, defaulting to false
287 * Used for configuration file switches
289 bool configuration_manager::analyse_bool_false(const string
& data
){
290 if ((data
.find("true",0)!=string::npos
)||
291 (data
.find("1",0)!=string::npos
)||
292 (data
.find("yes",0)!=string::npos
)) return true;
298 *brief Read in and parse a configuration file.
299 *\arg file String containing the filename
300 *\return Empty Vector or Vector full of strings containing
301 * the error message(s).
303 vector
<string
> configuration_manager::read_file(string filename
){
304 vector
<string
>result
;
305 FILE * fp
=fopen(filename
.c_str(),"r");
307 result
.insert(result
.end(),"Could not open file: "+filename
);
310 while(fgets(buffer
,1000,fp
)){
311 if (buffer
[strlen(buffer
)-1]=='\n')// Cut trailing newline
312 buffer
[strlen(buffer
)-1]=0;
313 if (strlen (buffer
)){
314 char*l2
=buffer
+strspn(buffer
," \t");
317 unsigned int pos
=argstring
.find("=");
319 result
.insert(result
.end(),"In File "+filename
+": Line beginning with '='!");
324 // Look for long switches.
325 for (unsigned int switch_no
=0;switch_no
<option_switches
.size();switch_no
++){
326 string switch_name
=option_switches
[switch_no
].longname
;
327 if (argstring
.compare(0,switch_name
.length(),switch_name
,0,switch_name
.length())==0){
328 string value
=analyse_string(argstring
);
329 //result.insert(result.end(),"argstring:\""+argstring+"\"");
330 bool res
=analyse_bool_false(value
);
331 if(option_switches
[switch_no
].allow_conffile
==true){
332 *(option_switches
[switch_no
].status
)=res
;
334 result
.insert(result
.end(),"In File "+filename
+": Switch \""
335 +option_switches
[switch_no
].longname
336 +"\" is not allowed in config files!");
343 // Look for long values.
344 for (unsigned int value_no
=0;value_no
<option_values
.size();value_no
++){
345 string value_name
=option_values
[value_no
].longname
;
346 if (argstring
.compare(0,value_name
.length(),value_name
,0,value_name
.length())==0){
347 string value
=analyse_string(argstring
);
348 *(option_values
[value_no
].status
)=1;
350 if(option_values
[value_no
].allow_conffile
==true){
351 *(option_values
[value_no
].status
)=1;
352 *(option_values
[value_no
].target
)=value
;
354 result
.insert(result
.end(),"In File "+filename
+": Option value \""
355 +option_values
[value_no
].longname
356 +"\" is not allowed in config files!");
363 result
.insert(result
.end(),"In File "+filename
+": Unknown option: "+argstring
+"!");
370 } // fp==0 else branch
372 if (!result
.empty()){
373 result
.insert(result
.begin(),"Error!");
374 result
.insert(result
.begin(),"");
381 *\brief Generate help.
382 *\arg target Reference to a vector<string> to which lots of helpful
383 * strings are appended.
385 void configuration_manager::get_help(vector
<string
> & target
){
386 target
.insert(target
.end(),"");
387 string line
="Usage: "+app_name
;
389 for (vector
<opt_switch_t
>::iterator parm_p
=option_switches
.begin();parm_p
<option_switches
.end();parm_p
++){
390 string addstr
=" [-"+parm_p
->shortname
;
392 if (line
.length()+addstr
.length()>MAX_LINE_LENGTH
){
393 target
.insert(target
.end(),line
);
394 line
=string(7+app_name
.length(),' ');
399 for (vector
<opt_value_t
>::iterator parm_p
=option_values
.begin();parm_p
<option_values
.end();parm_p
++){
400 string addstr
=" [-"+parm_p
->shortname
;
401 addstr
+=parm_p
->placeholder
;
403 if (line
.length()+addstr
.length()>MAX_LINE_LENGTH
){
404 target
.insert(target
.end(),line
);
405 line
=string(7+app_name
.length(),' ');
410 for (vector
<cmd_arg_t
>::iterator parm_p
=cmd_args
.begin();parm_p
<cmd_args
.end();parm_p
++){
411 if (line
.length()+parm_p
->placeholder
.length()>MAX_LINE_LENGTH
){
412 target
.insert(target
.end(),line
);
413 line
=string(7+app_name
.length(),' ');
415 line
+=" ["+parm_p
->placeholder
+"]";
417 target
.insert(target
.end(),line
);
419 /* Here comes the documentation output. */
421 vector
<string
> left
,right
;
422 unsigned int max_width
;
424 /* Output switches? */
425 if (option_switches
.size()){
426 target
.insert(target
.end(),"");
427 target
.insert(target
.end(),"Switches:");
429 left
=vector
<string
>();
430 right
=vector
<string
>();
433 /* Now lets insert switches into left and right */
434 for (unsigned int sw
=0; sw
<option_switches
.size();sw
++){
435 opt_switch_t akt_sw
=option_switches
[sw
];
436 string rline
=akt_sw
.description
;
437 string lline
="-"+akt_sw
.shortname
+", --"+akt_sw
.longname
;
438 left
.insert(left
.end(),lline
);
439 right
.insert(right
.end(),rline
);
442 // Determine maximal width for left column
444 for (unsigned int c
=0; c
<left
.size();c
++)
445 if(left
[c
].length()>max_width
) max_width
=left
[c
].length();
447 /* output all the mess */
448 for (unsigned int c
=0; c
<left
.size();c
++){
449 string
nl(max_width
,' ');
450 nl
.replace(0,left
[c
].length(),left
[c
]);
452 while (nl
.length()>80){ // Too long???
453 int limit
=nl
.find_last_of(' ',MAX_LINE_LENGTH
+1);
454 target
.insert(target
.end(),nl
.substr(0,limit
));
455 nl
=string(max_width
+2,' ')+nl
.substr(limit
+1);
457 target
.insert(target
.end(),nl
);
462 if (option_values
.size()){
463 target
.insert(target
.end(),"");
464 target
.insert(target
.end(),"Option values:");
466 left
=vector
<string
>();
467 right
=vector
<string
>();
470 for (unsigned int val
=0; val
<option_values
.size();val
++){
471 opt_value_t akt_val
=option_values
[val
];
472 string rline
=akt_val
.description
;
473 string lline
=" -"+akt_val
.shortname
+akt_val
.placeholder
+", --"+
474 akt_val
.longname
+"="+akt_val
.placeholder
;
475 left
.insert(left
.end(),lline
);
476 right
.insert(right
.end(),rline
);
479 // Determine maximal width for left column
480 for (unsigned int c
=0; c
<left
.size();c
++)
481 if(left
[c
].length()>max_width
) max_width
=left
[c
].length();
482 /* output all the mess */
483 for (unsigned int c
=0; c
<left
.size();c
++){
484 string
nl(max_width
,' ');
485 nl
.replace(0,left
[c
].length(),left
[c
]);
487 while (nl
.length()>80){ // Too long???
488 int limit
=nl
.find_last_of(' ',MAX_LINE_LENGTH
+1);
489 target
.insert(target
.end(),nl
.substr(0,limit
));
490 nl
=string(max_width
+2,' ')+nl
.substr(limit
+1);
492 target
.insert(target
.end(),nl
);
496 /* Output the Arguments */
497 if (cmd_args
.size()){
498 target
.insert(target
.end(),"");
499 target
.insert(target
.end(),"Arguments:");
501 left
=vector
<string
>();
502 right
=vector
<string
>();
505 for (unsigned int arg
=0; arg
<cmd_args
.size();arg
++){
506 cmd_arg_t akt_arg
=cmd_args
[arg
];
507 left
.insert(left
.end(),akt_arg
.placeholder
);
508 right
.insert(right
.end(),akt_arg
.description
);
511 // Determine maximal width for left column
513 for (unsigned int c
=0; c
<left
.size();c
++)
514 if(left
[c
].length()>max_width
) max_width
=left
[c
].length();
516 for (unsigned int c
=0; c
<left
.size();c
++){
517 string
nl(max_width
,' ');
518 nl
.replace(0,left
[c
].length(),left
[c
]);
520 while (nl
.length()>MAX_LINE_LENGTH
){ // Too long???
521 int limit
=nl
.find_last_of(' ',MAX_LINE_LENGTH
+1);
522 // printf("limit:%i\n",limit);
523 target
.insert(target
.end(),nl
.substr(0,limit
));
524 nl
=string(max_width
+2,' ')+nl
.substr(limit
+1);
526 target
.insert(target
.end(),nl
);
528 target
.insert(target
.end(),"");
533 *\brief Generate help.
534 *\return A vector containing many helpful strings for the user.
536 vector
<string
> configuration_manager::get_help(){
537 vector
<string
> result
;
543 /**************************************************/
546 configuration_manager::opt_value_t::opt_value_t(const string
& shortname
,
547 const string
& longname
,
548 const string
& description
,
551 const string
& placeholder
,
552 const bool & allow_conffile
,
553 const bool & allow_cmdline
555 this->shortname
=shortname
;
556 this->longname
=longname
;
557 this->description
=description
;
560 this->placeholder
=placeholder
;
561 this->allow_conffile
=allow_conffile
;
562 this->allow_cmdline
=allow_cmdline
;
563 if (status
) *status
=0;
567 configuration_manager::opt_switch_t::opt_switch_t(const string
& shortname
,
568 const string
& longname
,
569 const string
& description
,
571 const bool & allow_conffile
,
572 const bool & allow_cmdline
){
573 this->shortname
=shortname
;
574 this->longname
=longname
;
575 this->description
=description
;
578 this->placeholder
=placeholder
;
579 this->allow_conffile
=allow_conffile
;
580 this->allow_cmdline
=allow_cmdline
;
584 configuration_manager::cmd_arg_t::cmd_arg_t(const string
& placeholder
,
585 const string
& description
,
588 this->placeholder
=placeholder
;
589 this->description
=description
;