1 /******************************************************************************
5 * $Date: 2007/03/26 04:05:37 $
8 * $Log: configuration_manager.cpp,v $
9 * Revision 2.1 2007/03/26 04:05:37 hachti
10 * *** empty log message ***
12 * Revision 2.0 2007-03-26 01:00:38 hachti
13 * *** empty log message ***
16 ******************************************************************************/
18 #include "configuration_manager.hh"
23 #define MAX_LINE_LENGTH 80
28 * This constructor makes a new configuration_manager ready to use.
29 *\arg name Name of the application as mentioned in the
30 * "Use: <appname> ..." help message.
32 configuration_manager::configuration_manager(string name
){
38 *\brief Add a new configuration value to be searched for.
39 *\param shortname A character for the short form.
40 * For example the o in -o
41 *\param longname The double dash longname. For example
42 * output_file in --output_file=
43 *\param description A detailed description of the value.
44 *\param status Pointer to an integer. Will be set to 1 if arg found.
45 *\param target Pointer to string to put the value in.
46 *\param placeholder A placeholder for the documentation.
47 * For example <filename> in -f<filename>
48 *\param allow_cmdline Specifies if this value may be specified on the
50 *\param allow_conffile Specifies if this value may be specified in a
54 void configuration_manager::add_option_value (const string
& shortname
,
55 const string
& longname
,
56 const string
& description
,
59 const string
& placeholder
,
60 const bool & allow_cmdline
,
61 const bool & allow_conffile
64 if(status
!=NULL
) if (target
!=NULL
)
65 option_values
.insert(option_values
.end(),
66 opt_value_t(shortname
,
81 *\brief Add a new configuration switch to be searched for.
82 *\param shortname A character for the short form.
83 * For example the h in -h
84 *\param longname The double dash longname. For example
86 *\param description A detailed description of the value.
87 *\param status Pointer to an integer. Will be set to 1 if arg found.
88 *\param allow_cmdline Specifies whether the switch is acceptable on the
90 *\param allow_conffile Specifies whether the switch is acceptable
91 * in a configuration file.
93 void configuration_manager::add_option_switch (const string
& shortname
,
94 const string
& longname
,
95 const string
& description
,
97 const bool & allow_cmdline
,
98 const bool & allow_conffile
102 option_switches
.insert(option_switches
.end(),
103 opt_switch_t(shortname
,
113 *\brief Add an accepted argument to the argument reader.
114 *\param placeholder Something like "<input-file>".
115 *\param description Text describing the argument.
116 *\param status A pointer to a status variable. Will be set to 0 by default.
117 * If the argument is filled in, it will be set to 1.
118 *\param target A pointer to a c++ string where the argument goes to.
120 *\note Arguments are filled in the order of adding them to the
122 * There would be no other way to determine the order.
124 void configuration_manager::add_argument(const string
& description
,
127 const string
& placeholder
){
131 if (target
!=NULL
) if(status
!=NULL
)
132 cmd_args
.insert(cmd_args
.end(),cmd_arg_t(placeholder
,
140 *\brief Read in the args passed to main().
141 *\returns empty vector on success or the error messages to be output.
143 vector
<string
> configuration_manager::read_args(int argc
, char ** args
){
145 vector
<string
> messages
;
148 // First we make a C++ string vector out of the **argv.
149 for (char ** akt
=args
+1; *akt
;akt
++) argv
.insert(argv
.end(),string(*akt
));
151 // Counter for the free command line parameters.
152 unsigned int free_parms_count
=0;
154 // Loop through the command line arguments
155 for (unsigned int arg_no
=0;arg_no
<argv
.size();arg_no
++){
159 // Look for long parameters.
160 if ((argv
[arg_no
].substr(0,2)=="--")&&(free_parms_count
==0)){
161 argstring
=argv
[arg_no
].substr(2);
163 // Look for long switches.
164 for (unsigned int switch_no
=0;switch_no
<option_switches
.size();switch_no
++){
165 string switch_name
=option_switches
[switch_no
].longname
;
166 if (argstring
.compare(0,switch_name
.length(),switch_name
,0,switch_name
.length())==0){
167 string value
=analyse_string(argstring
);
168 bool result
=analyse_bool(value
);
169 if(option_switches
[switch_no
].allow_cmdline
==true){
170 *(option_switches
[switch_no
].status
)=result
;
172 messages
.insert(messages
.end(),"Switch \"--"
173 +option_switches
[switch_no
].longname
174 +"\" is not allowed on the command line!");
181 // Look for long values.
182 for (unsigned int value_no
=0;value_no
<option_values
.size();value_no
++){
183 string value_name
=option_values
[value_no
].longname
;
184 if (argstring
.compare(0,value_name
.length(),value_name
,0,value_name
.length())==0){
185 string value
=analyse_string(argstring
);
186 if(option_values
[value_no
].allow_cmdline
==true){
187 *(option_values
[value_no
].status
)=1;
188 *(option_values
[value_no
].target
)=value
;
190 messages
.insert(messages
.end(),"Option value \"--"
191 +option_values
[value_no
].longname
192 +"\" is not allowed on the command line!");
199 messages
.insert(messages
.begin(),"Unknown option: --"+argstring
+"!");
204 // Look for short parameters
205 if ((argv
[arg_no
].substr(0,1)=="-")&&(free_parms_count
==0)&&(!found
)){ // Short parameters
207 argstring
=argv
[arg_no
].substr(1); // Reassign, with one more character now
208 for (unsigned int pos
=0;pos
< argstring
.length();pos
++){
209 bool short_found
=false;
211 // First, find short switches
212 for (unsigned int switch_no
=0;switch_no
<option_switches
.size();switch_no
++){
213 string short_name
=option_switches
[switch_no
].shortname
;
214 if (short_name
.compare(0,short_name
.length(),argstring
,pos
,short_name
.length())==0){
215 if(option_switches
[switch_no
].allow_cmdline
==true){
216 *(option_switches
[switch_no
].status
)=1;
218 messages
.insert(messages
.end(),"Switch \"-"
219 +option_switches
[switch_no
].shortname
220 +"\" is not allowed on the command line!");
223 pos
+=short_name
.length()-1;
228 // Now, find short values
230 for (unsigned int value_no
=0;value_no
<option_values
.size();value_no
++){
231 string short_name
=option_values
[value_no
].shortname
;
233 if (short_name
.compare(0,short_name
.length(),argstring
,pos
,short_name
.length())==0){
234 if (pos
==argstring
.length()-1){ // Last character, where's the value?
235 if (arg_no
<argv
.size()-1){ // Take next complete argument as value!
236 *(option_values
[value_no
].status
)=1;
237 *(option_values
[value_no
].target
)=argv
[arg_no
+1];
238 arg_no
+=1; // Consume the next argument
240 } else { // No argument left!
241 messages
.insert(messages
.begin(),"Missing value for -"+short_name
+"!");
245 if(option_values
[value_no
].allow_cmdline
==true){
246 *(option_values
[value_no
].status
)=1;
247 *(option_values
[value_no
].target
)=argstring
.substr(pos
+1);
249 messages
.insert(messages
.end(),"Option value \"-"
250 +option_values
[value_no
].shortname
251 +"\" is not allowed on the command line!");
253 pos
=argstring
.length(); // Do not analyse other characters.
260 if (!short_found
) messages
.insert(messages
.begin(),"Unknown Option: -"+argstring
.substr(pos
,1)+"!");
264 if (!found
) { // Must be a free form parameter...
265 if (free_parms_count
<cmd_args
.size()){ // Space available
266 *(cmd_args
[free_parms_count
].target
)=argv
[arg_no
];
267 *(cmd_args
[free_parms_count
].status
)=1;
269 } else { // No more space for free form parameters!
270 messages
.insert(messages
.begin(),"Too many arguments!");
274 if (!messages
.empty()){
275 messages
.insert(messages
.begin(),"Error!");
276 messages
.insert(messages
.begin(),"");
283 *\brief Extract a value from a configuration file line or
284 * a command line argument
286 string
configuration_manager::analyse_string(const string
& line
){
288 unsigned int pos
=line
.find("=");
289 if (pos
!=string::npos
) result
=line
.substr(pos
+1);
294 *\brief Extract a boolean value out of a string, defaulting to true
296 * Used for commandline switches
298 bool configuration_manager::analyse_bool(const string
& data
){
299 if ((data
.find("false",0)!=string::npos
)||
300 (data
.find("0",0)!=string::npos
)||
301 (data
.find("no",0)!=string::npos
)) return false;
307 *\brief Extract a boolean value out of a string, defaulting to false
309 * Used for configuration file switches
311 bool configuration_manager::analyse_bool_false(const string
& data
){
312 if ((data
.find("true",0)!=string::npos
)||
313 (data
.find("1",0)!=string::npos
)||
314 (data
.find("yes",0)!=string::npos
)) return true;
320 *brief Read in and parse a configuration file.
321 *\arg file String containing the filename
322 *\return Empty Vector or Vector full of strings containing
323 * the error message(s).
325 vector
<string
> configuration_manager::read_file(string filename
){
326 vector
<string
>result
;
327 FILE * fp
=fopen(filename
.c_str(),"r");
329 result
.insert(result
.end(),"Could not open file: "+filename
);
332 while(fgets(buffer
,1000,fp
)){
333 if (buffer
[strlen(buffer
)-1]=='\n')// Cut trailing newline
334 buffer
[strlen(buffer
)-1]=0;
335 if (strlen (buffer
)){
336 char*l2
=buffer
+strspn(buffer
," \t");
339 unsigned int pos
=argstring
.find("=");
341 result
.insert(result
.end(),"In File "+filename
+": Line beginning with '='!");
346 // Look for long switches.
347 for (unsigned int switch_no
=0;switch_no
<option_switches
.size();switch_no
++){
348 string switch_name
=option_switches
[switch_no
].longname
;
349 if (argstring
.compare(0,switch_name
.length(),switch_name
,0,switch_name
.length())==0){
350 string value
=analyse_string(argstring
);
351 //result.insert(result.end(),"argstring:\""+argstring+"\"");
352 bool res
=analyse_bool_false(value
);
353 if(option_switches
[switch_no
].allow_conffile
==true){
354 *(option_switches
[switch_no
].status
)=res
;
356 result
.insert(result
.end(),"In File "+filename
+": Switch \""
357 +option_switches
[switch_no
].longname
358 +"\" is not allowed in config files!");
365 // Look for long values.
366 for (unsigned int value_no
=0;value_no
<option_values
.size();value_no
++){
367 string value_name
=option_values
[value_no
].longname
;
368 if (argstring
.compare(0,value_name
.length(),value_name
,0,value_name
.length())==0){
369 string value
=analyse_string(argstring
);
370 *(option_values
[value_no
].status
)=1;
372 if(option_values
[value_no
].allow_conffile
==true){
373 *(option_values
[value_no
].status
)=1;
374 *(option_values
[value_no
].target
)=value
;
376 result
.insert(result
.end(),"In File "+filename
+": Option value \""
377 +option_values
[value_no
].longname
378 +"\" is not allowed in config files!");
385 result
.insert(result
.end(),"In File "+filename
+": Unknown option: "+argstring
+"!");
392 } // fp==0 else branch
394 if (!result
.empty()){
395 result
.insert(result
.begin(),"Error!");
396 result
.insert(result
.begin(),"");
403 *\brief Generate help.
404 *\arg target Reference to a vector<string> to which lots of helpful
405 * strings are appended.
407 void configuration_manager::get_help(vector
<string
> & target
){
408 target
.insert(target
.end(),"");
409 string line
="Usage: "+app_name
;
411 for (vector
<opt_switch_t
>::iterator parm_p
=option_switches
.begin();parm_p
<option_switches
.end();parm_p
++){
412 string addstr
=" [-"+parm_p
->shortname
;
414 if (line
.length()+addstr
.length()>MAX_LINE_LENGTH
){
415 target
.insert(target
.end(),line
);
416 line
=string(7+app_name
.length(),' ');
421 for (vector
<opt_value_t
>::iterator parm_p
=option_values
.begin();parm_p
<option_values
.end();parm_p
++){
422 string addstr
=" [-"+parm_p
->shortname
;
423 addstr
+=parm_p
->placeholder
;
425 if (line
.length()+addstr
.length()>MAX_LINE_LENGTH
){
426 target
.insert(target
.end(),line
);
427 line
=string(7+app_name
.length(),' ');
432 for (vector
<cmd_arg_t
>::iterator parm_p
=cmd_args
.begin();parm_p
<cmd_args
.end();parm_p
++){
433 if (line
.length()+parm_p
->placeholder
.length()>MAX_LINE_LENGTH
){
434 target
.insert(target
.end(),line
);
435 line
=string(7+app_name
.length(),' ');
437 line
+=" ["+parm_p
->placeholder
+"]";
439 target
.insert(target
.end(),line
);
441 /* Here comes the documentation output. */
443 vector
<string
> left
,right
;
444 unsigned int max_width
;
446 /* Output switches? */
447 if (option_switches
.size()){
448 target
.insert(target
.end(),"");
449 target
.insert(target
.end(),"Switches:");
451 left
=vector
<string
>();
452 right
=vector
<string
>();
455 /* Now lets insert switches into left and right */
456 for (unsigned int sw
=0; sw
<option_switches
.size();sw
++){
457 opt_switch_t akt_sw
=option_switches
[sw
];
458 string rline
=akt_sw
.description
;
459 string lline
="-"+akt_sw
.shortname
+", --"+akt_sw
.longname
;
460 left
.insert(left
.end(),lline
);
461 right
.insert(right
.end(),rline
);
464 // Determine maximal width for left column
466 for (unsigned int c
=0; c
<left
.size();c
++)
467 if(left
[c
].length()>max_width
) max_width
=left
[c
].length();
469 /* output all the mess */
470 for (unsigned int c
=0; c
<left
.size();c
++){
471 string
nl(max_width
,' ');
472 nl
.replace(0,left
[c
].length(),left
[c
]);
474 while (nl
.length()>80){ // Too long???
475 int limit
=nl
.find_last_of(' ',MAX_LINE_LENGTH
+1);
476 target
.insert(target
.end(),nl
.substr(0,limit
));
477 nl
=string(max_width
+2,' ')+nl
.substr(limit
+1);
479 target
.insert(target
.end(),nl
);
484 if (option_values
.size()){
485 target
.insert(target
.end(),"");
486 target
.insert(target
.end(),"Option values:");
488 left
=vector
<string
>();
489 right
=vector
<string
>();
492 for (unsigned int val
=0; val
<option_values
.size();val
++){
493 opt_value_t akt_val
=option_values
[val
];
494 string rline
=akt_val
.description
;
495 string lline
=" -"+akt_val
.shortname
+akt_val
.placeholder
+", --"+
496 akt_val
.longname
+"="+akt_val
.placeholder
;
497 left
.insert(left
.end(),lline
);
498 right
.insert(right
.end(),rline
);
501 // Determine maximal width for left column
502 for (unsigned int c
=0; c
<left
.size();c
++)
503 if(left
[c
].length()>max_width
) max_width
=left
[c
].length();
504 /* output all the mess */
505 for (unsigned int c
=0; c
<left
.size();c
++){
506 string
nl(max_width
,' ');
507 nl
.replace(0,left
[c
].length(),left
[c
]);
509 while (nl
.length()>80){ // Too long???
510 int limit
=nl
.find_last_of(' ',MAX_LINE_LENGTH
+1);
511 target
.insert(target
.end(),nl
.substr(0,limit
));
512 nl
=string(max_width
+2,' ')+nl
.substr(limit
+1);
514 target
.insert(target
.end(),nl
);
518 /* Output the Arguments */
519 if (cmd_args
.size()){
520 target
.insert(target
.end(),"");
521 target
.insert(target
.end(),"Arguments:");
523 left
=vector
<string
>();
524 right
=vector
<string
>();
527 for (unsigned int arg
=0; arg
<cmd_args
.size();arg
++){
528 cmd_arg_t akt_arg
=cmd_args
[arg
];
529 left
.insert(left
.end(),akt_arg
.placeholder
);
530 right
.insert(right
.end(),akt_arg
.description
);
533 // Determine maximal width for left column
535 for (unsigned int c
=0; c
<left
.size();c
++)
536 if(left
[c
].length()>max_width
) max_width
=left
[c
].length();
538 for (unsigned int c
=0; c
<left
.size();c
++){
539 string
nl(max_width
,' ');
540 nl
.replace(0,left
[c
].length(),left
[c
]);
542 while (nl
.length()>MAX_LINE_LENGTH
){ // Too long???
543 int limit
=nl
.find_last_of(' ',MAX_LINE_LENGTH
+1);
544 // printf("limit:%i\n",limit);
545 target
.insert(target
.end(),nl
.substr(0,limit
));
546 nl
=string(max_width
+2,' ')+nl
.substr(limit
+1);
548 target
.insert(target
.end(),nl
);
550 target
.insert(target
.end(),"");
555 *\brief Generate help.
556 *\return A vector containing many helpful strings for the user.
558 vector
<string
> configuration_manager::get_help(){
559 vector
<string
> result
;
565 /**************************************************/
568 configuration_manager::opt_value_t::opt_value_t(const string
& shortname
,
569 const string
& longname
,
570 const string
& description
,
573 const string
& placeholder
,
574 const bool & allow_conffile
,
575 const bool & allow_cmdline
577 this->shortname
=shortname
;
578 this->longname
=longname
;
579 this->description
=description
;
582 this->placeholder
=placeholder
;
583 this->allow_conffile
=allow_conffile
;
584 this->allow_cmdline
=allow_cmdline
;
585 if (status
) *status
=0;
589 configuration_manager::opt_switch_t::opt_switch_t(const string
& shortname
,
590 const string
& longname
,
591 const string
& description
,
593 const bool & allow_conffile
,
594 const bool & allow_cmdline
){
595 this->shortname
=shortname
;
596 this->longname
=longname
;
597 this->description
=description
;
600 this->placeholder
=placeholder
;
601 this->allow_conffile
=allow_conffile
;
602 this->allow_cmdline
=allow_cmdline
;
606 configuration_manager::cmd_arg_t::cmd_arg_t(const string
& placeholder
,
607 const string
& description
,
610 this->placeholder
=placeholder
;
611 this->description
=description
;