*** empty log message ***
[h316.git] / pc-tools / ldc2 / src / configuration_manager.cpp
CommitLineData
4741aa72 1/******************************************************************************
2 *
3 * LDC2 source code
4 *
eabafcca 5 * $Date: 2008/08/25 21:02:24 $
4741aa72 6 * $Author: hachti $
7 *
8 * $Log: configuration_manager.cpp,v $
eabafcca 9 * Revision 2.3 2008/08/25 21:02:24 hachti
10 * *** empty log message ***
11 *
12 * Revision 2.2 2007-06-15 12:46:04 hachti
e7ba2001 13 * Some small changes in configuration_manager output - added (c/f) info.
14 * Changed order in tool.hh:add_unique....
15 *
16 * Revision 2.1 2007-03-26 04:05:37 hachti
ea78fc91 17 * *** empty log message ***
18 *
19 * Revision 2.0 2007-03-26 01:00:38 hachti
4741aa72 20 * *** empty log message ***
21 *
22 *
23 ******************************************************************************/
24
ca5fce3a 25#include "configuration_manager.hh"
ad324d29 26#include <stdio.h>
50c86ded 27#include <stdlib.h>
28#include <fcntl.h>
eabafcca 29#include <string.h>
ad324d29 30
909d3603 31#define MAX_LINE_LENGTH 80
32
ad324d29 33/*!
34 *\brief Constructor.
35 *
ca5fce3a 36 * This constructor makes a new configuration_manager ready to use.
ad324d29 37 *\arg name Name of the application as mentioned in the
38 * "Use: <appname> ..." help message.
39 */
ca5fce3a 40configuration_manager::configuration_manager(string name){
040533c5 41 app_name=name;
ad324d29 42}
43
44
45/*!
ca5fce3a 46 *\brief Add a new configuration value to be searched for.
ad324d29 47 *\param shortname A character for the short form.
909d3603 48 * For example the o in -o
ad324d29 49 *\param longname The double dash longname. For example
909d3603 50 * output_file in --output_file=
ca5fce3a 51 *\param description A detailed description of the value.
ad324d29 52 *\param status Pointer to an integer. Will be set to 1 if arg found.
ea4c19a4 53 *\param target Pointer to string to put the value in.
54 *\param placeholder A placeholder for the documentation.
ca5fce3a 55 * For example <filename> in -f<filename>
ea78fc91 56 *\param allow_cmdline Specifies if this value may be specified on the
57 * command line.
58 *\param allow_conffile Specifies if this value may be specified in a
59 * configuration file.
60 *
ad324d29 61 */
909d3603 62void configuration_manager::add_option_value (const string & shortname,
63 const string & longname,
64 const string & description,
65 int * status,
66 string * target,
67 const string & placeholder,
68 const bool & allow_cmdline,
69 const bool & allow_conffile
70 ){
ad324d29 71
909d3603 72 if(status!=NULL) if (target!=NULL)
73 option_values.insert(option_values.end(),
74 opt_value_t(shortname,
75 longname,
76 description,
77 status,
78 target,
79 placeholder,
80 allow_cmdline,
81 allow_conffile
82 )
83 );
ad324d29 84}
85
86
909d3603 87
88/*!
89 *\brief Add a new configuration switch to be searched for.
90 *\param shortname A character for the short form.
91 * For example the h in -h
92 *\param longname The double dash longname. For example
93 * help in --help
94 *\param description A detailed description of the value.
95 *\param status Pointer to an integer. Will be set to 1 if arg found.
96 *\param allow_cmdline Specifies whether the switch is acceptable on the
97 * command line.
98 *\param allow_conffile Specifies whether the switch is acceptable
99 * in a configuration file.
100 */
101void configuration_manager::add_option_switch (const string & shortname,
102 const string & longname,
103 const string & description,
104 int * status,
105 const bool & allow_cmdline,
106 const bool & allow_conffile
107 ){
108
109 if(status!=NULL)
110 option_switches.insert(option_switches.end(),
111 opt_switch_t(shortname,
112 longname,
113 description,
114 status,
115 allow_cmdline,
116 allow_conffile)
117 );
118}
119
ad324d29 120/*!
121 *\brief Add an accepted argument to the argument reader.
122 *\param placeholder Something like "<input-file>".
123 *\param description Text describing the argument.
909d3603 124 *\param status A pointer to a status variable. Will be set to 0 by default.
ad324d29 125 * If the argument is filled in, it will be set to 1.
126 *\param target A pointer to a c++ string where the argument goes to.
127 *
128 *\note Arguments are filled in the order of adding them to the
129 * argument reader.
130 * There would be no other way to determine the order.
131 */
909d3603 132void configuration_manager::add_argument(const string & description,
133 int * status,
134 string * target,
135 const string & placeholder){
136
137
138
ad324d29 139 if (target!=NULL) if(status!=NULL)
909d3603 140 cmd_args.insert(cmd_args.end(),cmd_arg_t(placeholder,
141 description,
142 status,
143 target));
ad324d29 144}
145
146
147/*!
ea78fc91 148 *\brief Read in the args passed to main().
ad324d29 149 *\returns empty vector on success or the error messages to be output.
150 */
ca5fce3a 151vector<string> configuration_manager::read_args(int argc, char ** args){
909d3603 152
153 vector<string> messages;
ad324d29 154 vector<string> argv;
ad324d29 155
909d3603 156 // First we make a C++ string vector out of the **argv.
157 for (char ** akt=args+1; *akt ;akt++) argv.insert(argv.end(),string(*akt));
158
159 // Counter for the free command line parameters.
ad324d29 160 unsigned int free_parms_count=0;
909d3603 161
162 // Loop through the command line arguments
163 for (unsigned int arg_no=0;arg_no<argv.size();arg_no++){
164 bool found=false;
165 string argstring;
ad324d29 166
909d3603 167 // Look for long parameters.
e7ba2001 168 // if ((argv[arg_no].substr(0,2)=="--")&&(free_parms_count==0)){
169 if ((argv[arg_no].substr(0,2)=="--")){ // Changed for comfort.
909d3603 170 argstring=argv[arg_no].substr(2);
171
172 // Look for long switches.
173 for (unsigned int switch_no=0;switch_no<option_switches.size();switch_no++){
174 string switch_name=option_switches[switch_no].longname;
175 if (argstring.compare(0,switch_name.length(),switch_name,0,switch_name.length())==0){
176 string value=analyse_string(argstring);
177 bool result=analyse_bool(value);
fed2c751 178 if(option_switches[switch_no].allow_cmdline==true){
179 *(option_switches[switch_no].status)=result;
180 }else{
181 messages.insert(messages.end(),"Switch \"--"
182 +option_switches[switch_no].longname
183 +"\" is not allowed on the command line!");
184 }
909d3603 185 found=true;
fed2c751 186 break;
909d3603 187 }
188 }
189
190 // Look for long values.
191 for (unsigned int value_no=0;value_no<option_values.size();value_no++){
192 string value_name=option_values[value_no].longname;
193 if (argstring.compare(0,value_name.length(),value_name,0,value_name.length())==0){
194 string value=analyse_string(argstring);
fed2c751 195 if(option_values[value_no].allow_cmdline==true){
196 *(option_values[value_no].status)=1;
197 *(option_values[value_no].target)=value;
198 }else{
199 messages.insert(messages.end(),"Option value \"--"
200 +option_values[value_no].longname
201 +"\" is not allowed on the command line!");
202 }
909d3603 203 found=true;
fed2c751 204 break;
ad324d29 205 }
909d3603 206 }
207 if (! found) {
208 messages.insert(messages.begin(),"Unknown option: --"+argstring+"!");
209 found=true; // Oh!
210 }
211 }
212
213 // Look for short parameters
e7ba2001 214 // if ((argv[arg_no].substr(0,1)=="-")&&(free_parms_count==0)&&(!found)){ // Short parameters
215 if ((argv[arg_no].substr(0,1)=="-")&&(!found)){ // Changed for comfort.
909d3603 216 found=true;
217 argstring=argv[arg_no].substr(1); // Reassign, with one more character now
218 for (unsigned int pos=0;pos < argstring.length();pos++){
219 bool short_found=false;
220
221 // First, find short switches
222 for (unsigned int switch_no=0;switch_no<option_switches.size();switch_no++){
223 string short_name=option_switches[switch_no].shortname;
224 if (short_name.compare(0,short_name.length(),argstring,pos,short_name.length())==0){
fed2c751 225 if(option_switches[switch_no].allow_cmdline==true){
226 *(option_switches[switch_no].status)=1;
227 }else{
228 messages.insert(messages.end(),"Switch \"-"
229 +option_switches[switch_no].shortname
230 +"\" is not allowed on the command line!");
231 }
909d3603 232 short_found=true;
233 pos+=short_name.length()-1;
fed2c751 234 break;
909d3603 235 }
236 }
237
238 // Now, find short values
239 if (!short_found){
240 for (unsigned int value_no=0;value_no<option_values.size();value_no++){
241 string short_name=option_values[value_no].shortname;
242
243 if (short_name.compare(0,short_name.length(),argstring,pos,short_name.length())==0){
244 if (pos==argstring.length()-1){ // Last character, where's the value?
245 if (arg_no<argv.size()-1){ // Take next complete argument as value!
246 *(option_values[value_no].status)=1;
247 *(option_values[value_no].target)=argv[arg_no+1];
248 arg_no+=1; // Consume the next argument
249 short_found=true;
250 } else { // No argument left!
251 messages.insert(messages.begin(),"Missing value for -"+short_name+"!");
252 short_found=true;
253 }
254 } else {
fed2c751 255 if(option_values[value_no].allow_cmdline==true){
256 *(option_values[value_no].status)=1;
257 *(option_values[value_no].target)=argstring.substr(pos+1);
258 }else{
259 messages.insert(messages.end(),"Option value \"-"
260 +option_values[value_no].shortname
261 +"\" is not allowed on the command line!");
262 }
909d3603 263 pos=argstring.length(); // Do not analyse other characters.
264 short_found=true;
fed2c751 265 break;
909d3603 266 }
267 }
268 }
269 }
270 if (!short_found) messages.insert(messages.begin(),"Unknown Option: -"+argstring.substr(pos,1)+"!");
271 }
272 }
273
274 if (!found) { // Must be a free form parameter...
275 if (free_parms_count<cmd_args.size()){ // Space available
276 *(cmd_args[free_parms_count].target)=argv[arg_no];
277 *(cmd_args[free_parms_count].status)=1;
ad324d29 278 free_parms_count++;
909d3603 279 } else { // No more space for free form parameters!
280 messages.insert(messages.begin(),"Too many arguments!");
ad324d29 281 }
909d3603 282 }
283 }
284 if (!messages.empty()){
285 messages.insert(messages.begin(),"Error!");
286 messages.insert(messages.begin(),"");
ad324d29 287 }
909d3603 288 return messages;
289}
50c86ded 290
909d3603 291
292/*!
293 *\brief Extract a value from a configuration file line or
294 * a command line argument
295 */
296string configuration_manager::analyse_string(const string & line){
297 string result="";
298 unsigned int pos=line.find("=");
299 if (pos!=string::npos) result=line.substr(pos+1);
ad324d29 300 return result;
301}
302
909d3603 303/*!
fed2c751 304 *\brief Extract a boolean value out of a string, defaulting to true
305 *
306 * Used for commandline switches
909d3603 307 */
308bool configuration_manager::analyse_bool(const string & data){
fed2c751 309 if ((data.find("false",0)!=string::npos)||
310 (data.find("0",0)!=string::npos)||
311 (data.find("no",0)!=string::npos)) return false;
312 else return true;
313}
314
315
316/*!
317 *\brief Extract a boolean value out of a string, defaulting to false
318 *
319 * Used for configuration file switches
320 */
321bool configuration_manager::analyse_bool_false(const string & data){
322 if ((data.find("true",0)!=string::npos)||
323 (data.find("1",0)!=string::npos)||
324 (data.find("yes",0)!=string::npos)) return true;
325 else return false;
909d3603 326}
327
fed2c751 328
50c86ded 329/*!
330 *brief Read in and parse a configuration file.
331 *\arg file String containing the filename
332 *\return Empty Vector or Vector full of strings containing
333 * the error message(s).
334 */
335vector<string> configuration_manager::read_file(string filename){
336 vector<string>result;
337 FILE * fp=fopen(filename.c_str(),"r");
338 if (! fp) {
fed2c751 339 result.insert(result.end(),"Could not open file: "+filename);
340 } else {
341 char buffer[1000];
342 while(fgets(buffer,1000,fp)){
343 if (buffer[strlen(buffer)-1]=='\n')// Cut trailing newline
344 buffer[strlen(buffer)-1]=0;
345 if (strlen (buffer)){
346 char*l2=buffer+strspn(buffer," \t");
347 if (l2[0]!='#'){
348 string argstring=l2;
349 unsigned int pos=argstring.find("=");
350 if (pos==0){
351 result.insert(result.end(),"In File "+filename+": Line beginning with '='!");
352 }
353
354 bool found=false;
355
356 // Look for long switches.
357 for (unsigned int switch_no=0;switch_no<option_switches.size();switch_no++){
358 string switch_name=option_switches[switch_no].longname;
359 if (argstring.compare(0,switch_name.length(),switch_name,0,switch_name.length())==0){
360 string value=analyse_string(argstring);
361 //result.insert(result.end(),"argstring:\""+argstring+"\"");
362 bool res=analyse_bool_false(value);
363 if(option_switches[switch_no].allow_conffile==true){
364 *(option_switches[switch_no].status)=res;
365 }else{
366 result.insert(result.end(),"In File "+filename+": Switch \""
367 +option_switches[switch_no].longname
368 +"\" is not allowed in config files!");
369 }
370 found=true;
371 break;
372 }
373 }
374
375 // Look for long values.
376 for (unsigned int value_no=0;value_no<option_values.size();value_no++){
377 string value_name=option_values[value_no].longname;
378 if (argstring.compare(0,value_name.length(),value_name,0,value_name.length())==0){
379 string value=analyse_string(argstring);
380 *(option_values[value_no].status)=1;
381
382 if(option_values[value_no].allow_conffile==true){
383 *(option_values[value_no].status)=1;
384 *(option_values[value_no].target)=value;
385 }else{
386 result.insert(result.end(),"In File "+filename+": Option value \""
387 +option_values[value_no].longname
388 +"\" is not allowed in config files!");
389 }
390 found=true;
391 break;
392 }
393 }
394 if (! found) {
395 result.insert(result.end(),"In File "+filename+": Unknown option: "+argstring+"!");
396 }
397
398 } // if not #
399 } // if (strlen())
400 } // while(...)
401 fclose (fp);
402 } // fp==0 else branch
50c86ded 403
fed2c751 404 if (!result.empty()){
405 result.insert(result.begin(),"Error!");
406 result.insert(result.begin(),"");
407 }
50c86ded 408 return result;
409}
410
fed2c751 411
ad324d29 412/*!
413 *\brief Generate help.
414 *\arg target Reference to a vector<string> to which lots of helpful
415 * strings are appended.
416 */
ca5fce3a 417void configuration_manager::get_help(vector<string> & target){
ad324d29 418 target.insert(target.end(),"");
419 string line="Usage: "+app_name;
909d3603 420
421 for (vector<opt_switch_t>::iterator parm_p=option_switches.begin();parm_p<option_switches.end();parm_p++){
ad324d29 422 string addstr=" [-"+parm_p->shortname;
ad324d29 423 addstr+="]";
909d3603 424 if (line.length()+addstr.length()>MAX_LINE_LENGTH){
ad324d29 425 target.insert(target.end(),line);
426 line=string(7+app_name.length(),' ');
427 }
428 line+=addstr;
429 }
909d3603 430
431 for (vector<opt_value_t>::iterator parm_p=option_values.begin();parm_p<option_values.end();parm_p++){
432 string addstr=" [-"+parm_p->shortname;
433 addstr+=parm_p->placeholder;
434 addstr+="]";
435 if (line.length()+addstr.length()>MAX_LINE_LENGTH){
436 target.insert(target.end(),line);
437 line=string(7+app_name.length(),' ');
438 }
439 line+=addstr;
440 }
441
442 for (vector<cmd_arg_t>::iterator parm_p=cmd_args.begin();parm_p<cmd_args.end();parm_p++){
443 if (line.length()+parm_p->placeholder.length()>MAX_LINE_LENGTH){
ad324d29 444 target.insert(target.end(),line);
445 line=string(7+app_name.length(),' ');
446 }
447 line+=" ["+parm_p->placeholder+"]";
448 }
449 target.insert(target.end(),line);
450
909d3603 451 /* Here comes the documentation output. */
452
ad324d29 453 vector<string> left,right;
909d3603 454 unsigned int max_width;
ad324d29 455
909d3603 456 /* Output switches? */
457 if (option_switches.size()){
458 target.insert(target.end(),"");
459 target.insert(target.end(),"Switches:");
460
461 left=vector<string>();
462 right=vector<string>();
463 max_width=0;
464
465 /* Now lets insert switches into left and right */
466 for (unsigned int sw=0; sw<option_switches.size();sw++){
467 opt_switch_t akt_sw=option_switches[sw];
e7ba2001 468 string rline="(" ;
469 if (akt_sw.allow_cmdline) rline+="c/";
470 else rline+="-/";
471 if (akt_sw.allow_conffile) rline+="f)";
472 else rline+="-)";
473 rline+=" "+akt_sw.description;
909d3603 474 string lline="-"+akt_sw.shortname+", --"+akt_sw.longname;
475 left.insert(left.end(),lline);
476 right.insert(right.end(),rline);
477 }
478
479 // Determine maximal width for left column
480 max_width=0;
481 for (unsigned int c=0; c<left.size();c++)
482 if(left[c].length()>max_width) max_width=left[c].length();
483
484 /* output all the mess */
485 for (unsigned int c=0; c<left.size();c++){
486 string nl(max_width,' ');
487 nl.replace(0,left[c].length(),left[c]);
488 nl+=" "+right[c];
e7ba2001 489 while (nl.length()>MAX_LINE_LENGTH){ // Too long???
490 int limit=nl.find_last_of(' ',MAX_LINE_LENGTH);
909d3603 491 target.insert(target.end(),nl.substr(0,limit));
e7ba2001 492 nl=string(max_width+2+6,' ')+nl.substr(limit+1);
909d3603 493 }
494 target.insert(target.end(),nl);
495 }
ad324d29 496 }
497
909d3603 498 // Output values
499 if (option_values.size()){
ad324d29 500 target.insert(target.end(),"");
909d3603 501 target.insert(target.end(),"Option values:");
502
503 left=vector<string>();
504 right=vector<string>();
505 max_width=0;
506
507 for (unsigned int val=0; val<option_values.size();val++){
508 opt_value_t akt_val=option_values[val];
e7ba2001 509 string rline="(" ;
510 if (akt_val.allow_cmdline) rline+="c/";
511 else rline+="-/";
512 if (akt_val.allow_conffile) rline+="f)";
513 else rline+="-)";
514 rline+=" "+akt_val.description;
515
516 string lline="-"+akt_val.shortname+akt_val.placeholder+", --"+
fed2c751 517 akt_val.longname+"="+akt_val.placeholder;
909d3603 518 left.insert(left.end(),lline);
519 right.insert(right.end(),rline);
520 }
521
522 // Determine maximal width for left column
523 for (unsigned int c=0; c<left.size();c++)
524 if(left[c].length()>max_width) max_width=left[c].length();
525 /* output all the mess */
526 for (unsigned int c=0; c<left.size();c++){
e7ba2001 527 string nl(max_width,' '); // Empty left side.
528 nl.replace(0,left[c].length(),left[c]); // Print in left side.
529 nl+=" "+right[c]; // Add right side.
530
531 while (nl.length()>MAX_LINE_LENGTH){ // Too long???
532 int limit=nl.find_last_of(' ',MAX_LINE_LENGTH);
909d3603 533 target.insert(target.end(),nl.substr(0,limit));
e7ba2001 534 nl=string(max_width+2+6,' ')+nl.substr(limit+1);
909d3603 535 }
536 target.insert(target.end(),nl);
537 }
ad324d29 538 }
539
e7ba2001 540 target.insert(target.end(),"");
541 target.insert(target.end(),"Legend (c/f):");
542 target.insert(target.end()," c: Allowed on command line.");
543 target.insert(target.end()," f: Allowed in configuration file.");
544
909d3603 545 /* Output the Arguments */
546 if (cmd_args.size()){
ad324d29 547 target.insert(target.end(),"");
548 target.insert(target.end(),"Arguments:");
ad324d29 549
909d3603 550 left=vector<string>();
551 right=vector<string>();
552 max_width=0;
553
554 for (unsigned int arg=0; arg<cmd_args.size();arg++){
555 cmd_arg_t akt_arg=cmd_args[arg];
556 left.insert(left.end(),akt_arg.placeholder);
557 right.insert(right.end(),akt_arg.description);
558 }
559
560 // Determine maximal width for left column
561 max_width=0;
562 for (unsigned int c=0; c<left.size();c++)
563 if(left[c].length()>max_width) max_width=left[c].length();
564
565 for (unsigned int c=0; c<left.size();c++){
566 string nl(max_width,' ');
567 nl.replace(0,left[c].length(),left[c]);
568 nl+=" "+right[c];
569 while (nl.length()>MAX_LINE_LENGTH){ // Too long???
570 int limit=nl.find_last_of(' ',MAX_LINE_LENGTH+1);
909d3603 571 target.insert(target.end(),nl.substr(0,limit));
572 nl=string(max_width+2,' ')+nl.substr(limit+1);
573 }
574 target.insert(target.end(),nl);
575 }
ad324d29 576 target.insert(target.end(),"");
909d3603 577 }
ad324d29 578}
579
580/*!
581 *\brief Generate help.
582 *\return A vector containing many helpful strings for the user.
583 */
ca5fce3a 584vector<string> configuration_manager::get_help(){
ad324d29 585 vector<string> result;
586 get_help(result);
587 return result;
588}
589
590
591/**************************************************/
592
909d3603 593
594configuration_manager::opt_value_t::opt_value_t(const string & shortname,
595 const string & longname,
596 const string & description,
597 int * status,
598 string * target,
599 const string & placeholder,
600 const bool & allow_conffile,
601 const bool & allow_cmdline
602 ){
603 this->shortname=shortname;
604 this->longname=longname;
605 this->description=description;
606 this->status=status;
607 this->target=target;
608 this->placeholder=placeholder;
609 this->allow_conffile=allow_conffile;
610 this->allow_cmdline=allow_cmdline;
ad324d29 611 if (status) *status=0;
612}
613
909d3603 614
615configuration_manager::opt_switch_t::opt_switch_t(const string & shortname,
616 const string & longname,
617 const string & description,
618 int * status,
619 const bool & allow_conffile,
620 const bool & allow_cmdline){
621 this->shortname=shortname;
622 this->longname=longname;
623 this->description=description;
624 this->status=status;
625 this->target=target;
626 this->placeholder=placeholder;
627 this->allow_conffile=allow_conffile;
628 this->allow_cmdline=allow_cmdline;
ad324d29 629}
909d3603 630
631
632configuration_manager::cmd_arg_t::cmd_arg_t(const string & placeholder,
633 const string & description,
634 int * status,
635 string * target){
636 this->placeholder=placeholder;
637 this->description=description;
638 this->status=status;
639 this->target=target;
909d3603 640}