*** 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 *(option_switches[switch_no].status)=result;
148 found=true;
149 }
150 }
151
152 // Look for long values.
153 for (unsigned int value_no=0;value_no<option_values.size();value_no++){
154 string value_name=option_values[value_no].longname;
155 if (argstring.compare(0,value_name.length(),value_name,0,value_name.length())==0){
156 string value=analyse_string(argstring);
157 *(option_values[value_no].status)=1;
158 *(option_values[value_no].target)=value;
159 found=true;
160 }
161 }
162 if (! found) {
163 messages.insert(messages.begin(),"Unknown option: --"+argstring+"!");
164 found=true; // Oh!
165 }
166 }
167
168 // Look for short parameters
169 if ((argv[arg_no].substr(0,1)=="-")&&(free_parms_count==0)&&(!found)){ // Short parameters
170 found=true;
171 argstring=argv[arg_no].substr(1); // Reassign, with one more character now
172 for (unsigned int pos=0;pos < argstring.length();pos++){
173 bool short_found=false;
174
175 // First, find short switches
176 for (unsigned int switch_no=0;switch_no<option_switches.size();switch_no++){
177 string short_name=option_switches[switch_no].shortname;
178 if (short_name.compare(0,short_name.length(),argstring,pos,short_name.length())==0){
179 *(option_switches[switch_no].status)=1;
180 short_found=true;
181 pos+=short_name.length()-1;
182 }
183 }
184
185 // Now, find short values
186 if (!short_found){
187 for (unsigned int value_no=0;value_no<option_values.size();value_no++){
188 string short_name=option_values[value_no].shortname;
189
190 if (short_name.compare(0,short_name.length(),argstring,pos,short_name.length())==0){
191 if (pos==argstring.length()-1){ // Last character, where's the value?
192 if (arg_no<argv.size()-1){ // Take next complete argument as value!
193 *(option_values[value_no].status)=1;
194 *(option_values[value_no].target)=argv[arg_no+1];
195 arg_no+=1; // Consume the next argument
196 short_found=true;
197 } else { // No argument left!
198 messages.insert(messages.begin(),"Missing value for -"+short_name+"!");
199 short_found=true;
200 }
201 } else {
202 *(option_values[value_no].status)=1;
203 *(option_values[value_no].target)=argstring.substr(pos+1);
204 pos=argstring.length(); // Do not analyse other characters.
205 short_found=true;
206 }
207 }
208 }
209 }
210 if (!short_found) messages.insert(messages.begin(),"Unknown Option: -"+argstring.substr(pos,1)+"!");
211 }
212 }
213
214 if (!found) { // Must be a free form parameter...
215 if (free_parms_count<cmd_args.size()){ // Space available
216 *(cmd_args[free_parms_count].target)=argv[arg_no];
217 *(cmd_args[free_parms_count].status)=1;
218 free_parms_count++;
219 } else { // No more space for free form parameters!
220 messages.insert(messages.begin(),"Too many arguments!");
221 }
222 }
223 }
224 if (!messages.empty()){
225 messages.insert(messages.begin(),"Error!");
226 messages.insert(messages.begin(),"");
227 }
228 return messages;
229 }
230
231
232 /*!
233 *\brief Extract a value from a configuration file line or
234 * a command line argument
235 */
236 string configuration_manager::analyse_string(const string & line){
237 string result="";
238 unsigned int pos=line.find("=");
239 if (pos!=string::npos) result=line.substr(pos+1);
240 return result;
241 }
242
243 /*!
244 *\brief Extract a boolean value out of a string.
245 */
246 bool configuration_manager::analyse_bool(const string & data){
247 if ((data=="false")||(data=="0")||(data=="no")) return false;
248 return true;
249 }
250
251 /*!
252 *brief Read in and parse a configuration file.
253 *\arg file String containing the filename
254 *\return Empty Vector or Vector full of strings containing
255 * the error message(s).
256 */
257 vector<string> configuration_manager::read_file(string filename){
258 vector<string>result;
259 FILE * fp=fopen(filename.c_str(),"r");
260 if (! fp) {
261 result.insert(result.end(),"Error!");
262 result.insert(result.end(),"Could not open file:"+filename);
263 return result;
264 }
265
266 char buffer[1000];
267 while(fgets(buffer,1000,fp)){
268 if (strlen (buffer)){
269 char*l2=buffer+strspn(buffer," \t");
270 if (l2[0]!='#'){
271 string line=l2;
272 unsigned int pos=line.find("=");
273 if (pos==0){
274 result.insert(result.end(),"Error!");
275 result.insert(result.end(),"In File:"+filename);
276 result.insert(result.end(),"Line beginning with '='!");
277 return result;
278 }
279
280 } // if not #
281
282 } // if (strlen())
283 } // while(...)
284
285
286 fclose (fp);
287 return result;
288 }
289
290 /*!
291 *\brief Generate help.
292 *\arg target Reference to a vector<string> to which lots of helpful
293 * strings are appended.
294 */
295 void configuration_manager::get_help(vector<string> & target){
296 target.insert(target.end(),"");
297 string line="Usage: "+app_name;
298
299 for (vector<opt_switch_t>::iterator parm_p=option_switches.begin();parm_p<option_switches.end();parm_p++){
300 string addstr=" [-"+parm_p->shortname;
301 addstr+="]";
302 if (line.length()+addstr.length()>MAX_LINE_LENGTH){
303 target.insert(target.end(),line);
304 line=string(7+app_name.length(),' ');
305 }
306 line+=addstr;
307 }
308
309 for (vector<opt_value_t>::iterator parm_p=option_values.begin();parm_p<option_values.end();parm_p++){
310 string addstr=" [-"+parm_p->shortname;
311 addstr+=parm_p->placeholder;
312 addstr+="]";
313 if (line.length()+addstr.length()>MAX_LINE_LENGTH){
314 target.insert(target.end(),line);
315 line=string(7+app_name.length(),' ');
316 }
317 line+=addstr;
318 }
319
320 for (vector<cmd_arg_t>::iterator parm_p=cmd_args.begin();parm_p<cmd_args.end();parm_p++){
321 if (line.length()+parm_p->placeholder.length()>MAX_LINE_LENGTH){
322 target.insert(target.end(),line);
323 line=string(7+app_name.length(),' ');
324 }
325 line+=" ["+parm_p->placeholder+"]";
326 }
327 target.insert(target.end(),line);
328
329 /* Here comes the documentation output. */
330
331 vector<string> left,right;
332 unsigned int max_width;
333
334 /* Output switches? */
335 if (option_switches.size()){
336 target.insert(target.end(),"");
337 target.insert(target.end(),"Switches:");
338
339 left=vector<string>();
340 right=vector<string>();
341 max_width=0;
342
343 /* Now lets insert switches into left and right */
344 for (unsigned int sw=0; sw<option_switches.size();sw++){
345 opt_switch_t akt_sw=option_switches[sw];
346 string rline=akt_sw.description;
347 string lline="-"+akt_sw.shortname+", --"+akt_sw.longname;
348 left.insert(left.end(),lline);
349 right.insert(right.end(),rline);
350 }
351
352 // Determine maximal width for left column
353 max_width=0;
354 for (unsigned int c=0; c<left.size();c++)
355 if(left[c].length()>max_width) max_width=left[c].length();
356
357 /* output all the mess */
358 for (unsigned int c=0; c<left.size();c++){
359 string nl(max_width,' ');
360 nl.replace(0,left[c].length(),left[c]);
361 nl+=" "+right[c];
362 while (nl.length()>80){ // Too long???
363 int limit=nl.find_last_of(' ',MAX_LINE_LENGTH+1);
364 target.insert(target.end(),nl.substr(0,limit));
365 nl=string(max_width+2,' ')+nl.substr(limit+1);
366 }
367 target.insert(target.end(),nl);
368 }
369 }
370
371 // Output values
372 if (option_values.size()){
373 target.insert(target.end(),"");
374 target.insert(target.end(),"Option values:");
375
376 left=vector<string>();
377 right=vector<string>();
378 max_width=0;
379
380 for (unsigned int val=0; val<option_values.size();val++){
381 opt_value_t akt_val=option_values[val];
382 string rline=akt_val.description;
383 string lline=" -"+akt_val.shortname+akt_val.placeholder+", --"+
384 akt_val.longname+akt_val.placeholder;
385 left.insert(left.end(),lline);
386 right.insert(right.end(),rline);
387 }
388
389 // Determine maximal width for left column
390 for (unsigned int c=0; c<left.size();c++)
391 if(left[c].length()>max_width) max_width=left[c].length();
392 /* output all the mess */
393 for (unsigned int c=0; c<left.size();c++){
394 string nl(max_width,' ');
395 nl.replace(0,left[c].length(),left[c]);
396 nl+=" "+right[c];
397 while (nl.length()>80){ // Too long???
398 int limit=nl.find_last_of(' ',MAX_LINE_LENGTH+1);
399 target.insert(target.end(),nl.substr(0,limit));
400 nl=string(max_width+2,' ')+nl.substr(limit+1);
401 }
402 target.insert(target.end(),nl);
403 }
404 }
405
406 /* Output the Arguments */
407 if (cmd_args.size()){
408 target.insert(target.end(),"");
409 target.insert(target.end(),"Arguments:");
410
411 left=vector<string>();
412 right=vector<string>();
413 max_width=0;
414
415 for (unsigned int arg=0; arg<cmd_args.size();arg++){
416 cmd_arg_t akt_arg=cmd_args[arg];
417 left.insert(left.end(),akt_arg.placeholder);
418 right.insert(right.end(),akt_arg.description);
419 }
420
421 // Determine maximal width for left column
422 max_width=0;
423 for (unsigned int c=0; c<left.size();c++)
424 if(left[c].length()>max_width) max_width=left[c].length();
425
426 for (unsigned int c=0; c<left.size();c++){
427 string nl(max_width,' ');
428 nl.replace(0,left[c].length(),left[c]);
429 nl+=" "+right[c];
430 while (nl.length()>MAX_LINE_LENGTH){ // Too long???
431 int limit=nl.find_last_of(' ',MAX_LINE_LENGTH+1);
432 // printf("limit:%i\n",limit);
433 target.insert(target.end(),nl.substr(0,limit));
434 nl=string(max_width+2,' ')+nl.substr(limit+1);
435 }
436 target.insert(target.end(),nl);
437 }
438 target.insert(target.end(),"");
439 }
440 }
441
442 /*!
443 *\brief Generate help.
444 *\return A vector containing many helpful strings for the user.
445 */
446 vector<string> configuration_manager::get_help(){
447 vector<string> result;
448 get_help(result);
449 return result;
450 }
451
452
453 /**************************************************/
454
455
456 configuration_manager::opt_value_t::opt_value_t(const string & shortname,
457 const string & longname,
458 const string & description,
459 int * status,
460 string * target,
461 const string & placeholder,
462 const bool & allow_conffile,
463 const bool & allow_cmdline
464 ){
465 this->shortname=shortname;
466 this->longname=longname;
467 this->description=description;
468 this->status=status;
469 this->target=target;
470 this->placeholder=placeholder;
471 this->allow_conffile=allow_conffile;
472 this->allow_cmdline=allow_cmdline;
473 if (status) *status=0;
474 }
475
476
477 configuration_manager::opt_switch_t::opt_switch_t(const string & shortname,
478 const string & longname,
479 const string & description,
480 int * status,
481 const bool & allow_conffile,
482 const bool & allow_cmdline){
483 this->shortname=shortname;
484 this->longname=longname;
485 this->description=description;
486 this->status=status;
487 this->target=target;
488 this->placeholder=placeholder;
489 this->allow_conffile=allow_conffile;
490 this->allow_cmdline=allow_cmdline;
491 if (status) *status=0;
492 }
493
494
495 configuration_manager::cmd_arg_t::cmd_arg_t(const string & placeholder,
496 const string & description,
497 int * status,
498 string * target){
499 this->placeholder=placeholder;
500 this->description=description;
501 this->status=status;
502 this->target=target;
503 if (this->status) *(this->status)=0;
504 }