1 /******************************************************************************
5 * $Date: 2007/03/26 01:00:38 $
8 * $Log: configuration_manager.cpp,v $
9 * Revision 2.0 2007/03/26 01:00:38 hachti
10 * *** empty log message ***
13 ******************************************************************************/
15 #include "configuration_manager.hh"
20 #define MAX_LINE_LENGTH 80
25 * This constructor makes a new configuration_manager ready to use.
26 *\arg name Name of the application as mentioned in the
27 * "Use: <appname> ..." help message.
29 configuration_manager::configuration_manager(string name
){
35 *\brief Add a new configuration value to be searched for.
36 *\param shortname A character for the short form.
37 * For example the o in -o
38 *\param longname The double dash longname. For example
39 * output_file in --output_file=
40 *\param description A detailed description of the value.
41 *\param status Pointer to an integer. Will be set to 1 if arg found.
42 *\param target Pointer to string to put the value in.
43 *\param placeholder A placeholder for the documentation.
44 * For example <filename> in -f<filename>
46 void configuration_manager::add_option_value (const string
& shortname
,
47 const string
& longname
,
48 const string
& description
,
51 const string
& placeholder
,
52 const bool & allow_cmdline
,
53 const bool & allow_conffile
56 if(status
!=NULL
) if (target
!=NULL
)
57 option_values
.insert(option_values
.end(),
58 opt_value_t(shortname
,
73 *\brief Add a new configuration switch to be searched for.
74 *\param shortname A character for the short form.
75 * For example the h in -h
76 *\param longname The double dash longname. For example
78 *\param description A detailed description of the value.
79 *\param status Pointer to an integer. Will be set to 1 if arg found.
80 *\param allow_cmdline Specifies whether the switch is acceptable on the
82 *\param allow_conffile Specifies whether the switch is acceptable
83 * in a configuration file.
85 void configuration_manager::add_option_switch (const string
& shortname
,
86 const string
& longname
,
87 const string
& description
,
89 const bool & allow_cmdline
,
90 const bool & allow_conffile
94 option_switches
.insert(option_switches
.end(),
95 opt_switch_t(shortname
,
105 *\brief Add an accepted argument to the argument reader.
106 *\param placeholder Something like "<input-file>".
107 *\param description Text describing the argument.
108 *\param status A pointer to a status variable. Will be set to 0 by default.
109 * If the argument is filled in, it will be set to 1.
110 *\param target A pointer to a c++ string where the argument goes to.
112 *\note Arguments are filled in the order of adding them to the
114 * There would be no other way to determine the order.
116 void configuration_manager::add_argument(const string
& description
,
119 const string
& placeholder
){
123 if (target
!=NULL
) if(status
!=NULL
)
124 cmd_args
.insert(cmd_args
.end(),cmd_arg_t(placeholder
,
132 *\Read in the args passed to main().
133 *\returns empty vector on success or the error messages to be output.
135 vector
<string
> configuration_manager::read_args(int argc
, char ** args
){
137 vector
<string
> messages
;
140 // First we make a C++ string vector out of the **argv.
141 for (char ** akt
=args
+1; *akt
;akt
++) argv
.insert(argv
.end(),string(*akt
));
143 // Counter for the free command line parameters.
144 unsigned int free_parms_count
=0;
146 // Loop through the command line arguments
147 for (unsigned int arg_no
=0;arg_no
<argv
.size();arg_no
++){
151 // Look for long parameters.
152 if ((argv
[arg_no
].substr(0,2)=="--")&&(free_parms_count
==0)){
153 argstring
=argv
[arg_no
].substr(2);
155 // Look for long switches.
156 for (unsigned int switch_no
=0;switch_no
<option_switches
.size();switch_no
++){
157 string switch_name
=option_switches
[switch_no
].longname
;
158 if (argstring
.compare(0,switch_name
.length(),switch_name
,0,switch_name
.length())==0){
159 string value
=analyse_string(argstring
);
160 bool result
=analyse_bool(value
);
161 if(option_switches
[switch_no
].allow_cmdline
==true){
162 *(option_switches
[switch_no
].status
)=result
;
164 messages
.insert(messages
.end(),"Switch \"--"
165 +option_switches
[switch_no
].longname
166 +"\" is not allowed on the command line!");
173 // Look for long values.
174 for (unsigned int value_no
=0;value_no
<option_values
.size();value_no
++){
175 string value_name
=option_values
[value_no
].longname
;
176 if (argstring
.compare(0,value_name
.length(),value_name
,0,value_name
.length())==0){
177 string value
=analyse_string(argstring
);
178 if(option_values
[value_no
].allow_cmdline
==true){
179 *(option_values
[value_no
].status
)=1;
180 *(option_values
[value_no
].target
)=value
;
182 messages
.insert(messages
.end(),"Option value \"--"
183 +option_values
[value_no
].longname
184 +"\" is not allowed on the command line!");
191 messages
.insert(messages
.begin(),"Unknown option: --"+argstring
+"!");
196 // Look for short parameters
197 if ((argv
[arg_no
].substr(0,1)=="-")&&(free_parms_count
==0)&&(!found
)){ // Short parameters
199 argstring
=argv
[arg_no
].substr(1); // Reassign, with one more character now
200 for (unsigned int pos
=0;pos
< argstring
.length();pos
++){
201 bool short_found
=false;
203 // First, find short switches
204 for (unsigned int switch_no
=0;switch_no
<option_switches
.size();switch_no
++){
205 string short_name
=option_switches
[switch_no
].shortname
;
206 if (short_name
.compare(0,short_name
.length(),argstring
,pos
,short_name
.length())==0){
207 if(option_switches
[switch_no
].allow_cmdline
==true){
208 *(option_switches
[switch_no
].status
)=1;
210 messages
.insert(messages
.end(),"Switch \"-"
211 +option_switches
[switch_no
].shortname
212 +"\" is not allowed on the command line!");
215 pos
+=short_name
.length()-1;
220 // Now, find short values
222 for (unsigned int value_no
=0;value_no
<option_values
.size();value_no
++){
223 string short_name
=option_values
[value_no
].shortname
;
225 if (short_name
.compare(0,short_name
.length(),argstring
,pos
,short_name
.length())==0){
226 if (pos
==argstring
.length()-1){ // Last character, where's the value?
227 if (arg_no
<argv
.size()-1){ // Take next complete argument as value!
228 *(option_values
[value_no
].status
)=1;
229 *(option_values
[value_no
].target
)=argv
[arg_no
+1];
230 arg_no
+=1; // Consume the next argument
232 } else { // No argument left!
233 messages
.insert(messages
.begin(),"Missing value for -"+short_name
+"!");
237 if(option_values
[value_no
].allow_cmdline
==true){
238 *(option_values
[value_no
].status
)=1;
239 *(option_values
[value_no
].target
)=argstring
.substr(pos
+1);
241 messages
.insert(messages
.end(),"Option value \"-"
242 +option_values
[value_no
].shortname
243 +"\" is not allowed on the command line!");
245 pos
=argstring
.length(); // Do not analyse other characters.
252 if (!short_found
) messages
.insert(messages
.begin(),"Unknown Option: -"+argstring
.substr(pos
,1)+"!");
256 if (!found
) { // Must be a free form parameter...
257 if (free_parms_count
<cmd_args
.size()){ // Space available
258 *(cmd_args
[free_parms_count
].target
)=argv
[arg_no
];
259 *(cmd_args
[free_parms_count
].status
)=1;
261 } else { // No more space for free form parameters!
262 messages
.insert(messages
.begin(),"Too many arguments!");
266 if (!messages
.empty()){
267 messages
.insert(messages
.begin(),"Error!");
268 messages
.insert(messages
.begin(),"");
275 *\brief Extract a value from a configuration file line or
276 * a command line argument
278 string
configuration_manager::analyse_string(const string
& line
){
280 unsigned int pos
=line
.find("=");
281 if (pos
!=string::npos
) result
=line
.substr(pos
+1);
286 *\brief Extract a boolean value out of a string, defaulting to true
288 * Used for commandline switches
290 bool configuration_manager::analyse_bool(const string
& data
){
291 if ((data
.find("false",0)!=string::npos
)||
292 (data
.find("0",0)!=string::npos
)||
293 (data
.find("no",0)!=string::npos
)) return false;
299 *\brief Extract a boolean value out of a string, defaulting to false
301 * Used for configuration file switches
303 bool configuration_manager::analyse_bool_false(const string
& data
){
304 if ((data
.find("true",0)!=string::npos
)||
305 (data
.find("1",0)!=string::npos
)||
306 (data
.find("yes",0)!=string::npos
)) return true;
312 *brief Read in and parse a configuration file.
313 *\arg file String containing the filename
314 *\return Empty Vector or Vector full of strings containing
315 * the error message(s).
317 vector
<string
> configuration_manager::read_file(string filename
){
318 vector
<string
>result
;
319 FILE * fp
=fopen(filename
.c_str(),"r");
321 result
.insert(result
.end(),"Could not open file: "+filename
);
324 while(fgets(buffer
,1000,fp
)){
325 if (buffer
[strlen(buffer
)-1]=='\n')// Cut trailing newline
326 buffer
[strlen(buffer
)-1]=0;
327 if (strlen (buffer
)){
328 char*l2
=buffer
+strspn(buffer
," \t");
331 unsigned int pos
=argstring
.find("=");
333 result
.insert(result
.end(),"In File "+filename
+": Line beginning with '='!");
338 // Look for long switches.
339 for (unsigned int switch_no
=0;switch_no
<option_switches
.size();switch_no
++){
340 string switch_name
=option_switches
[switch_no
].longname
;
341 if (argstring
.compare(0,switch_name
.length(),switch_name
,0,switch_name
.length())==0){
342 string value
=analyse_string(argstring
);
343 //result.insert(result.end(),"argstring:\""+argstring+"\"");
344 bool res
=analyse_bool_false(value
);
345 if(option_switches
[switch_no
].allow_conffile
==true){
346 *(option_switches
[switch_no
].status
)=res
;
348 result
.insert(result
.end(),"In File "+filename
+": Switch \""
349 +option_switches
[switch_no
].longname
350 +"\" is not allowed in config files!");
357 // Look for long values.
358 for (unsigned int value_no
=0;value_no
<option_values
.size();value_no
++){
359 string value_name
=option_values
[value_no
].longname
;
360 if (argstring
.compare(0,value_name
.length(),value_name
,0,value_name
.length())==0){
361 string value
=analyse_string(argstring
);
362 *(option_values
[value_no
].status
)=1;
364 if(option_values
[value_no
].allow_conffile
==true){
365 *(option_values
[value_no
].status
)=1;
366 *(option_values
[value_no
].target
)=value
;
368 result
.insert(result
.end(),"In File "+filename
+": Option value \""
369 +option_values
[value_no
].longname
370 +"\" is not allowed in config files!");
377 result
.insert(result
.end(),"In File "+filename
+": Unknown option: "+argstring
+"!");
384 } // fp==0 else branch
386 if (!result
.empty()){
387 result
.insert(result
.begin(),"Error!");
388 result
.insert(result
.begin(),"");
395 *\brief Generate help.
396 *\arg target Reference to a vector<string> to which lots of helpful
397 * strings are appended.
399 void configuration_manager::get_help(vector
<string
> & target
){
400 target
.insert(target
.end(),"");
401 string line
="Usage: "+app_name
;
403 for (vector
<opt_switch_t
>::iterator parm_p
=option_switches
.begin();parm_p
<option_switches
.end();parm_p
++){
404 string addstr
=" [-"+parm_p
->shortname
;
406 if (line
.length()+addstr
.length()>MAX_LINE_LENGTH
){
407 target
.insert(target
.end(),line
);
408 line
=string(7+app_name
.length(),' ');
413 for (vector
<opt_value_t
>::iterator parm_p
=option_values
.begin();parm_p
<option_values
.end();parm_p
++){
414 string addstr
=" [-"+parm_p
->shortname
;
415 addstr
+=parm_p
->placeholder
;
417 if (line
.length()+addstr
.length()>MAX_LINE_LENGTH
){
418 target
.insert(target
.end(),line
);
419 line
=string(7+app_name
.length(),' ');
424 for (vector
<cmd_arg_t
>::iterator parm_p
=cmd_args
.begin();parm_p
<cmd_args
.end();parm_p
++){
425 if (line
.length()+parm_p
->placeholder
.length()>MAX_LINE_LENGTH
){
426 target
.insert(target
.end(),line
);
427 line
=string(7+app_name
.length(),' ');
429 line
+=" ["+parm_p
->placeholder
+"]";
431 target
.insert(target
.end(),line
);
433 /* Here comes the documentation output. */
435 vector
<string
> left
,right
;
436 unsigned int max_width
;
438 /* Output switches? */
439 if (option_switches
.size()){
440 target
.insert(target
.end(),"");
441 target
.insert(target
.end(),"Switches:");
443 left
=vector
<string
>();
444 right
=vector
<string
>();
447 /* Now lets insert switches into left and right */
448 for (unsigned int sw
=0; sw
<option_switches
.size();sw
++){
449 opt_switch_t akt_sw
=option_switches
[sw
];
450 string rline
=akt_sw
.description
;
451 string lline
="-"+akt_sw
.shortname
+", --"+akt_sw
.longname
;
452 left
.insert(left
.end(),lline
);
453 right
.insert(right
.end(),rline
);
456 // Determine maximal width for left column
458 for (unsigned int c
=0; c
<left
.size();c
++)
459 if(left
[c
].length()>max_width
) max_width
=left
[c
].length();
461 /* output all the mess */
462 for (unsigned int c
=0; c
<left
.size();c
++){
463 string
nl(max_width
,' ');
464 nl
.replace(0,left
[c
].length(),left
[c
]);
466 while (nl
.length()>80){ // Too long???
467 int limit
=nl
.find_last_of(' ',MAX_LINE_LENGTH
+1);
468 target
.insert(target
.end(),nl
.substr(0,limit
));
469 nl
=string(max_width
+2,' ')+nl
.substr(limit
+1);
471 target
.insert(target
.end(),nl
);
476 if (option_values
.size()){
477 target
.insert(target
.end(),"");
478 target
.insert(target
.end(),"Option values:");
480 left
=vector
<string
>();
481 right
=vector
<string
>();
484 for (unsigned int val
=0; val
<option_values
.size();val
++){
485 opt_value_t akt_val
=option_values
[val
];
486 string rline
=akt_val
.description
;
487 string lline
=" -"+akt_val
.shortname
+akt_val
.placeholder
+", --"+
488 akt_val
.longname
+"="+akt_val
.placeholder
;
489 left
.insert(left
.end(),lline
);
490 right
.insert(right
.end(),rline
);
493 // Determine maximal width for left column
494 for (unsigned int c
=0; c
<left
.size();c
++)
495 if(left
[c
].length()>max_width
) max_width
=left
[c
].length();
496 /* output all the mess */
497 for (unsigned int c
=0; c
<left
.size();c
++){
498 string
nl(max_width
,' ');
499 nl
.replace(0,left
[c
].length(),left
[c
]);
501 while (nl
.length()>80){ // Too long???
502 int limit
=nl
.find_last_of(' ',MAX_LINE_LENGTH
+1);
503 target
.insert(target
.end(),nl
.substr(0,limit
));
504 nl
=string(max_width
+2,' ')+nl
.substr(limit
+1);
506 target
.insert(target
.end(),nl
);
510 /* Output the Arguments */
511 if (cmd_args
.size()){
512 target
.insert(target
.end(),"");
513 target
.insert(target
.end(),"Arguments:");
515 left
=vector
<string
>();
516 right
=vector
<string
>();
519 for (unsigned int arg
=0; arg
<cmd_args
.size();arg
++){
520 cmd_arg_t akt_arg
=cmd_args
[arg
];
521 left
.insert(left
.end(),akt_arg
.placeholder
);
522 right
.insert(right
.end(),akt_arg
.description
);
525 // 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();
530 for (unsigned int c
=0; c
<left
.size();c
++){
531 string
nl(max_width
,' ');
532 nl
.replace(0,left
[c
].length(),left
[c
]);
534 while (nl
.length()>MAX_LINE_LENGTH
){ // Too long???
535 int limit
=nl
.find_last_of(' ',MAX_LINE_LENGTH
+1);
536 // printf("limit:%i\n",limit);
537 target
.insert(target
.end(),nl
.substr(0,limit
));
538 nl
=string(max_width
+2,' ')+nl
.substr(limit
+1);
540 target
.insert(target
.end(),nl
);
542 target
.insert(target
.end(),"");
547 *\brief Generate help.
548 *\return A vector containing many helpful strings for the user.
550 vector
<string
> configuration_manager::get_help(){
551 vector
<string
> result
;
557 /**************************************************/
560 configuration_manager::opt_value_t::opt_value_t(const string
& shortname
,
561 const string
& longname
,
562 const string
& description
,
565 const string
& placeholder
,
566 const bool & allow_conffile
,
567 const bool & allow_cmdline
569 this->shortname
=shortname
;
570 this->longname
=longname
;
571 this->description
=description
;
574 this->placeholder
=placeholder
;
575 this->allow_conffile
=allow_conffile
;
576 this->allow_cmdline
=allow_cmdline
;
577 if (status
) *status
=0;
581 configuration_manager::opt_switch_t::opt_switch_t(const string
& shortname
,
582 const string
& longname
,
583 const string
& description
,
585 const bool & allow_conffile
,
586 const bool & allow_cmdline
){
587 this->shortname
=shortname
;
588 this->longname
=longname
;
589 this->description
=description
;
592 this->placeholder
=placeholder
;
593 this->allow_conffile
=allow_conffile
;
594 this->allow_cmdline
=allow_cmdline
;
598 configuration_manager::cmd_arg_t::cmd_arg_t(const string
& placeholder
,
599 const string
& description
,
602 this->placeholder
=placeholder
;
603 this->description
=description
;