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