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