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