f21de674ca791c2e7f03b97f85a82d05b2a85e78
[h316.git] / pc-tools / ldc2 / src / configuration_manager.cpp
1 /******************************************************************************
2 *
3 * LDC2 source code
4 *
5 * $Date: 2008/08/25 21:02:24 $
6 * $Author: hachti $
7 *
8 * $Log: configuration_manager.cpp,v $
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
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
17 * *** empty log message ***
18 *
19 * Revision 2.0 2007-03-26 01:00:38 hachti
20 * *** empty log message ***
21 *
22 *
23 ******************************************************************************/
24
25 #include "configuration_manager.hh"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <fcntl.h>
29 #include <string.h>
30
31 #define MAX_LINE_LENGTH 80
32
33 /*!
34 *\brief Constructor.
35 *
36 * This constructor makes a new configuration_manager ready to use.
37 *\arg name Name of the application as mentioned in the
38 * "Use: <appname> ..." help message.
39 */
40 configuration_manager::configuration_manager(string name){
41 app_name=name;
42 }
43
44
45 /*!
46 *\brief Add a new configuration value to be searched for.
47 *\param shortname A character for the short form.
48 * For example the o in -o
49 *\param longname The double dash longname. For example
50 * output_file in --output_file=
51 *\param description A detailed description of the value.
52 *\param status Pointer to an integer. Will be set to 1 if arg found.
53 *\param target Pointer to string to put the value in.
54 *\param placeholder A placeholder for the documentation.
55 * For example <filename> in -f<filename>
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 *
61 */
62 void 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 ){
71
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 );
84 }
85
86
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 */
101 void 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
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.
124 *\param status A pointer to a status variable. Will be set to 0 by default.
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 */
132 void configuration_manager::add_argument(const string & description,
133 int * status,
134 string * target,
135 const string & placeholder){
136
137
138
139 if (target!=NULL) if(status!=NULL)
140 cmd_args.insert(cmd_args.end(),cmd_arg_t(placeholder,
141 description,
142 status,
143 target));
144 }
145
146
147 /*!
148 *\brief Read in the args passed to main().
149 *\returns empty vector on success or the error messages to be output.
150 */
151 vector<string> configuration_manager::read_args(int argc, char ** args){
152
153 vector<string> messages;
154 vector<string> argv;
155
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.
160 unsigned int free_parms_count=0;
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;
166
167 // Look for long parameters.
168 // if ((argv[arg_no].substr(0,2)=="--")&&(free_parms_count==0)){
169 if ((argv[arg_no].substr(0,2)=="--")){ // Changed for comfort.
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);
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 }
185 found=true;
186 break;
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);
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 }
203 found=true;
204 break;
205 }
206 }
207 if (! found) {
208 messages.insert(messages.begin(),"Unknown option: --"+argstring+"!");
209 found=true; // Oh!
210 }
211 }
212
213 // Look for short parameters
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.
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){
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 }
232 short_found=true;
233 pos+=short_name.length()-1;
234 break;
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 {
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 }
263 pos=argstring.length(); // Do not analyse other characters.
264 short_found=true;
265 break;
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;
278 free_parms_count++;
279 } else { // No more space for free form parameters!
280 messages.insert(messages.begin(),"Too many arguments!");
281 }
282 }
283 }
284 if (!messages.empty()){
285 messages.insert(messages.begin(),"Error!");
286 messages.insert(messages.begin(),"");
287 }
288 return messages;
289 }
290
291
292 /*!
293 *\brief Extract a value from a configuration file line or
294 * a command line argument
295 */
296 string 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);
300 return result;
301 }
302
303 /*!
304 *\brief Extract a boolean value out of a string, defaulting to true
305 *
306 * Used for commandline switches
307 */
308 bool configuration_manager::analyse_bool(const string & data){
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 */
321 bool 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;
326 }
327
328
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 */
335 vector<string> configuration_manager::read_file(string filename){
336 vector<string>result;
337 FILE * fp=fopen(filename.c_str(),"r");
338 if (! fp) {
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
403
404 if (!result.empty()){
405 result.insert(result.begin(),"Error!");
406 result.insert(result.begin(),"");
407 }
408 return result;
409 }
410
411
412 /*!
413 *\brief Generate help.
414 *\arg target Reference to a vector<string> to which lots of helpful
415 * strings are appended.
416 */
417 void configuration_manager::get_help(vector<string> & target){
418 target.insert(target.end(),"");
419 string line="Usage: "+app_name;
420
421 for (vector<opt_switch_t>::iterator parm_p=option_switches.begin();parm_p<option_switches.end();parm_p++){
422 string addstr=" [-"+parm_p->shortname;
423 addstr+="]";
424 if (line.length()+addstr.length()>MAX_LINE_LENGTH){
425 target.insert(target.end(),line);
426 line=string(7+app_name.length(),' ');
427 }
428 line+=addstr;
429 }
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){
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
451 /* Here comes the documentation output. */
452
453 vector<string> left,right;
454 unsigned int max_width;
455
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];
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;
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];
489 while (nl.length()>MAX_LINE_LENGTH){ // Too long???
490 int limit=nl.find_last_of(' ',MAX_LINE_LENGTH);
491 target.insert(target.end(),nl.substr(0,limit));
492 nl=string(max_width+2+6,' ')+nl.substr(limit+1);
493 }
494 target.insert(target.end(),nl);
495 }
496 }
497
498 // Output values
499 if (option_values.size()){
500 target.insert(target.end(),"");
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];
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+", --"+
517 akt_val.longname+"="+akt_val.placeholder;
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++){
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);
533 target.insert(target.end(),nl.substr(0,limit));
534 nl=string(max_width+2+6,' ')+nl.substr(limit+1);
535 }
536 target.insert(target.end(),nl);
537 }
538 }
539
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
545 /* Output the Arguments */
546 if (cmd_args.size()){
547 target.insert(target.end(),"");
548 target.insert(target.end(),"Arguments:");
549
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);
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 }
576 target.insert(target.end(),"");
577 }
578 }
579
580 /*!
581 *\brief Generate help.
582 *\return A vector containing many helpful strings for the user.
583 */
584 vector<string> configuration_manager::get_help(){
585 vector<string> result;
586 get_help(result);
587 return result;
588 }
589
590
591 /**************************************************/
592
593
594 configuration_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;
611 if (status) *status=0;
612 }
613
614
615 configuration_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;
629 }
630
631
632 configuration_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;
640 }