*** empty log message ***
[h316.git] / pc-tools / ldc2 / src / configuration_manager.cpp
CommitLineData
4741aa72 1/******************************************************************************
2 *
3 * LDC2 source code
4 *
ea78fc91 5 * $Date: 2007/03/26 04:05:37 $
4741aa72 6 * $Author: hachti $
7 *
8 * $Log: configuration_manager.cpp,v $
ea78fc91 9 * Revision 2.1 2007/03/26 04:05:37 hachti
10 * *** empty log message ***
11 *
12 * Revision 2.0 2007-03-26 01:00:38 hachti
4741aa72 13 * *** empty log message ***
14 *
15 *
16 ******************************************************************************/
17
ca5fce3a 18#include "configuration_manager.hh"
ad324d29 19#include <stdio.h>
50c86ded 20#include <stdlib.h>
21#include <fcntl.h>
ad324d29 22
909d3603 23#define MAX_LINE_LENGTH 80
24
ad324d29 25/*!
26 *\brief Constructor.
27 *
ca5fce3a 28 * This constructor makes a new configuration_manager ready to use.
ad324d29 29 *\arg name Name of the application as mentioned in the
30 * "Use: <appname> ..." help message.
31 */
ca5fce3a 32configuration_manager::configuration_manager(string name){
040533c5 33 app_name=name;
ad324d29 34}
35
36
37/*!
ca5fce3a 38 *\brief Add a new configuration value to be searched for.
ad324d29 39 *\param shortname A character for the short form.
909d3603 40 * For example the o in -o
ad324d29 41 *\param longname The double dash longname. For example
909d3603 42 * output_file in --output_file=
ca5fce3a 43 *\param description A detailed description of the value.
ad324d29 44 *\param status Pointer to an integer. Will be set to 1 if arg found.
ea4c19a4 45 *\param target Pointer to string to put the value in.
46 *\param placeholder A placeholder for the documentation.
ca5fce3a 47 * For example <filename> in -f<filename>
ea78fc91 48 *\param allow_cmdline Specifies if this value may be specified on the
49 * command line.
50 *\param allow_conffile Specifies if this value may be specified in a
51 * configuration file.
52 *
ad324d29 53 */
909d3603 54void configuration_manager::add_option_value (const string & shortname,
55 const string & longname,
56 const string & description,
57 int * status,
58 string * target,
59 const string & placeholder,
60 const bool & allow_cmdline,
61 const bool & allow_conffile
62 ){
ad324d29 63
909d3603 64 if(status!=NULL) if (target!=NULL)
65 option_values.insert(option_values.end(),
66 opt_value_t(shortname,
67 longname,
68 description,
69 status,
70 target,
71 placeholder,
72 allow_cmdline,
73 allow_conffile
74 )
75 );
ad324d29 76}
77
78
909d3603 79
80/*!
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
85 * help in --help
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
89 * command line.
90 *\param allow_conffile Specifies whether the switch is acceptable
91 * in a configuration file.
92 */
93void configuration_manager::add_option_switch (const string & shortname,
94 const string & longname,
95 const string & description,
96 int * status,
97 const bool & allow_cmdline,
98 const bool & allow_conffile
99 ){
100
101 if(status!=NULL)
102 option_switches.insert(option_switches.end(),
103 opt_switch_t(shortname,
104 longname,
105 description,
106 status,
107 allow_cmdline,
108 allow_conffile)
109 );
110}
111
ad324d29 112/*!
113 *\brief Add an accepted argument to the argument reader.
114 *\param placeholder Something like "<input-file>".
115 *\param description Text describing the argument.
909d3603 116 *\param status A pointer to a status variable. Will be set to 0 by default.
ad324d29 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.
119 *
120 *\note Arguments are filled in the order of adding them to the
121 * argument reader.
122 * There would be no other way to determine the order.
123 */
909d3603 124void configuration_manager::add_argument(const string & description,
125 int * status,
126 string * target,
127 const string & placeholder){
128
129
130
ad324d29 131 if (target!=NULL) if(status!=NULL)
909d3603 132 cmd_args.insert(cmd_args.end(),cmd_arg_t(placeholder,
133 description,
134 status,
135 target));
ad324d29 136}
137
138
139/*!
ea78fc91 140 *\brief Read in the args passed to main().
ad324d29 141 *\returns empty vector on success or the error messages to be output.
142 */
ca5fce3a 143vector<string> configuration_manager::read_args(int argc, char ** args){
909d3603 144
145 vector<string> messages;
ad324d29 146 vector<string> argv;
ad324d29 147
909d3603 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));
150
151 // Counter for the free command line parameters.
ad324d29 152 unsigned int free_parms_count=0;
909d3603 153
154 // Loop through the command line arguments
155 for (unsigned int arg_no=0;arg_no<argv.size();arg_no++){
156 bool found=false;
157 string argstring;
ad324d29 158
909d3603 159 // Look for long parameters.
160 if ((argv[arg_no].substr(0,2)=="--")&&(free_parms_count==0)){
161 argstring=argv[arg_no].substr(2);
162
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);
fed2c751 169 if(option_switches[switch_no].allow_cmdline==true){
170 *(option_switches[switch_no].status)=result;
171 }else{
172 messages.insert(messages.end(),"Switch \"--"
173 +option_switches[switch_no].longname
174 +"\" is not allowed on the command line!");
175 }
909d3603 176 found=true;
fed2c751 177 break;
909d3603 178 }
179 }
180
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);
fed2c751 186 if(option_values[value_no].allow_cmdline==true){
187 *(option_values[value_no].status)=1;
188 *(option_values[value_no].target)=value;
189 }else{
190 messages.insert(messages.end(),"Option value \"--"
191 +option_values[value_no].longname
192 +"\" is not allowed on the command line!");
193 }
909d3603 194 found=true;
fed2c751 195 break;
ad324d29 196 }
909d3603 197 }
198 if (! found) {
199 messages.insert(messages.begin(),"Unknown option: --"+argstring+"!");
200 found=true; // Oh!
201 }
202 }
203
204 // Look for short parameters
205 if ((argv[arg_no].substr(0,1)=="-")&&(free_parms_count==0)&&(!found)){ // Short parameters
206 found=true;
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;
210
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){
fed2c751 215 if(option_switches[switch_no].allow_cmdline==true){
216 *(option_switches[switch_no].status)=1;
217 }else{
218 messages.insert(messages.end(),"Switch \"-"
219 +option_switches[switch_no].shortname
220 +"\" is not allowed on the command line!");
221 }
909d3603 222 short_found=true;
223 pos+=short_name.length()-1;
fed2c751 224 break;
909d3603 225 }
226 }
227
228 // Now, find short values
229 if (!short_found){
230 for (unsigned int value_no=0;value_no<option_values.size();value_no++){
231 string short_name=option_values[value_no].shortname;
232
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
239 short_found=true;
240 } else { // No argument left!
241 messages.insert(messages.begin(),"Missing value for -"+short_name+"!");
242 short_found=true;
243 }
244 } else {
fed2c751 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);
248 }else{
249 messages.insert(messages.end(),"Option value \"-"
250 +option_values[value_no].shortname
251 +"\" is not allowed on the command line!");
252 }
909d3603 253 pos=argstring.length(); // Do not analyse other characters.
254 short_found=true;
fed2c751 255 break;
909d3603 256 }
257 }
258 }
259 }
260 if (!short_found) messages.insert(messages.begin(),"Unknown Option: -"+argstring.substr(pos,1)+"!");
261 }
262 }
263
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;
ad324d29 268 free_parms_count++;
909d3603 269 } else { // No more space for free form parameters!
270 messages.insert(messages.begin(),"Too many arguments!");
ad324d29 271 }
909d3603 272 }
273 }
274 if (!messages.empty()){
275 messages.insert(messages.begin(),"Error!");
276 messages.insert(messages.begin(),"");
ad324d29 277 }
909d3603 278 return messages;
279}
50c86ded 280
909d3603 281
282/*!
283 *\brief Extract a value from a configuration file line or
284 * a command line argument
285 */
286string configuration_manager::analyse_string(const string & line){
287 string result="";
288 unsigned int pos=line.find("=");
289 if (pos!=string::npos) result=line.substr(pos+1);
ad324d29 290 return result;
291}
292
909d3603 293/*!
fed2c751 294 *\brief Extract a boolean value out of a string, defaulting to true
295 *
296 * Used for commandline switches
909d3603 297 */
298bool configuration_manager::analyse_bool(const string & data){
fed2c751 299 if ((data.find("false",0)!=string::npos)||
300 (data.find("0",0)!=string::npos)||
301 (data.find("no",0)!=string::npos)) return false;
302 else return true;
303}
304
305
306/*!
307 *\brief Extract a boolean value out of a string, defaulting to false
308 *
309 * Used for configuration file switches
310 */
311bool 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;
315 else return false;
909d3603 316}
317
fed2c751 318
50c86ded 319/*!
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).
324 */
325vector<string> configuration_manager::read_file(string filename){
326 vector<string>result;
327 FILE * fp=fopen(filename.c_str(),"r");
328 if (! fp) {
fed2c751 329 result.insert(result.end(),"Could not open file: "+filename);
330 } else {
331 char buffer[1000];
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");
337 if (l2[0]!='#'){
338 string argstring=l2;
339 unsigned int pos=argstring.find("=");
340 if (pos==0){
341 result.insert(result.end(),"In File "+filename+": Line beginning with '='!");
342 }
343
344 bool found=false;
345
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;
355 }else{
356 result.insert(result.end(),"In File "+filename+": Switch \""
357 +option_switches[switch_no].longname
358 +"\" is not allowed in config files!");
359 }
360 found=true;
361 break;
362 }
363 }
364
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;
371
372 if(option_values[value_no].allow_conffile==true){
373 *(option_values[value_no].status)=1;
374 *(option_values[value_no].target)=value;
375 }else{
376 result.insert(result.end(),"In File "+filename+": Option value \""
377 +option_values[value_no].longname
378 +"\" is not allowed in config files!");
379 }
380 found=true;
381 break;
382 }
383 }
384 if (! found) {
385 result.insert(result.end(),"In File "+filename+": Unknown option: "+argstring+"!");
386 }
387
388 } // if not #
389 } // if (strlen())
390 } // while(...)
391 fclose (fp);
392 } // fp==0 else branch
50c86ded 393
fed2c751 394 if (!result.empty()){
395 result.insert(result.begin(),"Error!");
396 result.insert(result.begin(),"");
397 }
50c86ded 398 return result;
399}
400
fed2c751 401
ad324d29 402/*!
403 *\brief Generate help.
404 *\arg target Reference to a vector<string> to which lots of helpful
405 * strings are appended.
406 */
ca5fce3a 407void configuration_manager::get_help(vector<string> & target){
ad324d29 408 target.insert(target.end(),"");
409 string line="Usage: "+app_name;
909d3603 410
411 for (vector<opt_switch_t>::iterator parm_p=option_switches.begin();parm_p<option_switches.end();parm_p++){
ad324d29 412 string addstr=" [-"+parm_p->shortname;
ad324d29 413 addstr+="]";
909d3603 414 if (line.length()+addstr.length()>MAX_LINE_LENGTH){
ad324d29 415 target.insert(target.end(),line);
416 line=string(7+app_name.length(),' ');
417 }
418 line+=addstr;
419 }
909d3603 420
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;
424 addstr+="]";
425 if (line.length()+addstr.length()>MAX_LINE_LENGTH){
426 target.insert(target.end(),line);
427 line=string(7+app_name.length(),' ');
428 }
429 line+=addstr;
430 }
431
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){
ad324d29 434 target.insert(target.end(),line);
435 line=string(7+app_name.length(),' ');
436 }
437 line+=" ["+parm_p->placeholder+"]";
438 }
439 target.insert(target.end(),line);
440
909d3603 441 /* Here comes the documentation output. */
442
ad324d29 443 vector<string> left,right;
909d3603 444 unsigned int max_width;
ad324d29 445
909d3603 446 /* Output switches? */
447 if (option_switches.size()){
448 target.insert(target.end(),"");
449 target.insert(target.end(),"Switches:");
450
451 left=vector<string>();
452 right=vector<string>();
453 max_width=0;
454
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);
462 }
463
464 // Determine maximal width for left column
465 max_width=0;
466 for (unsigned int c=0; c<left.size();c++)
467 if(left[c].length()>max_width) max_width=left[c].length();
468
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]);
473 nl+=" "+right[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);
478 }
479 target.insert(target.end(),nl);
480 }
ad324d29 481 }
482
909d3603 483 // Output values
484 if (option_values.size()){
ad324d29 485 target.insert(target.end(),"");
909d3603 486 target.insert(target.end(),"Option values:");
487
488 left=vector<string>();
489 right=vector<string>();
490 max_width=0;
491
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+", --"+
fed2c751 496 akt_val.longname+"="+akt_val.placeholder;
909d3603 497 left.insert(left.end(),lline);
498 right.insert(right.end(),rline);
499 }
500
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]);
508 nl+=" "+right[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);
513 }
514 target.insert(target.end(),nl);
515 }
ad324d29 516 }
517
909d3603 518 /* Output the Arguments */
519 if (cmd_args.size()){
ad324d29 520 target.insert(target.end(),"");
521 target.insert(target.end(),"Arguments:");
ad324d29 522
909d3603 523 left=vector<string>();
524 right=vector<string>();
525 max_width=0;
526
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);
531 }
532
533 // Determine maximal width for left column
534 max_width=0;
535 for (unsigned int c=0; c<left.size();c++)
536 if(left[c].length()>max_width) max_width=left[c].length();
537
538 for (unsigned int c=0; c<left.size();c++){
539 string nl(max_width,' ');
540 nl.replace(0,left[c].length(),left[c]);
541 nl+=" "+right[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);
547 }
548 target.insert(target.end(),nl);
549 }
ad324d29 550 target.insert(target.end(),"");
909d3603 551 }
ad324d29 552}
553
554/*!
555 *\brief Generate help.
556 *\return A vector containing many helpful strings for the user.
557 */
ca5fce3a 558vector<string> configuration_manager::get_help(){
ad324d29 559 vector<string> result;
560 get_help(result);
561 return result;
562}
563
564
565/**************************************************/
566
909d3603 567
568configuration_manager::opt_value_t::opt_value_t(const string & shortname,
569 const string & longname,
570 const string & description,
571 int * status,
572 string * target,
573 const string & placeholder,
574 const bool & allow_conffile,
575 const bool & allow_cmdline
576 ){
577 this->shortname=shortname;
578 this->longname=longname;
579 this->description=description;
580 this->status=status;
581 this->target=target;
582 this->placeholder=placeholder;
583 this->allow_conffile=allow_conffile;
584 this->allow_cmdline=allow_cmdline;
ad324d29 585 if (status) *status=0;
586}
587
909d3603 588
589configuration_manager::opt_switch_t::opt_switch_t(const string & shortname,
590 const string & longname,
591 const string & description,
592 int * status,
593 const bool & allow_conffile,
594 const bool & allow_cmdline){
595 this->shortname=shortname;
596 this->longname=longname;
597 this->description=description;
598 this->status=status;
599 this->target=target;
600 this->placeholder=placeholder;
601 this->allow_conffile=allow_conffile;
602 this->allow_cmdline=allow_cmdline;
ad324d29 603}
909d3603 604
605
606configuration_manager::cmd_arg_t::cmd_arg_t(const string & placeholder,
607 const string & description,
608 int * status,
609 string * target){
610 this->placeholder=placeholder;
611 this->description=description;
612 this->status=status;
613 this->target=target;
909d3603 614}