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