source: trunk/FACT++/src/Configuration.cc@ 14177

Last change on this file since 14177 was 14046, checked in by tbretz, 13 years ago
Implemented conversion of data-base to uri
File size: 47.9 KB
Line 
1// **************************************************************************
2/** @class Configuration
3
4@brief Commandline parsing, resource file parsing and database access
5
6
7@section User For the user
8
9The Configuration class will process the following steps:
10
11Check the command-line for <B> --default=default.rc </B> (If no configuration
12filename is given on the command-line use \e program_name.rc instead. (Note
13that the name is retrieved from \b argv[0] and might change if you start
14the program through a symbolic link with a different name)
15
16Read the "<B>database=user:password@database:port/database</B>" entry from the file.
17(For details about the syntax see Configuration::parse_database)
18The retrieved entry can be overwritten by
19"<B>--database=user:passwd@server:port/database</B>" from the command line. If
20neither option is given no configuration data will be read from the
21database. To suppress any database access use \b --no-database.
22
23Check the command-line for <B> -C priority.rc </B>
24
25The configuration data is now evaluated from the following sources in
26the following order. Note that options from earlier source have
27priority.
28
29 - (1) Commandline options
30 - (2) Options from the high prioroty configuration-file (given by \b -C or \b --config)
31 - (3) Database entries
32 - (4) Options from the default configuration-file (given by \b --default, defaults to \b program_name.rc)
33 - (5) Options from the global configuration-file (constrctor path + \b fact++.rc)
34 - (6) Environment variables
35
36Which options are accepted is defined by the program. To get a list
37of all command-line option use \b --help. This also lists all other
38available options to list for exmaple the options available in the
39configuration files or from the databse. In addition some default options
40are available which allow to debug parsing of the options, by either printing
41the options retrieval or after parsing.
42
43Options in the configuration files must be given in the form
44
45 - key = value
46
47which is equivalent to the command-line option <B>--key=value</B>.
48
49If there are sections in the configuration file like
50
51\code
52
53 [section1]
54 key = value
55
56\endcode
57
58the key is transformed into <B>section1.key</B> (which would be equivalent
59to <B>--section1.key</B>)
60
61@attention
62In principle it is possible that an exception is thrown before options
63like \b --help are properly parsed and evaluated. In this case it is
64necessary to first resolve the problem. Usually, this mean that there
65is a design flaw in the program rather than a mistake of usage.
66
67For more details on the order in which configuration is read,
68check Configuration::Parse. For more details on the parsing itself
69see the documentation of boost::program_options.
70
71
72
73
74@section API For the programmer
75
76The Configuration class heavily uses the
77<A HREF="http://www.boost.org"><B>C++ boost library</B></A>
78and makes heavy use of the
79<A HREF="http://www.boost.org/doc/libs/release/doc/html/program_options.html">
80<B>boost::program_options</B></A>
81
82The databse access is based on the
83<A HREF="http://tangentsoft.net/mysql++/"><B>MySQL++ library</B></A>.
84
85The basic idea is to have an easy to use, but powerfull setup. The setup on
86all options is based on a special syntax of options_description. Here is an
87example:
88
89\code
90
91 int opt = 0;
92
93 po::options_description config("Section");
94 config.add_options()
95 ("option1", var<string>(), "This is option1")
96 ("option2", var<int>(22), "This is option2")
97 ("option3,o", var<double>->required(), "This option is mandatory")
98 ("option4", var<int>(&opt), "This is option 4")
99 ("option5", vars<string>(), "A list of strings")
100 ("option6", vars<string>(), "A list of strings")
101 ("option7", vars<string>, "A list of strings")
102 ("option8", var<string>()->implicit_value("val"), "Just a string")
103 ("option9", var<string>()->default_value("def"), "Just a string")
104 ("optionA", var<string>("def"), "Just a string")
105 ("bool", po_bool(), "A special switch")
106 ;
107
108\endcode
109
110This will setup, e.g., the commandline option '<B>--option1 arg</B>' (which
111is identical to '<B>--option1=arg</B>'. Option 3 can also be expressed
112in a short form as '<B>-o arg</B>' or '<B>-o=arg</B>'. Option 2 defaults
113to 22 if no explicit value is given. Option 3 is mandatory and an exceptionb
114is thrown if not specified. Option 4 will, apart from the usual access to the
115option values, also store its value in the variable opt.
116
117The used functions po_*() are defined in configuration.h and are abbreviations.
118Generally speaking also other variable types are possible.
119
120If the options are displayed, e.g. by \b --help the corresponding section will
121by titled \e Section, also untitled sections are possible.
122
123If an option can be given more than once then a std::vector<type> can be used.
124Abbreviations po_ints(), po_doubles() and po_strings() are available.
125
126There are several ways to define the behaviour of the options. In the
127example above Parse will throw an exception if the "--option3" or "-o"
128option is not given. "option9" will evaluate to "def" if it is not
129given on the command line. The syntax of "optionA" is just an
130abbreviation. "option8" will evaluate to "val" if just "--option5" but
131no argument is given. Note, that these modifiers can be concatenated.
132
133A special type po_bool() is provided which is an abbreviation of
134var<bool>()->implicit_value(true)->default_value(false). In
135contradiction to po_switch() this allows to set a true and
136false value in the setup file.
137
138In addition to options introduced by a minus or double minus, so called
139positional options can be given on the command line. To describe these
140options use
141
142\code
143
144 po::positional_options_description p;
145 p.add("option5", 2); // The first 2 positional options
146 p.add("option6", 3); // The next three positional options
147 // p.add("option7", -1); // All others, if wanted
148
149\endcode
150
151This assigns option-keys to the positional options (arguments) in the
152command-line. Note that the validity of the given commandline is checked.
153Hence, this way of defining the options makes sense.
154
155As needed options_descriptions can be grouped together
156
157\code
158
159 po::options_description config1("Section1");
160 po::options_description config2("Section2");
161
162 po::options_description configall;
163 configall.add(config1);
164 configall.add(config2);
165
166\endcode
167
168The member functions of Configurations allow to define for which option
169source these options are valid. The member functions are:
170
171\code
172
173 Configuration conf;
174
175 conf.AddOptionsCommandline(configall, true);
176 conf.AddOptionsConfigfile(config1, true);
177 conf.AddOptionsDatabase(config2, true);
178
179 // To enable the mapping of the position arguments call this
180 conf.SetArgumentPositions(p);
181
182\endcode
183
184If the second option is false, the options will not be displayed in any
185\b --help directive, but are available to the user. Each of the functions
186can be called more than once. If an option should be available from
187all kind of inputs AddOptions() can be used which will call all
188four other AddOptions() functions.
189
190A special case are the options from environment variables. Since you might
191want to use the same option-key for the command-line and the environment,
192a mapping is needed (e.g. from \b PATH to \b --path). This mapping
193can be implemented by a mapping function or by the build in mapping
194and be initialized like this:
195
196\code
197
198 conf.AddEnv("path", "PATH");
199
200\endcode
201
202or
203
204\code
205
206 const string name_mapper(const string str)
207 {
208 return str=="PATH" ? "path" : "";
209 }
210
211 conf.SetNameMapper(name_mapper);
212
213\endcode
214
215Assuming all the above is done in a function calles SetupConfiguration(),
216a simple program to demonstrate the power of the class could look like this:
217
218\code
219
220 int main(int argc, char **argv)
221 {
222 int opt;
223
224 Configuration conf(argv[0]);
225 SetupConfiguration(conf, opt);
226
227 po::variables_map vm;
228 try
229 {
230 vm = conf.Parse(argc, argv);
231 }
232 catch (std::exception &e)
233 {
234 po::multiple_occurrences *MO = dynamic_cast<po::multiple_occurrences*>(&e);
235 if (MO)
236 cout << "Error: " << e.what() << " of '" << MO->get_option_name() << "' option." << endl;
237 else
238 cout << "Error: " << e.what() << endl;
239 cout << endl;
240
241 return -1;
242 }
243
244 cout << "Opt1: " << conf.GetString("option1") << endl;
245 cout << "Opt2: " << conf.GetInt("option2") << endl;
246 cout << "Opt3: " << conf.GetDouble("option3") << endl;
247 cout << "Opt4: " << opt << endl;
248
249 return 0;
250 }
251
252\endcode
253
254Another possibility to access the result is the direct approach, for example:
255
256\code
257
258 vector<int> i = vm["option2"].as<int>();
259 vector<string> vec = vm["option6"].as<vector<string>>();
260
261\endcode
262
263Note that accessing an option which was not given will throw an exception.
264Therefor its availability should first be checked in one of the following
265ways:
266
267\code
268
269 bool has_option1 = vm.count("option1");
270 bool has_option2 = conf.Has("option2");
271
272\endcode
273
274@section Extensions
275
276The configuration interpreter can be easily extended to new types, for example:
277
278\code
279
280template<class T,class S> // Just for the output
281 std::ostream &operator<<(std::ostream &out, const pair<T,S> &f)
282 {
283 out << f.first << "|" << f.second;
284 return out;
285 }
286
287template<class T, class S> // Needed to convert the option
288 std::istream &operator>>(std::istream &in, pair<T,S> &f)
289 {
290 char c;
291 in >> f.first;
292 in >> c;
293 if (c!=':')
294 return in;
295 in >> f.second;
296 return in;
297 }
298
299typedef pair<int,int> mytype; // Type definition
300
301void main(int argc, char **argv)
302{
303 po::options_description config("Configuration");
304 config.add_options()
305 ("mytype", var<mytype>(), "my new type")
306 ;
307
308 Configuration conf;
309 conf.AddOptionsCommandline(config);
310 conf.Parse(argc, argv);
311
312 cout << conf.Get<mytype>("mytype") << endl;
313}
314
315\endcode
316
317@section Examples
318
319 - An example can be found in \ref argv.cc
320
321@todo
322
323 - Maybe we should remove the necessity to propagate argv[0] in the constructor?
324 - Add an option to the constructor to switch of database/file access
325
326*/
327// **************************************************************************
328#include "Configuration.h"
329
330#include <fstream>
331#include <iostream>
332#include <iomanip>
333
334#include <boost/filesystem.hpp>
335
336#ifdef HAVE_SQL
337#include "Database.h"
338#endif
339
340using namespace std;
341
342namespace style = boost::program_options::command_line_style;
343
344// --------------------------------------------------------------------------
345//
346//! The purpose of this function is basically to connect to the database,
347//! and retrieve all the options entries from the 'Configuration' table.
348//!
349//! @param database
350//! The URL of the database from which the configuration data is
351//! retrieved. It should be given in the form
352//! \li [user[:password]@]server.com[:port]/database
353//!
354//! with
355//! - user: user name (default is the current user)
356//! - password: necessary if required by the database rights
357//! - server: the URL of the server (can be 'localhost')
358//! - port: the port to which to connect (usually obsolete)
359//! - database: The name of the database containing the table
360//!
361//! @param desc
362//! A reference to the object with the description of the options
363//! which should be retrieved.
364//!
365//! @param allow_unregistered
366//! If this is true also unregistered, i.e. options unknown to desc,
367//! are returned. Otherwise an exception is thrown if such an option
368//! was retrieved.
369//!
370//! @return
371//! Return an object of type basic_parsed_options containing all
372//! the entries retrieved from the database. Options not found in
373//! desc are flagged as unregistered.
374//!
375//! @throws
376//! Two types of exceptions are thrown
377//! - It thows an unnamed exception if the options could not be
378//! retrieved properly from the databse.
379//! - If an option is not registered within the given descriptions
380//! and \b allow_unregistered is \b false, an exception of type
381//! \b po::unknown_option is thrown.
382//!
383//! @todo
384//! - The exceptions handling should be improved.
385//! - The final database layout is missing in the description
386//! - Shell we allow options to be given more than once?
387//
388#ifdef HAVE_SQL
389po::basic_parsed_options<char>
390 Configuration::parse_database(const string &prgname, const string &database, const po::options_description& desc, bool allow_unregistered)
391{
392 Database db(database);
393
394 cout << "Connected to '" << db.uri() << "' for " << prgname << endl;
395
396 const mysqlpp::StoreQueryResult res =
397 db.query("SELECT CONCAT(fKey1,fKey2), fValue "
398 "FROM ProgramOption "
399 "WHERE fCounter=(SELECT MAX(fCounter) FROM History) "
400 "AND NOT ISNULL(fValue) "
401 "AND (fProgram='"+prgname+"' OR fProgram='*')").store();
402
403 set<string> allowed_options;
404
405 const vector<boost::shared_ptr<po::option_description>> &options = desc.options();
406 for (unsigned i=0; i<options.size(); ++i)
407 {
408 const po::option_description &d = *options[i];
409 if (d.long_name().empty())
410 boost::throw_exception(po::error("long name required for database"));
411
412 allowed_options.insert(d.long_name());
413 }
414
415 po::parsed_options result(&desc);
416
417 for (vector<mysqlpp::Row>::const_iterator v=res.begin(); v<res.end(); v++)
418 {
419 const string key = (*v)[0].c_str();
420 if (key.empty()) // key == > Throw exception
421 continue;
422
423 // Check if we are allowed to accept unregistered options,
424 // i.e. options which are not in options_description &desc.
425 const bool unregistered = allowed_options.find(key)==allowed_options.end();
426 if (unregistered && allow_unregistered)
427 boost::throw_exception(po::unknown_option(key));
428
429 // Create a key/value-pair and store whether it is a
430 // registered option of not
431 po::option n;
432 n.string_key = key;
433 // This is now identical to file parsing. What if we want
434 // to concatenate options like on the command line?
435 n.value.clear(); // Fixme: composing?
436 n.value.push_back((*v)[1].c_str());
437 //n.unregistered = unregistered;
438
439 // If any parsing will be done in the future...
440 //n.value().original_tokens.clear();
441 //n.value().original_tokens.push_back(name);
442 //n.value().original_tokens.push_back(value);
443
444 result.options.push_back(n);
445 }
446
447 return result;
448}
449#else
450po::basic_parsed_options<char>
451 Configuration::parse_database(const string &, const string &, const po::options_description &desc, bool)
452{
453 return po::parsed_options(&desc);
454}
455#endif
456
457// --------------------------------------------------------------------------
458//
459//!
460//
461Configuration::Configuration(const string &prgname) : fName(UnLibToolize(prgname)),
462fNameMapper(bind1st(mem_fun(&Configuration::DefaultMapper), this)),
463fPrintUsage(bind(&Configuration::PrintUsage, this))
464{
465 po::options_description generic("Generic options");
466 generic.add_options()
467 ("version,V", "Print version information.")
468 ("help", "Print available commandline options.")
469 ("help-environment", "Print available environment variables.")
470 ("help-database", "Print available options retreived from the database.")
471 ("help-config", "Print available configuration file options.")
472 ("print-all", "Print all options as parsed from all the different sources.")
473 ("print", "Print options as parsed from the commandline.")
474 ("print-default", "Print options as parsed from default configuration file.")
475 ("print-database", "Print options as retrieved from the database.")
476 ("print-config", "Print options as parsed from the high priority configuration file.")
477 ("print-environment", "Print options as parsed from the environment.")
478 ("print-unknown", "Print unrecognized options.")
479 ("print-options", "Print options as passed to program.")
480 ("print-wildcards", "Print all options registered with wildcards.")
481 ("dont-check", "Do not check validity of options from files and database.")
482 ("dont-check-files", "Do not check validity of options from files.")
483 ("dont-check-database", "Do not check validity of options from database.")
484 ;
485
486 po::options_description def_config;
487 def_config.add_options()
488 ("default", var<string>(fName+string(".rc")), "Default configuration file.")
489 ;
490
491 po::options_description config("Configuration options");
492 config.add_options()
493 ("config,C", var<string>(), "Configuration file overwriting options retrieved from the database.")
494 ("database", var<string>(), "Database link as in\n\t[user[:password]@]server.com[:port]/database\nOverwrites options from the default configuration file.")
495 ("no-database", "Suppress any access to the database even if a database URL was set.")
496 ;
497
498 fOptionsCommandline[kVisible].add(generic);
499 fOptionsCommandline[kVisible].add(config);
500 fOptionsCommandline[kVisible].add(def_config);
501 fOptionsConfigfile[kVisible].add(config);
502}
503
504// --------------------------------------------------------------------------
505//
506//!
507//
508void Configuration::PrintParsed(const po::parsed_options &parsed) const
509{
510 const vector< po::basic_option<char> >& options = parsed.options;
511
512 // .description -> Pointer to opt_commandline
513 // const std::vector< shared_ptr<option_description> >& options() const;
514
515 //const std::string& key(const std::string& option) const;
516 //const std::string& long_name() const;
517 //const std::string& description() const;
518 //shared_ptr<const value_semantic> semantic() const;
519
520 int maxlen = 0;
521 for (unsigned i=0; i<options.size(); ++i)
522 {
523 const po::basic_option<char> &opt = options[i];
524
525 if (opt.value.size()>0 && opt.string_key[0]!='-')
526 Max(maxlen, opt.string_key.length());
527 }
528
529 cout.setf(ios_base::left);
530
531 // =============> Implement prining of parsed options
532 for(unsigned i=0; i<options.size(); ++i)
533 {
534 const po::basic_option<char> &opt = options[i];
535
536 if (opt.value.size()==0 && !opt.string_key[0]=='-')
537 cout << "--";
538 cout << setw(maxlen) << opt.string_key;
539 if (opt.value.size()>0)
540 cout << " = " << opt.value[0];
541
542 //for (int j=0; j<options[i].value.size(); j++)
543 // cout << "\t = " << options[i].value[j];
544 //cout << "/" << options[i].original_tokens[0];
545
546 ostringstream com;
547
548 if (opt.position_key>=0)
549 com << " [position=" << opt.position_key << "]";
550 if (opt.unregistered)
551 com << " [unregistered]";
552
553 if (!com.str().empty())
554 cout << " # " << com.str();
555
556 cout << endl;
557 }
558}
559
560template<class T>
561string Configuration::VecAsStr(const po::variable_value &v) const
562{
563 ostringstream str;
564
565 const vector<T> vec = v.as<vector<T>>();
566 for (typename std::vector<T>::const_iterator s=vec.begin(); s<vec.end(); s++)
567 str << " " << *s;
568
569 return str.str().substr(1);
570}
571
572string Configuration::VarAsStr(const po::variable_value &v) const
573{
574 if (v.value().type()==typeid(bool))
575 return v.as<bool>() ? "yes ": "no";
576
577 if (v.value().type()==typeid(string))
578 return v.as<string>();
579
580 if (v.value().type()==typeid(int16_t))
581 return to_string((long long int)v.as<int16_t>());
582
583 if (v.value().type()==typeid(int32_t))
584 return to_string((long long int)v.as<int32_t>());
585
586 if (v.value().type()==typeid(int64_t))
587 return to_string((long long int)v.as<int64_t>());
588
589 if (v.value().type()==typeid(uint16_t))
590 return to_string((long long unsigned int)v.as<uint16_t>());
591
592 if (v.value().type()==typeid(uint32_t))
593 return to_string((long long unsigned int)v.as<uint32_t>());
594
595 if (v.value().type()==typeid(uint64_t))
596 return to_string((long long unsigned int)v.as<uint64_t>());
597
598 if (v.value().type()==typeid(float))
599 return to_string((long double)v.as<float>());
600
601 if (v.value().type()==typeid(double))
602 return to_string((long double)v.as<double>());
603
604 if (v.value().type()==typeid(vector<string>))
605 return VecAsStr<string>(v);
606
607 if (v.value().type()==typeid(vector<int16_t>))
608 return VecAsStr<int16_t>(v);
609
610 if (v.value().type()==typeid(vector<int32_t>))
611 return VecAsStr<int32_t>(v);
612
613 if (v.value().type()==typeid(vector<int64_t>))
614 return VecAsStr<int64_t>(v);
615
616 if (v.value().type()==typeid(vector<uint16_t>))
617 return VecAsStr<uint16_t>(v);
618
619 if (v.value().type()==typeid(vector<uint32_t>))
620 return VecAsStr<uint32_t>(v);
621
622 if (v.value().type()==typeid(vector<uint64_t>))
623 return VecAsStr<uint64_t>(v);
624
625 if (v.value().type()==typeid(vector<float>))
626 return VecAsStr<float>(v);
627
628 if (v.value().type()==typeid(vector<double>))
629 return VecAsStr<double>(v);
630
631 ostringstream str;
632 str << hex << setfill('0') << "0x";
633 if (v.value().type()==typeid(Hex<uint16_t>))
634 str << setw(4) << v.as<Hex<uint16_t>>();
635
636 if (v.value().type()==typeid(Hex<uint32_t>))
637 str << setw(8) << v.as<Hex<uint32_t>>();
638
639 if (v.value().type()==typeid(Hex<uint64_t>))
640 str << setw(16) << v.as<Hex<uint64_t>>();
641
642 return str.str();
643}
644
645// --------------------------------------------------------------------------
646//
647//!
648//
649void Configuration::PrintOptions() const
650{
651 cout << "Options propagated to program:" << endl;
652
653 int maxlen = 0;
654 for (map<string,po::variable_value>::const_iterator m=fVariables.begin();
655 m!=fVariables.end(); m++)
656 Max(maxlen, m->first.length());
657
658 cout.setf(ios_base::left);
659
660 // =============> Implement prining of options in use
661 for (map<string,po::variable_value>::const_iterator m=fVariables.begin();
662 m!=fVariables.end(); m++)
663 {
664 const po::variable_value &v = m->second;
665
666 ostringstream str;
667
668 if (v.value().type()==typeid(bool))
669 str << " bool";
670 if (v.value().type()==typeid(string))
671 str << " string";
672 if (v.value().type()==typeid(int16_t))
673 str << " int16_t";
674 if (v.value().type()==typeid(int32_t))
675 str << " int32_t";
676 if (v.value().type()==typeid(int64_t))
677 str << " int64_t";
678 if (v.value().type()==typeid(uint16_t))
679 str << " uint16_t";
680 if (v.value().type()==typeid(uint32_t))
681 str << " uint32_t";
682 if (v.value().type()==typeid(uint64_t))
683 str << " uint64_t";
684 if (v.value().type()==typeid(float))
685 str << " float";
686 if (v.value().type()==typeid(double))
687 str << " double";
688 if (v.value().type()==typeid(Hex<uint16_t>))
689 str << " Hex<uint16_t>";
690 if (v.value().type()==typeid(Hex<uint32_t>))
691 str << " Hex<uint32_t>";
692 if (v.value().type()==typeid(Hex<uint64_t>))
693 str << " Hex<uint64_t>";
694 if (v.value().type()==typeid(vector<string>))
695 str << " vector<string>";
696 if (v.value().type()==typeid(vector<int16_t>))
697 str << " vector<int16_t>";
698 if (v.value().type()==typeid(vector<int32_t>))
699 str << " vector<int32_t>";
700 if (v.value().type()==typeid(vector<int64_t>))
701 str << " vector<int64_t>";
702 if (v.value().type()==typeid(vector<uint16_t>))
703 str << " vector<uint16_t>";
704 if (v.value().type()==typeid(vector<uint32_t>))
705 str << " vector<uint32_t>";
706 if (v.value().type()==typeid(vector<uint64_t>))
707 str << " vector<uint64_t>";
708 if (v.value().type()==typeid(vector<float>))
709 str << " vector<float>";
710 if (v.value().type()==typeid(vector<double>))
711 str << " vector<double>";
712
713 if (str.str().empty())
714 str << " unknown[" << v.value().type().name() << "]";
715
716 const string var = VarAsStr(v);
717 cout << setw(maxlen) << m->first;
718 if (!var.empty())
719 cout << " = ";
720 cout << var << " #" << str.str();
721
722 if (v.defaulted())
723 cout << " [default]";
724 if (v.empty())
725 cout << " [empty]";
726
727 cout << endl;
728 }
729
730 cout << endl;
731}
732
733// --------------------------------------------------------------------------
734//
735//!
736//
737void Configuration::PrintUnknown(const vector<string> &vec, int steps) const
738{
739 for (vector<string>::const_iterator v=vec.begin(); v<vec.end(); v+=steps)
740 cout << " " << *v << endl;
741 cout << endl;
742}
743
744multimap<string, string> Configuration::GetOptions() const
745{
746 multimap<string,string> rc;
747
748 for (map<string,po::variable_value>::const_iterator m=fVariables.begin();
749 m!=fVariables.end(); m++)
750 rc.insert(make_pair(m->first, VarAsStr(m->second)));
751
752 return rc;
753}
754
755// --------------------------------------------------------------------------
756//
757//!
758//
759void Configuration::PrintUnknown() const
760{
761 if (fUnknownCommandline.size())
762 {
763 cout << "Unknown commandline options:" << endl;
764 PrintUnknown(fUnknownCommandline);
765 }
766
767 if (fUnknownConfigfile.size())
768 {
769 cout << "Unknown options in configfile:" << endl;
770 PrintUnknown(fUnknownConfigfile, 2);
771 }
772
773 if (fUnknownEnvironment.size())
774 {
775 cout << "Unknown environment variables:" << endl;
776 PrintUnknown(fUnknownEnvironment);
777 }
778
779 if (fUnknownDatabase.size())
780 {
781 cout << "Unknown database entry:" << endl;
782 PrintUnknown(fUnknownDatabase);
783 }
784}
785
786// --------------------------------------------------------------------------
787//
788//!
789//
790void Configuration::AddOptionsCommandline(const po::options_description &cl, bool visible)
791{
792 fOptionsCommandline[visible].add(cl);
793}
794
795// --------------------------------------------------------------------------
796//
797//!
798//
799void Configuration::AddOptionsConfigfile(const po::options_description &cf, bool visible)
800{
801 fOptionsConfigfile[visible].add(cf);
802}
803
804// --------------------------------------------------------------------------
805//
806//!
807//
808void Configuration::AddOptionsEnvironment(const po::options_description &env, bool visible)
809{
810 fOptionsEnvironment[visible].add(env);
811}
812
813// --------------------------------------------------------------------------
814//
815//!
816//
817void Configuration::AddOptionsDatabase(const po::options_description &db, bool visible)
818{
819 fOptionsDatabase[visible].add(db);
820}
821
822// --------------------------------------------------------------------------
823//
824//!
825//
826void Configuration::SetArgumentPositions(const po::positional_options_description &desc)
827{
828 fArgumentPositions = desc;
829}
830
831// --------------------------------------------------------------------------
832//
833//!
834//
835void Configuration::SetNameMapper(const function<string(string)> &func)
836{
837 fNameMapper = func;
838}
839
840void Configuration::SetNameMapper()
841{
842 fNameMapper = bind1st(mem_fun(&Configuration::DefaultMapper), this);
843}
844
845void Configuration::SetPrintUsage(const function<void(void)> &func)
846{
847 fPrintUsage = func;
848}
849
850void Configuration::SetPrintUsage()
851{
852 fPrintUsage = bind(&Configuration::PrintUsage, this);
853}
854
855void Configuration::SetPrintVersion(const function<void(const string&)> &func)
856{
857 fPrintVersion = func;
858}
859
860void Configuration::SetPrintVersion()
861{
862 fPrintVersion = function<void(const string&)>();
863}
864
865// --------------------------------------------------------------------------
866//
867//!
868//! The idea of the Parse() memeber-function is to parse the command-line,
869//! the configuration files, the databse and the environment and return
870//! a proper combined result.
871//!
872//! In details the following actions are performed in the given order:
873//!
874//! - (0) Init local variables with the list of options described by the
875//! data members.
876//! - (1) Reset the data members fPriorityFile, fDefaultFile, fDatabase
877//! - (2) Parse the command line
878//! - (3) Check for \b --help* command-line options and performe
879//! corresponding action
880//! - (4) Check for \b --print and \b --print-all and perform corresponding
881//! action
882//! - (5) Read and parse the global configuration file, which is compiled
883//! from the path corresponding to the argument given in the
884//! constructor + "/fact++.rc", unrecognized options are always
885//! allowed. Note that in contradiction to all other options
886//! the options in this file are not checked at all. Hence,
887//! typos might stay unnoticed.
888//! - (6) Read and parse the default configuration file, which is either
889//! given by the default name or the \b --default command-line
890//! option. The default name is compiled from the argument
891//! given to the constructor and ".rc". If the file-name is
892//! identical to the default (no command-line option given)
893//! a missing configuration file is no error. Depending on
894//! the \b --dont-check and \b --dont-check-files options,
895//! unrecognized options in the file throw an exception or not.
896//! - (7) Check for \b --print-default and \b --print-all and perform
897//! corresponding action
898//! - (8) Read and parse the priority configuration file, which must be given
899//! by the \b --config or \b -C command-line option or a
900//! corresponding entry in the default-configuration file.
901//! If an option on the command-line and the in the configuration
902//! file exists, the command-line option has priority.
903//! If none is given, no priority file is read. Depending on
904//! the \b --dont-check and \b --dont-check-files options,
905//! unrecognized options in the file throw an exception or not.
906//! - (9) Check for \b --print-config and \b --print-all and perform
907//! corresponding action
908//! - (10) Retrieve options from the database according to the
909//! options \b --database and \b --no-database. Note that
910//! options given on the command-line have highest priority.
911//! The second priority is the priority-configuration file.
912//! The options from the default configuration-file have
913//! lowest priority.
914//! - (11) Check for \b --print-database and \b --print-all and perform
915//! corresponding action
916//! - (12) Parse the environment options.
917//! - (13) Check for \b --print-environment and \b --print-all and perform
918//! corresponding action
919//! - (14) Compile the final result. The priority of the options is (in
920//! decreasing order): command-line options, options from the
921//! priority configuration file, options from the database,
922//! options from the default configuration-file and options
923//! from the environment.
924//! - (15) Find all options which were found and flagged as unrecognized,
925//! because they are not in the user-defined list of described
926//! options, are collected and stored in the corresponding
927//! data-members.
928//! - (16) Find all options which where registered with wildcards and
929//! store the list in fWildcardOptions.
930//! - (17) Before the function returns it check for \b --print-options
931//! and \b --print-unknown and performs the corresponding actions.
932//!
933//!
934//! @param argc,argv
935//! arguments passed to <B>main(int argc, char **argv)</B>
936//!
937//! @returns
938//! A reference to the list with the resulting options with their
939//! values.
940//!
941//! @todo
942//! - describe the exceptions
943//! - describe what happens in a more general way
944//! - print a waring when no default coonfig file is read
945//! - proper handling and error messages if files not available
946//
947const po::variables_map &Configuration::Parse(int argc, const char **argv, const std::function<void()> &PrintHelp)
948{
949 const po::positional_options_description &opt_positional = fArgumentPositions;
950
951 // ------------------------ (0) --------------------------
952#ifdef DEBUG
953 cout << "--0--" << endl;
954#endif
955
956 po::options_description opt_commandline;
957 po::options_description opt_configfile;
958 po::options_description opt_environment;
959 po::options_description opt_database;
960
961 for (int i=0; i<2; i++)
962 {
963 opt_commandline.add(fOptionsCommandline[i]);
964 opt_configfile.add(fOptionsConfigfile[i]);
965 opt_environment.add(fOptionsEnvironment[i]);
966 opt_database.add(fOptionsDatabase[i]);
967 }
968
969 // ------------------------ (1) --------------------------
970#ifdef DEBUG
971 cout << "--1--" << endl;
972#endif
973
974 fPriorityFile = "";
975 fDefaultFile = "";
976 fDatabase = "";
977
978 // ------------------------ (2) --------------------------
979#ifdef DEBUG
980 cout << "--2--" << endl;
981#endif
982
983 po::command_line_parser parser(argc, const_cast<char**>(argv));
984 parser.options(opt_commandline);
985 parser.positional(opt_positional);
986 parser.style(style::unix_style&~style::allow_guessing);
987 //parser.allow_unregistered();
988
989 const po::parsed_options parsed_commandline = parser.run();
990
991 // ------------------------ (3) --------------------------
992#ifdef DEBUG
993 cout << "--3--" << endl;
994#endif
995
996 po::variables_map getfiles;
997 po::store(parsed_commandline, getfiles);
998
999 if (getfiles.count("version"))
1000 PrintVersion();
1001 if (getfiles.count("help"))
1002 {
1003 fPrintUsage();
1004 cout <<
1005 "Options:\n"
1006 "The following describes the available commandline options. "
1007 "For further details on how command line option are parsed "
1008 "and in which order which configuration sources are accessed "
1009 "please refer to the class reference of the Configuration class." << endl;
1010 cout << fOptionsCommandline[kVisible] << endl;
1011 }
1012 if (getfiles.count("help-config"))
1013 cout << fOptionsConfigfile[kVisible] << endl;
1014 if (getfiles.count("help-env"))
1015 cout << fOptionsEnvironment[kVisible] << endl;
1016 if (getfiles.count("help-database"))
1017 cout << fOptionsDatabase[kVisible] << endl;
1018
1019
1020
1021 // ------------------------ (4) --------------------------
1022#ifdef DEBUG
1023 cout << "--4--" << endl;
1024#endif
1025
1026 if (getfiles.count("print") || getfiles.count("print-all"))
1027 {
1028 cout << endl << "Parsed commandline options:" << endl;
1029 PrintParsed(parsed_commandline);
1030 cout << endl;
1031 }
1032
1033 if (getfiles.count("help") || getfiles.count("help-config") ||
1034 getfiles.count("help-env") || getfiles.count("help-database"))
1035 {
1036 if (PrintHelp)
1037 PrintHelp();
1038 }
1039
1040 // ------------------------ (5) --------------------------
1041#ifdef DEBUG
1042 cout << "--5--" << endl;
1043#endif
1044
1045 const boost::filesystem::path path(GetName());
1046 const string globalfile = (path.parent_path()/boost::filesystem::path("fact++.rc")).string();
1047
1048 cerr << "Reading global options from '" << globalfile << "'." << endl;
1049
1050 ifstream gfile(globalfile.c_str());
1051 // ===> FIXME: Proper handling of missing file or wrong file name
1052 const po::parsed_options parsed_globalfile =
1053 !gfile ?
1054 po::parsed_options(&opt_configfile) :
1055 po::parse_config_file<char>(gfile, opt_configfile, true);
1056
1057 // ------------------------ (6) --------------------------
1058#ifdef DEBUG
1059 cout << "--6--" << endl;
1060#endif
1061
1062 // Get default file from command line
1063 if (getfiles.count("default"))
1064 {
1065 fDefaultFile = getfiles["default"].as<string>();
1066 cerr << "Reading default options from '" << fDefaultFile << "'." << endl;
1067 }
1068
1069 const bool checkf = !getfiles.count("dont-check-files") && !getfiles.count("dont-check");
1070 const bool defaulted = getfiles.count("default") && getfiles["default"].defaulted();
1071 //const bool exists = boost::filesystem::exists(fDefaultFile);
1072
1073 ifstream indef(fDefaultFile.c_str());
1074 // ===> FIXME: Proper handling of missing file or wrong file name
1075 const po::parsed_options parsed_defaultfile =
1076 !indef && defaulted ?
1077 po::parsed_options(&opt_configfile) :
1078 po::parse_config_file<char>(indef, opt_configfile, !checkf);
1079
1080 // ------------------------ (7) --------------------------
1081#ifdef DEBUG
1082 cout << "--7--" << endl;
1083#endif
1084
1085 if (getfiles.count("print-default") || getfiles.count("print-all"))
1086 {
1087 if (!indef.is_open() && defaulted)
1088 cout << "No configuration file by --default option specified." << endl;
1089 else
1090 {
1091 cout << endl << "Parsed options from '" << fDefaultFile << "':" << endl;
1092 PrintParsed(parsed_defaultfile);
1093 cout << endl;
1094 }
1095 }
1096
1097 po::store(parsed_defaultfile, getfiles);
1098
1099 // ------------------------ (8) --------------------------
1100#ifdef DEBUG
1101 cout << "--8--" << endl;
1102#endif
1103
1104 // Get priority from commandline(1), defaultfile(2)
1105 if (getfiles.count("config"))
1106 {
1107 fPriorityFile = getfiles["config"].as<string>();
1108 cerr << "Reading config options from '" << fPriorityFile << "'." << endl;
1109 }
1110
1111 ifstream inpri(fPriorityFile.c_str());
1112 // ===> FIXME: Proper handling of missing file or wrong file name
1113 const po::parsed_options parsed_priorityfile =
1114 fPriorityFile.empty() ? po::parsed_options(&opt_configfile) :
1115 po::parse_config_file<char>(inpri, opt_configfile, !checkf);
1116
1117 // ------------------------ (9) --------------------------
1118#ifdef DEBUG
1119 cout << "--9--" << endl;
1120#endif
1121
1122 if (getfiles.count("print-config") || getfiles.count("print-all"))
1123 {
1124 if (fPriorityFile.empty())
1125 cout << "No configuration file by --config option specified." << endl;
1126 else
1127 {
1128 cout << endl << "Parsed options from '" << fPriorityFile << "':" << endl;
1129 PrintParsed(parsed_priorityfile);
1130 cout << endl;
1131 }
1132 }
1133
1134 // ------------------------ (10) -------------------------
1135#ifdef DEBUG
1136 cout << "--10--" << endl;
1137#endif
1138
1139 po::variables_map getdatabase;
1140 po::store(parsed_commandline, getdatabase);
1141 po::store(parsed_priorityfile, getdatabase);
1142 po::store(parsed_defaultfile, getdatabase);
1143 po::store(parsed_globalfile, getdatabase);
1144
1145 if (getdatabase.count("database") && !getdatabase.count("no-database"))
1146 {
1147 fDatabase = getdatabase["database"].as<string>();
1148 cerr << "Requesting options from database for '" << fName << "'" << endl;
1149 }
1150
1151 const bool checkdb = !getdatabase.count("dont-check-database") && !getdatabase.count("dont-check");
1152
1153 const po::parsed_options parsed_database =
1154 fDatabase.empty() ? po::parsed_options(&opt_database) :
1155#if BOOST_VERSION < 104600
1156 parse_database(path.filename(), fDatabase, opt_database, !checkdb);
1157#else
1158 parse_database(path.filename().string(), fDatabase, opt_database, !checkdb);
1159#endif
1160 // ------------------------ (11) -------------------------
1161#ifdef DEBUG
1162 cout << "--11--" << endl;
1163#endif
1164
1165 if (getfiles.count("print-database") || getfiles.count("print-all"))
1166 {
1167 if (fDatabase.empty())
1168 cout << "No database access requested." << endl;
1169 else
1170 {
1171 cout << endl << "Options received from '" << fDatabase << "':" << endl;
1172 PrintParsed(parsed_database);
1173 cout << endl;
1174 }
1175 }
1176
1177 // ------------------------ (12) -------------------------
1178#ifdef DEBUG
1179 cout << "--12--" << endl;
1180#endif
1181
1182 const po::parsed_options parsed_environment = po::parse_environment(opt_environment, fNameMapper);
1183
1184 // ------------------------ (13) -------------------------
1185#ifdef DEBUG
1186 cout << "--13--" << endl;
1187#endif
1188
1189 if (getfiles.count("print-environment"))
1190 {
1191 cout << "Parsed options from environment:" << endl;
1192 PrintParsed(parsed_environment);
1193 cout << endl;
1194 }
1195
1196 // ------------------------ (14) -------------------------
1197#ifdef DEBUG
1198 cout << "--14--" << endl;
1199#endif
1200
1201 po::variables_map result;
1202 po::store(parsed_commandline, result);
1203 po::store(parsed_priorityfile, result);
1204 po::store(parsed_database, result);
1205 po::store(parsed_defaultfile, result);
1206 po::store(parsed_globalfile, result);
1207 po::store(parsed_environment, result);
1208 po::notify(result);
1209
1210 fVariables = result;
1211
1212 // ------------------------ (15) -------------------------
1213#ifdef DEBUG
1214 cout << "--15--" << endl;
1215#endif
1216
1217 const vector<string> unknown0 = collect_unrecognized(parsed_globalfile.options, po::exclude_positional);
1218 const vector<string> unknown1 = collect_unrecognized(parsed_defaultfile.options, po::exclude_positional);
1219 const vector<string> unknown2 = collect_unrecognized(parsed_priorityfile.options, po::exclude_positional);
1220
1221 fUnknownConfigfile.clear();
1222 fUnknownConfigfile.insert(fUnknownConfigfile.end(), unknown0.begin(), unknown0.end());
1223 fUnknownConfigfile.insert(fUnknownConfigfile.end(), unknown1.begin(), unknown1.end());
1224 fUnknownConfigfile.insert(fUnknownConfigfile.end(), unknown2.begin(), unknown2.end());
1225
1226 fUnknownCommandline = collect_unrecognized(parsed_commandline.options, po::exclude_positional);
1227 fUnknownEnvironment = collect_unrecognized(parsed_environment.options, po::exclude_positional);
1228 fUnknownDatabase = collect_unrecognized(parsed_database.options, po::exclude_positional);
1229
1230 // ------------------------ (16) -------------------------
1231#ifdef DEBUG
1232 cout << "--16--" << endl;
1233#endif
1234
1235 CreateWildcardOptions();
1236
1237 // ------------------------ (17) -------------------------
1238#ifdef DEBUG
1239 cout << "--17--" << endl;
1240#endif
1241
1242 if (result.count("print-options"))
1243 PrintOptions();
1244
1245 if (result.count("print-wildcards"))
1246 PrintWildcardOptions();
1247
1248 if (result.count("print-unknown"))
1249 PrintUnknown();
1250
1251#ifdef DEBUG
1252 cout << "------" << endl;
1253#endif
1254
1255 return fVariables;
1256}
1257
1258bool Configuration::DoParse(int argc, const char **argv, const std::function<void()> &PrintHelp)
1259{
1260 try
1261 {
1262 Parse(argc, argv, PrintHelp);
1263 }
1264#if BOOST_VERSION > 104000
1265 catch (po::multiple_occurrences &e)
1266 {
1267 cerr << "Program options invalid due to: " << e.what() << " of '" << e.get_option_name() << "'." << endl;
1268 return false;
1269 }
1270#endif
1271 catch (exception& e)
1272 {
1273 cerr << "Program options invalid due to: " << e.what() << endl;
1274 return false;
1275 }
1276
1277 return !HasVersion() && !HasPrint() && !HasHelp();
1278}
1279
1280// --------------------------------------------------------------------------
1281//
1282//! Create a list of all options which were registered using wildcards
1283//!
1284void Configuration::CreateWildcardOptions()
1285{
1286 po::options_description opts;
1287
1288 for (int i=0; i<2; i++)
1289 {
1290 opts.add(fOptionsCommandline[i]);
1291 opts.add(fOptionsConfigfile[i]);
1292 opts.add(fOptionsEnvironment[i]);
1293 opts.add(fOptionsDatabase[i]);
1294 }
1295
1296 fWildcardOptions.clear();
1297
1298 typedef map<string,po::variable_value> Vars;
1299 typedef vector<boost::shared_ptr<po::option_description>> Descs;
1300
1301 const Descs &desc = opts.options();
1302
1303 for (Vars::const_iterator io=fVariables.begin(); io!=fVariables.end(); io++)
1304 {
1305 for (Descs::const_iterator id=desc.begin(); id!=desc.end(); id++)
1306#if BOOST_VERSION > 104000
1307 if ((*id)->match(io->first, false, false, false)==po::option_description::approximate_match)
1308#else
1309 if ((*id)->match(io->first, false)==po::option_description::approximate_match)
1310#endif
1311 fWildcardOptions[io->first] = (*id)->long_name();
1312 }
1313}
1314
1315// --------------------------------------------------------------------------
1316//
1317//! Print a list of all options which were registered using wildcards and
1318//! have not be registered subsequently by access.
1319//!
1320void Configuration::PrintWildcardOptions() const
1321{
1322 cout << "Options registered with wildcards and not yet accessed:" << endl;
1323
1324 size_t max = 0;
1325 for (auto it=fWildcardOptions.begin(); it!=fWildcardOptions.end(); it++)
1326 if (it->second.length()>max)
1327 max = it->second.length();
1328
1329 cout.setf(ios_base::left);
1330 for (auto it=fWildcardOptions.begin(); it!=fWildcardOptions.end(); it++)
1331 cout << setw(max+1) << it->second << " : " << it->first <<endl;
1332}
1333
1334const vector<string> Configuration::GetWildcardOptions(const std::string &opt) const
1335{
1336 vector<string> rc;
1337
1338 for (auto it=fWildcardOptions.begin(); it!=fWildcardOptions.end(); it++)
1339 {
1340 if (it->second == opt)
1341 rc.push_back(it->first);
1342 }
1343
1344 return rc;
1345}
1346
1347// --------------------------------------------------------------------------
1348//
1349//! Removes /.libs/lt- from a path or just lt- from the filename.
1350//!
1351//! @param src
1352//! input path with filename
1353//! @returns
1354//! path cleaned from libtool extensions
1355//!
1356string Configuration::UnLibToolize(const string &src) const
1357{
1358 const boost::filesystem::path path(src);
1359
1360 string pname = path.parent_path().string();
1361#if BOOST_VERSION < 104600
1362 string fname = path.filename();
1363#else
1364 string fname = path.filename().string();
1365#endif
1366
1367 // If the filename starts with "lt-" remove it from the name
1368 if (fname.substr(0, 3)=="lt-")
1369 fname = fname.substr(3);
1370
1371 string pwd;
1372 // If no directory is contained determine the current directory
1373 if (pname.empty())
1374 pname = boost::filesystem::current_path().string();
1375
1376 // If the directory is relative and just ".libs" forget about it
1377 if (pname==".libs")
1378 return fname;
1379
1380
1381 // Check if the directory is long enough to contain "/.libs"
1382 if (pname.length()>=6)
1383 {
1384 // If the directory ends with "/.libs", remove it
1385 const size_t pos = pname.length()-6;
1386 if (pname.substr(pos)=="/.libs")
1387 pname = pname.substr(0, pos);
1388 }
1389
1390 // If the path is the local path do not return the path-name
1391 if (pname==boost::filesystem::current_path().string())
1392 return fname;
1393
1394 return pname+'/'+fname;
1395}
1396
1397// --------------------------------------------------------------------------
1398//
1399//! Print version information about the program and package.
1400//!
1401//! The program name is taken from fName. If a leading "lt-" is found,
1402//! it is removed. This is useful if the program was build and run
1403//! using libtool.
1404//!
1405//! The package name is taken from the define PACKAGE_STRING. If it is
1406//! not defined (like automatically done by autoconf) no package information
1407//! is printed. The same is true for PACKAGE_URL and PACKAGE_BUGREPORT.
1408//!
1409//! From help2man:
1410//!
1411//! The first line of the --version information is assumed to be in one
1412//! of the following formats:
1413//!
1414//! \verbatim
1415//! - <version>
1416//! - <program> <version>
1417//! - {GNU,Free} <program> <version>
1418//! - <program> ({GNU,Free} <package>) <version>
1419//! - <program> - {GNU,Free} <package> <version>
1420//! \endverbatim
1421//!
1422//! and separated from any copyright/author details by a blank line.
1423//!
1424//! Handle multi-line bug reporting sections of the form:
1425//!
1426//! \verbatim
1427//! - Report <program> bugs to <addr>
1428//! - GNU <package> home page: <url>
1429//! - ...
1430//! \endverbatim
1431//!
1432//! @param name
1433//! name of the program (usually argv[0]).
1434//!
1435void Configuration::PrintVersion() const
1436{
1437#ifndef PACKAGE_STRING
1438#define PACKAGE_STRING ""
1439#endif
1440
1441#ifndef PACKAGE_URL
1442#define PACKAGE_URL ""
1443#endif
1444
1445#ifndef PACKAGE_BUGREPORT
1446#define PACKAGE_BUGREPORT ""
1447#endif
1448
1449 if (fPrintVersion)
1450 {
1451 fPrintVersion(fName);
1452 return;
1453 }
1454
1455#if BOOST_VERSION < 104600
1456 const std::string n = boost::filesystem::path(fName).filename();
1457#else
1458 const std::string n = boost::filesystem::path(fName).filename().string();
1459#endif
1460
1461 const string name = PACKAGE_STRING;
1462 const string bugs = PACKAGE_BUGREPORT;
1463 const string url = PACKAGE_URL;
1464
1465 cout << n;
1466 if (!name.empty())
1467 cout << " - " << name;
1468 cout <<
1469 "\n\n"
1470 "Written by Thomas Bretz et al.\n"
1471 "\n";
1472 if (!bugs.empty())
1473 cout << "Report bugs to <" << bugs << ">\n";
1474 if (!url.empty())
1475 cout << "Home page: " << url << "\n";
1476 cout <<
1477 "\n"
1478 "Copyright (C) 2011 by the FACT Collaboration.\n"
1479 "This is free software; see the source for copying conditions.\n"
1480 << std::endl;
1481}
Note: See TracBrowser for help on using the repository browser.