1 /******************************************************************************
5 * $Date: 2008/10/01 13:30:14 $
8 * $Log: configuration_manager.cpp,v $
9 * Revision 2.4 2008/10/01 13:30:14 hachti
12 * Revision 2.3 2008-08-25 21:02:24 hachti
13 * *** empty log message ***
15 * Revision 2.2 2007-06-15 12:46:04 hachti
16 * Some small changes in configuration_manager output - added (c/f) info.
17 * Changed order in tool.hh:add_unique....
19 * Revision 2.1 2007-03-26 04:05:37 hachti
20 * *** empty log message ***
22 * Revision 2.0 2007-03-26 01:00:38 hachti
23 * *** empty log message ***
26 ******************************************************************************/
28 #include "configuration_manager.hh"
35 #define MAX_LINE_LENGTH 80
40 * This constructor makes a new configuration_manager ready to use.
41 *\arg name Name of the application as mentioned in the
42 * "Use: <appname> ..." help message.
44 configuration_manager::configuration_manager(string name
){
50 *\brief Add a new configuration value to be searched for.
51 *\param shortname A character for the short form.
52 * For example the o in -o
53 *\param longname The double dash longname. For example
54 * output_file in --output_file=
55 *\param description A detailed description of the value.
56 *\param status Pointer to an integer. Will be set to 1 if arg found.
57 *\param target Pointer to string to put the value in.
58 *\param placeholder A placeholder for the documentation.
59 * For example <filename> in -f<filename>
60 *\param allow_cmdline Specifies if this value may be specified on the
62 *\param allow_conffile Specifies if this value may be specified in a
66 void configuration_manager::add_option_value (const string
& shortname
,
67 const string
& longname
,
68 const string
& description
,
71 const string
& placeholder
,
72 const bool & allow_cmdline
,
73 const bool & allow_conffile
76 if(status
!=NULL
) if (target
!=NULL
)
77 option_values
.insert(option_values
.end(),
78 opt_value_t(shortname
,
93 *\brief Add a new configuration switch to be searched for.
94 *\param shortname A character for the short form.
95 * For example the h in -h
96 *\param longname The double dash longname. For example
98 *\param description A detailed description of the value.
99 *\param status Pointer to an integer. Will be set to 1 if arg found.
100 *\param allow_cmdline Specifies whether the switch is acceptable on the
102 *\param allow_conffile Specifies whether the switch is acceptable
103 * in a configuration file.
105 void configuration_manager::add_option_switch (const string
& shortname
,
106 const string
& longname
,
107 const string
& description
,
109 const bool & allow_cmdline
,
110 const bool & allow_conffile
114 option_switches
.insert(option_switches
.end(),
115 opt_switch_t(shortname
,
125 *\brief Add an accepted argument to the argument reader.
126 *\param placeholder Something like "<input-file>".
127 *\param description Text describing the argument.
128 *\param status A pointer to a status variable. Will be set to 0 by default.
129 * If the argument is filled in, it will be set to 1.
130 *\param target A pointer to a c++ string where the argument goes to.
132 *\note Arguments are filled in the order of adding them to the
134 * There would be no other way to determine the order.
136 void configuration_manager::add_argument(const string
& description
,
139 const string
& placeholder
){
143 if (target
!=NULL
) if(status
!=NULL
)
144 cmd_args
.insert(cmd_args
.end(),cmd_arg_t(placeholder
,
152 *\brief Read in the args passed to main().
153 *\returns empty vector on success or the error messages to be output.
155 vector
<string
> configuration_manager::read_args(int argc
, char ** args
){
157 vector
<string
> messages
;
160 // First we make a C++ string vector out of the **argv.
161 for (char ** akt
=args
+1; *akt
;akt
++) argv
.insert(argv
.end(),string(*akt
));
163 // Counter for the free command line parameters.
164 unsigned int free_parms_count
=0;
166 // Loop through the command line arguments
167 for (unsigned int arg_no
=0;arg_no
<argv
.size();arg_no
++){
171 // Look for long parameters.
172 // if ((argv[arg_no].substr(0,2)=="--")&&(free_parms_count==0)){
173 if ((argv
[arg_no
].substr(0,2)=="--")){ // Changed for comfort.
174 argstring
=argv
[arg_no
].substr(2);
176 // Look for long switches.
177 for (unsigned int switch_no
=0;switch_no
<option_switches
.size();switch_no
++){
178 string switch_name
=option_switches
[switch_no
].longname
;
179 if (argstring
.compare(0,switch_name
.length(),switch_name
,0,switch_name
.length())==0){
180 string value
=analyse_string(argstring
);
181 bool result
=analyse_bool(value
);
182 if(option_switches
[switch_no
].allow_cmdline
==true){
183 *(option_switches
[switch_no
].status
)=result
;
185 messages
.insert(messages
.end(),"Switch \"--"
186 +option_switches
[switch_no
].longname
187 +"\" is not allowed on the command line!");
194 // Look for long values.
195 for (unsigned int value_no
=0;value_no
<option_values
.size();value_no
++){
196 string value_name
=option_values
[value_no
].longname
;
197 if (argstring
.compare(0,value_name
.length(),value_name
,0,value_name
.length())==0){
198 string value
=analyse_string(argstring
);
199 if(option_values
[value_no
].allow_cmdline
==true){
200 *(option_values
[value_no
].status
)=1;
201 *(option_values
[value_no
].target
)=value
;
203 messages
.insert(messages
.end(),"Option value \"--"
204 +option_values
[value_no
].longname
205 +"\" is not allowed on the command line!");
212 messages
.insert(messages
.begin(),"Unknown option: --"+argstring
+"!");
217 // Look for short parameters
218 // if ((argv[arg_no].substr(0,1)=="-")&&(free_parms_count==0)&&(!found)){ // Short parameters
219 if ((argv
[arg_no
].substr(0,1)=="-")&&(!found
)){ // Changed for comfort.
221 argstring
=argv
[arg_no
].substr(1); // Reassign, with one more character now
222 for (unsigned int pos
=0;pos
< argstring
.length();pos
++){
223 bool short_found
=false;
225 // First, find short switches
226 for (unsigned int switch_no
=0;switch_no
<option_switches
.size();switch_no
++){
227 string short_name
=option_switches
[switch_no
].shortname
;
228 if (short_name
.compare(0,short_name
.length(),argstring
,pos
,short_name
.length())==0){
229 if(option_switches
[switch_no
].allow_cmdline
==true){
230 *(option_switches
[switch_no
].status
)=1;
232 messages
.insert(messages
.end(),"Switch \"-"
233 +option_switches
[switch_no
].shortname
234 +"\" is not allowed on the command line!");
237 pos
+=short_name
.length()-1;
242 // Now, find short values
244 for (unsigned int value_no
=0;value_no
<option_values
.size();value_no
++){
245 string short_name
=option_values
[value_no
].shortname
;
247 if (short_name
.compare(0,short_name
.length(),argstring
,pos
,short_name
.length())==0){
248 if (pos
==argstring
.length()-1){ // Last character, where's the value?
249 if (arg_no
<argv
.size()-1){ // Take next complete argument as value!
250 *(option_values
[value_no
].status
)=1;
251 *(option_values
[value_no
].target
)=argv
[arg_no
+1];
252 arg_no
+=1; // Consume the next argument
254 } else { // No argument left!
255 messages
.insert(messages
.begin(),"Missing value for -"+short_name
+"!");
259 if(option_values
[value_no
].allow_cmdline
==true){
260 *(option_values
[value_no
].status
)=1;
261 *(option_values
[value_no
].target
)=argstring
.substr(pos
+1);
263 messages
.insert(messages
.end(),"Option value \"-"
264 +option_values
[value_no
].shortname
265 +"\" is not allowed on the command line!");
267 pos
=argstring
.length(); // Do not analyse other characters.
274 if (!short_found
) messages
.insert(messages
.begin(),"Unknown Option: -"+argstring
.substr(pos
,1)+"!");
278 if (!found
) { // Must be a free form parameter...
279 if (free_parms_count
<cmd_args
.size()){ // Space available
280 *(cmd_args
[free_parms_count
].target
)=argv
[arg_no
];
281 *(cmd_args
[free_parms_count
].status
)=1;
283 } else { // No more space for free form parameters!
284 messages
.insert(messages
.begin(),"Too many arguments!");
288 if (!messages
.empty()){
289 messages
.insert(messages
.begin(),"Error!");
290 messages
.insert(messages
.begin(),"");
297 *\brief Extract a value from a configuration file line or
298 * a command line argument
300 string
configuration_manager::analyse_string(const string
& line
){
302 unsigned int pos
=line
.find("=");
303 if (pos
!=string::npos
) result
=line
.substr(pos
+1);
308 *\brief Extract a boolean value out of a string, defaulting to true
310 * Used for commandline switches
312 bool configuration_manager::analyse_bool(const string
& data
){
313 if ((data
.find("false",0)!=string::npos
)||
314 (data
.find("0",0)!=string::npos
)||
315 (data
.find("no",0)!=string::npos
)) return false;
321 *\brief Extract a boolean value out of a string, defaulting to false
323 * Used for configuration file switches
325 bool configuration_manager::analyse_bool_false(const string
& data
){
326 if ((data
.find("true",0)!=string::npos
)||
327 (data
.find("1",0)!=string::npos
)||
328 (data
.find("yes",0)!=string::npos
)) return true;
334 *brief Read in and parse a configuration file.
335 *\arg file String containing the filename
336 *\return Empty Vector or Vector full of strings containing
337 * the error message(s).
339 vector
<string
> configuration_manager::read_file(string filename
){
340 vector
<string
>result
;
341 FILE * fp
=fopen(filename
.c_str(),"r");
343 result
.insert(result
.end(),"Could not open file: "+filename
);
346 while(fgets(buffer
,1000,fp
)){
347 if (buffer
[strlen(buffer
)-1]=='\n')// Cut trailing newline
348 buffer
[strlen(buffer
)-1]=0;
349 if (strlen (buffer
)){
350 char*l2
=buffer
+strspn(buffer
," \t");
353 unsigned int pos
=argstring
.find("=");
355 result
.insert(result
.end(),"In File "+filename
+": Line beginning with '='!");
360 // Look for long switches.
361 for (unsigned int switch_no
=0;switch_no
<option_switches
.size();switch_no
++){
362 string switch_name
=option_switches
[switch_no
].longname
;
363 if (argstring
.compare(0,switch_name
.length(),switch_name
,0,switch_name
.length())==0){
364 string value
=analyse_string(argstring
);
365 //result.insert(result.end(),"argstring:\""+argstring+"\"");
366 bool res
=analyse_bool_false(value
);
367 if(option_switches
[switch_no
].allow_conffile
==true){
368 *(option_switches
[switch_no
].status
)=res
;
370 result
.insert(result
.end(),"In File "+filename
+": Switch \""
371 +option_switches
[switch_no
].longname
372 +"\" is not allowed in config files!");
379 // Look for long values.
380 for (unsigned int value_no
=0;value_no
<option_values
.size();value_no
++){
381 string value_name
=option_values
[value_no
].longname
;
382 if (argstring
.compare(0,value_name
.length(),value_name
,0,value_name
.length())==0){
383 string value
=analyse_string(argstring
);
384 *(option_values
[value_no
].status
)=1;
386 if(option_values
[value_no
].allow_conffile
==true){
387 *(option_values
[value_no
].status
)=1;
388 *(option_values
[value_no
].target
)=value
;
390 result
.insert(result
.end(),"In File "+filename
+": Option value \""
391 +option_values
[value_no
].longname
392 +"\" is not allowed in config files!");
399 result
.insert(result
.end(),"In File "+filename
+": Unknown option: "+argstring
+"!");
406 } // fp==0 else branch
408 if (!result
.empty()){
409 result
.insert(result
.begin(),"Error!");
410 result
.insert(result
.begin(),"");
417 *\brief Generate help.
418 *\arg target Reference to a vector<string> to which lots of helpful
419 * strings are appended.
421 void configuration_manager::get_help(vector
<string
> & target
){
422 target
.insert(target
.end(),"");
423 string line
="Usage: "+app_name
;
425 for (vector
<opt_switch_t
>::iterator parm_p
=option_switches
.begin();parm_p
<option_switches
.end();parm_p
++){
426 string addstr
=" [-"+parm_p
->shortname
;
428 if (line
.length()+addstr
.length()>MAX_LINE_LENGTH
){
429 target
.insert(target
.end(),line
);
430 line
=string(7+app_name
.length(),' ');
435 for (vector
<opt_value_t
>::iterator parm_p
=option_values
.begin();parm_p
<option_values
.end();parm_p
++){
436 string addstr
=" [-"+parm_p
->shortname
;
437 addstr
+=parm_p
->placeholder
;
439 if (line
.length()+addstr
.length()>MAX_LINE_LENGTH
){
440 target
.insert(target
.end(),line
);
441 line
=string(7+app_name
.length(),' ');
446 for (vector
<cmd_arg_t
>::iterator parm_p
=cmd_args
.begin();parm_p
<cmd_args
.end();parm_p
++){
447 if (line
.length()+parm_p
->placeholder
.length()>MAX_LINE_LENGTH
){
448 target
.insert(target
.end(),line
);
449 line
=string(7+app_name
.length(),' ');
451 line
+=" ["+parm_p
->placeholder
+"]";
453 target
.insert(target
.end(),line
);
455 /* Here comes the documentation output. */
457 vector
<string
> left
,right
;
458 unsigned int max_width
;
460 /* Output switches? */
461 if (option_switches
.size()){
462 target
.insert(target
.end(),"");
463 target
.insert(target
.end(),"Switches:");
465 left
=vector
<string
>();
466 right
=vector
<string
>();
469 /* Now lets insert switches into left and right */
470 for (unsigned int sw
=0; sw
<option_switches
.size();sw
++){
471 opt_switch_t akt_sw
=option_switches
[sw
];
473 if (akt_sw
.allow_cmdline
) rline
+="c/";
475 if (akt_sw
.allow_conffile
) rline
+="f)";
477 rline
+=" "+akt_sw
.description
;
478 string lline
="-"+akt_sw
.shortname
+", --"+akt_sw
.longname
;
479 left
.insert(left
.end(),lline
);
480 right
.insert(right
.end(),rline
);
483 // Determine maximal width for left column
485 for (unsigned int c
=0; c
<left
.size();c
++)
486 if(left
[c
].length()>max_width
) max_width
=left
[c
].length();
488 /* output all the mess */
489 for (unsigned int c
=0; c
<left
.size();c
++){
490 string
nl(max_width
,' ');
491 nl
.replace(0,left
[c
].length(),left
[c
]);
493 while (nl
.length()>MAX_LINE_LENGTH
){ // Too long???
494 int limit
=nl
.find_last_of(' ',MAX_LINE_LENGTH
);
495 target
.insert(target
.end(),nl
.substr(0,limit
));
496 nl
=string(max_width
+2+6,' ')+nl
.substr(limit
+1);
498 target
.insert(target
.end(),nl
);
503 if (option_values
.size()){
504 target
.insert(target
.end(),"");
505 target
.insert(target
.end(),"Option values:");
507 left
=vector
<string
>();
508 right
=vector
<string
>();
511 for (unsigned int val
=0; val
<option_values
.size();val
++){
512 opt_value_t akt_val
=option_values
[val
];
514 if (akt_val
.allow_cmdline
) rline
+="c/";
516 if (akt_val
.allow_conffile
) rline
+="f)";
518 rline
+=" "+akt_val
.description
;
520 string lline
="-"+akt_val
.shortname
+akt_val
.placeholder
+", --"+
521 akt_val
.longname
+"="+akt_val
.placeholder
;
522 left
.insert(left
.end(),lline
);
523 right
.insert(right
.end(),rline
);
526 // Determine maximal width for left column
527 for (unsigned int c
=0; c
<left
.size();c
++)
528 if(left
[c
].length()>max_width
) max_width
=left
[c
].length();
529 /* output all the mess */
530 for (unsigned int c
=0; c
<left
.size();c
++){
531 string
nl(max_width
,' '); // Empty left side.
532 nl
.replace(0,left
[c
].length(),left
[c
]); // Print in left side.
533 nl
+=" "+right
[c
]; // Add right side.
535 while (nl
.length()>MAX_LINE_LENGTH
){ // Too long???
536 int limit
=nl
.find_last_of(' ',MAX_LINE_LENGTH
);
537 target
.insert(target
.end(),nl
.substr(0,limit
));
538 nl
=string(max_width
+2+6,' ')+nl
.substr(limit
+1);
540 target
.insert(target
.end(),nl
);
544 target
.insert(target
.end(),"");
545 target
.insert(target
.end(),"Legend (c/f):");
546 target
.insert(target
.end()," c: Allowed on command line.");
547 target
.insert(target
.end()," f: Allowed in configuration file.");
549 /* Output the Arguments */
550 if (cmd_args
.size()){
551 target
.insert(target
.end(),"");
552 target
.insert(target
.end(),"Arguments:");
554 left
=vector
<string
>();
555 right
=vector
<string
>();
558 for (unsigned int arg
=0; arg
<cmd_args
.size();arg
++){
559 cmd_arg_t akt_arg
=cmd_args
[arg
];
560 left
.insert(left
.end(),akt_arg
.placeholder
);
561 right
.insert(right
.end(),akt_arg
.description
);
564 // Determine maximal width for left column
566 for (unsigned int c
=0; c
<left
.size();c
++)
567 if(left
[c
].length()>max_width
) max_width
=left
[c
].length();
569 for (unsigned int c
=0; c
<left
.size();c
++){
570 string
nl(max_width
,' ');
571 nl
.replace(0,left
[c
].length(),left
[c
]);
573 while (nl
.length()>MAX_LINE_LENGTH
){ // Too long???
574 int limit
=nl
.find_last_of(' ',MAX_LINE_LENGTH
+1);
575 target
.insert(target
.end(),nl
.substr(0,limit
));
576 nl
=string(max_width
+2,' ')+nl
.substr(limit
+1);
578 target
.insert(target
.end(),nl
);
580 target
.insert(target
.end(),"");
585 *\brief Generate help.
586 *\return A vector containing many helpful strings for the user.
588 vector
<string
> configuration_manager::get_help(){
589 vector
<string
> result
;
595 /**************************************************/
598 configuration_manager::opt_value_t::opt_value_t(const string
& shortname
,
599 const string
& longname
,
600 const string
& description
,
603 const string
& placeholder
,
604 const bool & allow_conffile
,
605 const bool & allow_cmdline
607 this->shortname
=shortname
;
608 this->longname
=longname
;
609 this->description
=description
;
612 this->placeholder
=placeholder
;
613 this->allow_conffile
=allow_conffile
;
614 this->allow_cmdline
=allow_cmdline
;
615 if (status
) *status
=0;
619 configuration_manager::opt_switch_t::opt_switch_t(const string
& shortname
,
620 const string
& longname
,
621 const string
& description
,
623 const bool & allow_conffile
,
624 const bool & allow_cmdline
){
625 this->shortname
=shortname
;
626 this->longname
=longname
;
627 this->description
=description
;
630 this->placeholder
=placeholder
;
631 this->allow_conffile
=allow_conffile
;
632 this->allow_cmdline
=allow_cmdline
;
636 configuration_manager::cmd_arg_t::cmd_arg_t(const string
& placeholder
,
637 const string
& description
,
640 this->placeholder
=placeholder
;
641 this->description
=description
;