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

Last change on this file since 11528 was 11483, checked in by tbretz, 13 years ago
Added a registry for wildcarded options to detect unaccessed options; for this EvalConfiguration(const Configuration&) has been changed to EvalOptions(Configuration&)
File size: 46.4 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/regex.hpp>
335#include <boost/filesystem.hpp>
336
337#define HAS_SQL
338
339#ifdef HAS_SQL
340#include <mysql++/mysql++.h>
341#endif
342
343using namespace std;
344
345namespace style = boost::program_options::command_line_style;
346
347// --------------------------------------------------------------------------
348//
349//! The purpose of this function is basically to connect to the database,
350//! and retrieve all the options entries from the 'Configuration' table.
351//!
352//! @param database
353//! The URL of the database from which the configuration data is
354//! retrieved. It should be given in the form
355//! \li [user[:password]@]server.com[:port]/database
356//!
357//! with
358//! - user: user name (default is the current user)
359//! - password: necessary if required by the database rights
360//! - server: the URL of the server (can be 'localhost')
361//! - port: the port to which to connect (usually obsolete)
362//! - database: The name of the database containing the table
363//!
364//! @param desc
365//! A reference to the object with the description of the options
366//! which should be retrieved.
367//!
368//! @param allow_unregistered
369//! If this is true also unregistered, i.e. options unknown to desc,
370//! are returned. Otherwise an exception is thrown if such an option
371//! was retrieved.
372//!
373//! @return
374//! Return an object of type basic_parsed_options containing all
375//! the entries retrieved from the database. Options not found in
376//! desc are flagged as unregistered.
377//!
378//! @throws
379//! Two types of exceptions are thrown
380//! - It thows an unnamed exception if the options could not be
381//! retrieved properly from the databse.
382//! - If an option is not registered within the given descriptions
383//! and \b allow_unregistered is \b false, an exception of type
384//! \b po::unknown_option is thrown.
385//!
386//! @todo
387//! - The exceptions handling should be improved.
388//! - The final database layout is missing in the description
389//! - Shell we allow options to be given more than once?
390//
391#ifdef HAS_SQL
392po::basic_parsed_options<char>
393 Configuration::parse_database(const string &database, const po::options_description& desc, bool allow_unregistered)
394{
395 //static const boost::regex expr("(([[:word:].-]+)(:(.+))?@)?([[:word:].-]+)(:([[:digit:]]+))?(/([[:word:].-]+))?");
396 static const boost::regex expr("(([[:word:].-]+)(:(.+))?@)?([[:word:].-]+)(:([[:digit:]]+))?(/([[:word:].-]+))");
397 // 2: user
398 // 4: pass
399 // 5: server
400 // 7: port
401 // 9: db
402
403 boost::smatch what;
404 if (!boost::regex_match(database, what, expr, boost::match_extra))
405 {
406 cout << "Couldn't parse '" << database << "'." << endl;
407 throw;
408 }
409
410 if (what.size()!=10)
411 {
412 cout << "Error parsing '" << database << "'." << endl;
413 throw;
414 }
415
416 const string user = what[2];
417 const string passwd = what[4];
418 const string server = what[5];
419 const string db = what[9];
420 const int port = stoi(string(what[7]));
421
422 cout << "Connecting to '";
423 if (!user.empty())
424 cout << user << "@";
425 cout << server;
426 if (port)
427 cout << ":" << port;
428 if (!db.empty())
429 cout << "/" << db;
430 cout << "'" << endl;
431
432 mysqlpp::Connection conn(db.c_str(), server.c_str(), user.c_str(), passwd.c_str(), port);
433 /* throws exceptions
434 if (!conn.connected())
435 {
436 cout << "MySQL connection error: " << conn.error() << endl;
437 throw;
438 }*/
439
440 // Retrieve a subset of the sample stock table set up by resetdb
441 // and display it.
442 // FIXME: What about a prefix?
443 const mysqlpp::StoreQueryResult res = conn.query("select `Key`, Value from Configuration").store();
444 /* throws exceptions
445 if (!res)
446 {
447 cout << "MySQL query failed: " << query.error() << endl;
448 throw;
449 }*/
450
451 set<string> allowed_options;
452
453 const vector<boost::shared_ptr<po::option_description>> &options = desc.options();
454 for (unsigned i=0; i<options.size(); ++i)
455 {
456 const po::option_description &d = *options[i];
457 if (d.long_name().empty())
458 boost::throw_exception(po::error("long name required for database"));
459
460 allowed_options.insert(d.long_name());
461 }
462
463 po::parsed_options result(&desc);
464
465 for (vector<mysqlpp::Row>::const_iterator v=res.begin(); v<res.end(); v++)
466 {
467 const string key = (*v)[0].c_str();
468 if (key.empty()) // key == > Throw exception
469 continue;
470
471 // Check if we are allowed to accept unregistered options,
472 // i.e. options which are not in options_description &desc.
473 const bool unregistered = allowed_options.find(key)==allowed_options.end();
474 if (unregistered && allow_unregistered)
475 boost::throw_exception(po::unknown_option(key));
476
477 // Create a key/value-pair and store whether it is a
478 // registered option of not
479 po::option n;
480 n.string_key = key;
481 // This is now identical to file parsing. What if we want
482 // to concatenate options like on the command line?
483 n.value.clear(); // Fixme: composing?
484 n.value.push_back((*v)[1].c_str());
485 n.unregistered = unregistered;
486
487 // If any parsing will be done in the future...
488 //n.value().original_tokens.clear();
489 //n.value().original_tokens.push_back(name);
490 //n.value().original_tokens.push_back(value);
491
492 result.options.push_back(n);
493 }
494
495 cout << endl;
496
497 return result;
498}
499#else
500po::basic_parsed_options<char>
501 Configuration::parse_database(const string &, const po::options_description &desc, bool)
502{
503 return po::parsed_options(&desc);
504}
505#endif
506
507// --------------------------------------------------------------------------
508//
509//!
510//
511Configuration::Configuration(const string &prgname) : fName(UnLibToolize(prgname)),
512fNameMapper(bind1st(mem_fun(&Configuration::DefaultMapper), this)),
513fPrintUsage(bind(&Configuration::PrintUsage, this))
514{
515 po::options_description generic("Generic options");
516 generic.add_options()
517 ("version,V", "Print version information.")
518 ("help", "Print available commandline options.")
519 ("help-environment", "Print available environment variables.")
520 ("help-database", "Print available options retreived from the database.")
521 ("help-config", "Print available configuration file options.")
522 ("print-all", "Print all options as parsed from all the different sources.")
523 ("print", "Print options as parsed from the commandline.")
524 ("print-default", "Print options as parsed from default configuration file.")
525 ("print-database", "Print options as retrieved from the database.")
526 ("print-config", "Print options as parsed from the high priority configuration file.")
527 ("print-environment", "Print options as parsed from the environment.")
528 ("print-unknown", "Print unrecognized options.")
529 ("print-options", "Print options as passed to program.")
530 ("print-wildcards", "Print all options registered with wildcards.")
531 ("dont-check", "Do not check validity of options from files and database.")
532 ("dont-check-files", "Do not check validity of options from files.")
533 ("dont-check-database", "Do not check validity of options from database.")
534 ;
535
536 po::options_description def_config;
537 def_config.add_options()
538 ("default", var<string>(fName+string(".rc")), "Default configuration file.")
539 ;
540
541 po::options_description config("Configuration options");
542 config.add_options()
543 ("config,C", var<string>(), "Configuration file overwriting options retrieved from the database.")
544 ("database", var<string>(), "Database link as in\n\t[user[:password]@]server.com[:port]/database\nOverwrites options from the default configuration file.")
545 ("no-database", "Suppress any access to the database even if a database URL was set.")
546 ;
547
548 fOptionsCommandline[kVisible].add(generic);
549 fOptionsCommandline[kVisible].add(config);
550 fOptionsCommandline[kVisible].add(def_config);
551 fOptionsConfigfile[kVisible].add(config);
552}
553
554// --------------------------------------------------------------------------
555//
556//!
557//
558void Configuration::PrintParsed(const po::parsed_options &parsed) const
559{
560 const vector< po::basic_option<char> >& options = parsed.options;
561
562 // .description -> Pointer to opt_commandline
563 // const std::vector< shared_ptr<option_description> >& options() const;
564
565 //const std::string& key(const std::string& option) const;
566 //const std::string& long_name() const;
567 //const std::string& description() const;
568 //shared_ptr<const value_semantic> semantic() const;
569
570 int maxlen = 0;
571 for (unsigned i=0; i<options.size(); ++i)
572 {
573 const po::basic_option<char> &opt = options[i];
574
575 if (opt.value.size()>0 && opt.string_key[0]!='-')
576 Max(maxlen, opt.string_key.length());
577 }
578
579 cout.setf(ios_base::left);
580
581 // =============> Implement prining of parsed options
582 for(unsigned i=0; i<options.size(); ++i)
583 {
584 const po::basic_option<char> &opt = options[i];
585
586 if (opt.value.size()==0 && !opt.string_key[0]=='-')
587 cout << "--";
588 cout << setw(maxlen) << opt.string_key;
589 if (opt.value.size()>0)
590 cout << " = " << opt.value[0];
591
592 //for (int j=0; j<options[i].value.size(); j++)
593 // cout << "\t = " << options[i].value[j];
594 //cout << "/" << options[i].original_tokens[0];
595
596 ostringstream com;
597
598 if (opt.position_key>=0)
599 com << " [position=" << opt.position_key << "]";
600 if (opt.unregistered)
601 com << " [unregistered]";
602
603 if (!com.str().empty())
604 cout << " # " << com.str();
605
606 cout << endl;
607 }
608}
609
610template<class T>
611string Configuration::VecAsStr(const po::variable_value &v) const
612{
613 ostringstream str;
614
615 const vector<T> vec = v.as<vector<T>>();
616 for (typename std::vector<T>::const_iterator s=vec.begin(); s<vec.end(); s++)
617 str << " " << *s;
618
619 return str.str().substr(1);
620}
621
622string Configuration::VarAsStr(const po::variable_value &v) const
623{
624 if (v.value().type()==typeid(bool))
625 return v.as<bool>() ? "yes ": "no";
626
627 if (v.value().type()==typeid(string))
628 return v.as<string>();
629
630 if (v.value().type()==typeid(int16_t))
631 return to_string((long long int)v.as<int16_t>());
632
633 if (v.value().type()==typeid(int32_t))
634 return to_string((long long int)v.as<int32_t>());
635
636 if (v.value().type()==typeid(int64_t))
637 return to_string((long long int)v.as<int64_t>());
638
639 if (v.value().type()==typeid(uint16_t))
640 return to_string((long long unsigned int)v.as<uint16_t>());
641
642 if (v.value().type()==typeid(uint32_t))
643 return to_string((long long unsigned int)v.as<uint32_t>());
644
645 if (v.value().type()==typeid(uint64_t))
646 return to_string((long long unsigned int)v.as<uint64_t>());
647
648 if (v.value().type()==typeid(float))
649 return to_string((long double)v.as<float>());
650
651 if (v.value().type()==typeid(double))
652 return to_string((long double)v.as<double>());
653
654 if (v.value().type()==typeid(vector<string>))
655 return VecAsStr<string>(v);
656
657 if (v.value().type()==typeid(vector<int16_t>))
658 return VecAsStr<int16_t>(v);
659
660 if (v.value().type()==typeid(vector<int32_t>))
661 return VecAsStr<int32_t>(v);
662
663 if (v.value().type()==typeid(vector<int64_t>))
664 return VecAsStr<int64_t>(v);
665
666 if (v.value().type()==typeid(vector<uint16_t>))
667 return VecAsStr<uint16_t>(v);
668
669 if (v.value().type()==typeid(vector<uint32_t>))
670 return VecAsStr<uint32_t>(v);
671
672 if (v.value().type()==typeid(vector<uint64_t>))
673 return VecAsStr<uint64_t>(v);
674
675 if (v.value().type()==typeid(vector<float>))
676 return VecAsStr<float>(v);
677
678 if (v.value().type()==typeid(vector<double>))
679 return VecAsStr<double>(v);
680
681 ostringstream str;
682 str << hex << setfill('0') << "0x";
683 if (v.value().type()==typeid(Hex<uint16_t>))
684 str << setw(4) << v.as<Hex<uint16_t>>();
685
686 if (v.value().type()==typeid(Hex<uint32_t>))
687 str << setw(8) << v.as<Hex<uint32_t>>();
688
689 if (v.value().type()==typeid(Hex<uint64_t>))
690 str << setw(16) << v.as<Hex<uint64_t>>();
691
692 return str.str();
693}
694
695// --------------------------------------------------------------------------
696//
697//!
698//
699void Configuration::PrintOptions() const
700{
701 cout << "Options propagated to program:" << endl;
702
703 int maxlen = 0;
704 for (map<string,po::variable_value>::const_iterator m=fVariables.begin();
705 m!=fVariables.end(); m++)
706 Max(maxlen, m->first.length());
707
708 cout.setf(ios_base::left);
709
710 // =============> Implement prining of options in use
711 for (map<string,po::variable_value>::const_iterator m=fVariables.begin();
712 m!=fVariables.end(); m++)
713 {
714 const po::variable_value &v = m->second;
715
716 ostringstream str;
717
718 if (v.value().type()==typeid(bool))
719 str << " bool";
720 if (v.value().type()==typeid(string))
721 str << " string";
722 if (v.value().type()==typeid(int16_t))
723 str << " int16_t";
724 if (v.value().type()==typeid(int32_t))
725 str << " int32_t";
726 if (v.value().type()==typeid(int64_t))
727 str << " int64_t";
728 if (v.value().type()==typeid(uint16_t))
729 str << " uint16_t";
730 if (v.value().type()==typeid(uint32_t))
731 str << " uint32_t";
732 if (v.value().type()==typeid(uint64_t))
733 str << " uint64_t";
734 if (v.value().type()==typeid(float))
735 str << " float";
736 if (v.value().type()==typeid(double))
737 str << " double";
738 if (v.value().type()==typeid(Hex<uint16_t>))
739 str << " Hex<uint16_t>";
740 if (v.value().type()==typeid(Hex<uint32_t>))
741 str << " Hex<uint32_t>";
742 if (v.value().type()==typeid(Hex<uint64_t>))
743 str << " Hex<uint64_t>";
744 if (v.value().type()==typeid(vector<string>))
745 str << " vector<string>";
746 if (v.value().type()==typeid(vector<int16_t>))
747 str << " vector<int16_t>";
748 if (v.value().type()==typeid(vector<int32_t>))
749 str << " vector<int32_t>";
750 if (v.value().type()==typeid(vector<int64_t>))
751 str << " vector<int64_t>";
752 if (v.value().type()==typeid(vector<uint16_t>))
753 str << " vector<uint16_t>";
754 if (v.value().type()==typeid(vector<uint32_t>))
755 str << " vector<uint32_t>";
756 if (v.value().type()==typeid(vector<uint64_t>))
757 str << " vector<uint64_t>";
758 if (v.value().type()==typeid(vector<float>))
759 str << " vector<float>";
760 if (v.value().type()==typeid(vector<double>))
761 str << " vector<double>";
762
763 if (str.str().empty())
764 str << " unknown[" << v.value().type().name() << "]";
765
766 const string var = VarAsStr(v);
767 cout << setw(maxlen) << m->first;
768 if (!var.empty())
769 cout << " = ";
770 cout << var << " #" << str.str();
771
772 if (v.defaulted())
773 cout << " [default]";
774 if (v.empty())
775 cout << " [empty]";
776
777 cout << endl;
778 }
779
780 cout << endl;
781}
782
783// --------------------------------------------------------------------------
784//
785//!
786//
787void Configuration::PrintUnknown(const vector<string> &vec, int steps) const
788{
789 for (vector<string>::const_iterator v=vec.begin(); v<vec.end(); v+=steps)
790 cout << " " << *v << endl;
791 cout << endl;
792}
793
794multimap<string, string> Configuration::GetOptions() const
795{
796 multimap<string,string> rc;
797
798 for (map<string,po::variable_value>::const_iterator m=fVariables.begin();
799 m!=fVariables.end(); m++)
800 rc.insert(make_pair(m->first, VarAsStr(m->second)));
801
802 return rc;
803}
804
805// --------------------------------------------------------------------------
806//
807//!
808//
809void Configuration::PrintUnknown() const
810{
811 if (fUnknownCommandline.size())
812 {
813 cout << "Unknown commandline options:" << endl;
814 PrintUnknown(fUnknownCommandline);
815 }
816
817 if (fUnknownConfigfile.size())
818 {
819 cout << "Unknown options in configfile:" << endl;
820 PrintUnknown(fUnknownConfigfile, 2);
821 }
822
823 if (fUnknownEnvironment.size())
824 {
825 cout << "Unknown environment variables:" << endl;
826 PrintUnknown(fUnknownEnvironment);
827 }
828
829 if (fUnknownDatabase.size())
830 {
831 cout << "Unknown database entry:" << endl;
832 PrintUnknown(fUnknownDatabase);
833 }
834}
835
836// --------------------------------------------------------------------------
837//
838//!
839//
840void Configuration::AddOptionsCommandline(const po::options_description &cl, bool visible)
841{
842 fOptionsCommandline[visible].add(cl);
843}
844
845// --------------------------------------------------------------------------
846//
847//!
848//
849void Configuration::AddOptionsConfigfile(const po::options_description &cf, bool visible)
850{
851 fOptionsConfigfile[visible].add(cf);
852}
853
854// --------------------------------------------------------------------------
855//
856//!
857//
858void Configuration::AddOptionsEnvironment(const po::options_description &env, bool visible)
859{
860 fOptionsEnvironment[visible].add(env);
861}
862
863// --------------------------------------------------------------------------
864//
865//!
866//
867void Configuration::AddOptionsDatabase(const po::options_description &db, bool visible)
868{
869 fOptionsDatabase[visible].add(db);
870}
871
872// --------------------------------------------------------------------------
873//
874//!
875//
876void Configuration::SetArgumentPositions(const po::positional_options_description &desc)
877{
878 fArgumentPositions = desc;
879}
880
881// --------------------------------------------------------------------------
882//
883//!
884//
885void Configuration::SetNameMapper(const function<string(string)> &func)
886{
887 fNameMapper = func;
888}
889
890void Configuration::SetNameMapper()
891{
892 fNameMapper = bind1st(mem_fun(&Configuration::DefaultMapper), this);
893}
894
895void Configuration::SetPrintUsage(const function<void(void)> &func)
896{
897 fPrintUsage = func;
898}
899
900void Configuration::SetPrintUsage()
901{
902 fPrintUsage = bind(&Configuration::PrintUsage, this);
903}
904
905void Configuration::SetPrintVersion(const function<void(const string&)> &func)
906{
907 fPrintVersion = func;
908}
909
910void Configuration::SetPrintVersion()
911{
912 fPrintVersion = function<void(const string&)>();
913}
914
915// --------------------------------------------------------------------------
916//
917//!
918//! The idea of the Parse() memeber-function is to parse the command-line,
919//! the configuration files, the databse and the environment and return
920//! a proper combined result.
921//!
922//! In details the following actions are performed in the given order:
923//!
924//! - (0) Init local variables with the list of options described by the
925//! data members.
926//! - (1) Reset the data members fPriorityFile, fDefaultFile, fDatabase
927//! - (2) Parse the command line
928//! - (3) Check for \b --help* command-line options and performe
929//! corresponding action
930//! - (4) Check for \b --print and \b --print-all and perform corresponding
931//! action
932//! - (5) Read and parse the global configuration file, which is compiled
933//! from the path corresponding to the argument given in the
934//! constructor + "/fact++.rc", unrecognized options are always
935//! allowed.
936//! - (6) Read and parse the default configuration file, which is either
937//! given by the default name or the \b --default command-line
938//! option. The default name is compiled from the argument
939//! given to the constructor and ".rc". If the file-name is
940//! identical to the default (no command-line option given)
941//! a missing configuration file is no error. Depending on
942//! the \b --dont-check and \b --dont-check-files options,
943//! unrecognized options in the file throw an exception or not.
944//! - (7) Check for \b --print-default and \b --print-all and perform
945//! corresponding action
946//! - (8) Read and parse the priority configuration file, which must be given
947//! by the \b --config or \b -C command-line option or a
948//! corresponding entry in the default-configuration file.
949//! If an option on the command-line and the in the configuration
950//! file exists, the command-line option has priority.
951//! If none is given, no priority file is read. Depending on
952//! the \b --dont-check and \b --dont-check-files options,
953//! unrecognized options in the file throw an exception or not.
954//! - (9) Check for \b --print-config and \b --print-all and perform
955//! corresponding action
956//! - (10) Retrieve options from the database according to the
957//! options \b --database and \b --no-database. Note that
958//! options given on the command-line have highest priority.
959//! The second priority is the priority-configuration file.
960//! The options from the default configuration-file have
961//! lowest priority.
962//! - (11) Check for \b --print-database and \b --print-all and perform
963//! corresponding action
964//! - (12) Parse the environment options.
965//! - (13) Check for \b --print-environment and \b --print-all and perform
966//! corresponding action
967//! - (14) Compile the final result. The priority of the options is (in
968//! decreasing order): command-line options, options from the
969//! priority configuration file, options from the database,
970//! options from the default configuration-file and options
971//! from the environment.
972//! - (15) Find all options which were found and flagged as unrecognized,
973//! because they are not in the user-defined list of described
974//! options, are collected and stored in the corresponding
975//! data-members.
976//! - (16) Find all options which where registered with wildcards and
977//! store the list in fWildcardOptions.
978//! - (17) Before the function returns it check for \b --print-options
979//! and \b --print-unknown and performs the corresponding actions.
980//!
981//!
982//! @param argc,argv
983//! arguments passed to <B>main(int argc, char **argv)</B>
984//!
985//! @returns
986//! A reference to the list with the resulting options with their
987//! values.
988//!
989//! @todo
990//! - describe the exceptions
991//! - describe what happens in a more general way
992//! - print a waring when no default coonfig file is read
993//! - proper handling and error messages if files not available
994//
995const po::variables_map &Configuration::Parse(int argc, const char **argv)
996{
997 const po::positional_options_description &opt_positional = fArgumentPositions;
998
999 // ------------------------ (0) --------------------------
1000
1001 po::options_description opt_commandline;
1002 po::options_description opt_configfile;
1003 po::options_description opt_environment;
1004 po::options_description opt_database;
1005
1006 for (int i=0; i<2; i++)
1007 {
1008 opt_commandline.add(fOptionsCommandline[i]);
1009 opt_configfile.add(fOptionsConfigfile[i]);
1010 opt_environment.add(fOptionsEnvironment[i]);
1011 opt_database.add(fOptionsDatabase[i]);
1012 }
1013
1014 // ------------------------ (1) --------------------------
1015
1016 fPriorityFile = "";
1017 fDefaultFile = "";
1018 fDatabase = "";
1019
1020 // ------------------------ (2) --------------------------
1021
1022 po::command_line_parser parser(argc, const_cast<char**>(argv));
1023 parser.options(opt_commandline);
1024 parser.positional(opt_positional);
1025 parser.style(style::unix_style&~style::allow_guessing);
1026 //parser.allow_unregistered();
1027
1028 const po::parsed_options parsed_commandline = parser.run();
1029
1030 // ------------------------ (3) --------------------------
1031
1032 po::variables_map getfiles;
1033 po::store(parsed_commandline, getfiles);
1034
1035 if (getfiles.count("version"))
1036 PrintVersion();
1037 if (getfiles.count("help"))
1038 {
1039 fPrintUsage();
1040 cout <<
1041 "Options:\n"
1042 "The following describes the available commandline options. "
1043 "For further details on how command line option are parsed "
1044 "and in which order which configuration sources are accessed "
1045 "please refer to the class reference of the Configuration class." << endl;
1046 cout << fOptionsCommandline[kVisible] << endl;
1047 }
1048 if (getfiles.count("help-config"))
1049 cout << fOptionsConfigfile[kVisible] << endl;
1050 if (getfiles.count("help-env"))
1051 cout << fOptionsEnvironment[kVisible] << endl;
1052 if (getfiles.count("help-database"))
1053 cout << fOptionsDatabase[kVisible] << endl;
1054
1055 // ------------------------ (4) --------------------------
1056
1057 if (getfiles.count("print") || getfiles.count("print-all"))
1058 {
1059 cout << endl << "Parsed commandline options:" << endl;
1060 PrintParsed(parsed_commandline);
1061 cout << endl;
1062 }
1063
1064 // ------------------------ (5) --------------------------
1065
1066 const boost::filesystem::path path(GetName());
1067 const string globalfile = path.parent_path().string()+"/fact++.rc";
1068
1069 cerr << "Reading global options from '" << globalfile << "'." << endl;
1070
1071 ifstream gfile(globalfile.c_str());
1072 // ===> FIXME: Proper handling of missing file or wrong file name
1073 const po::parsed_options parsed_globalfile =
1074 !gfile ?
1075 po::parsed_options(&opt_configfile) :
1076 po::parse_config_file<char>(gfile, opt_configfile, false);
1077
1078 // ------------------------ (6) --------------------------
1079
1080 // Get default file from command line
1081 if (getfiles.count("default"))
1082 {
1083 fDefaultFile = getfiles["default"].as<string>();
1084 cerr << "Reading default options from '" << fDefaultFile << "'." << endl;
1085 }
1086
1087 const bool checkf = !getfiles.count("dont-check-files") && !getfiles.count("dont-check");
1088 const bool defaulted = getfiles.count("default") && getfiles["default"].defaulted();
1089 //const bool exists = boost::filesystem::exists(fDefaultFile);
1090
1091 ifstream indef(fDefaultFile.c_str());
1092 // ===> FIXME: Proper handling of missing file or wrong file name
1093 const po::parsed_options parsed_defaultfile =
1094 !indef && defaulted ?
1095 po::parsed_options(&opt_configfile) :
1096 po::parse_config_file<char>(indef, opt_configfile, !checkf);
1097
1098 // ------------------------ (7) --------------------------
1099
1100 if (getfiles.count("print-default") || getfiles.count("print-all"))
1101 {
1102 if (!indef.is_open() && defaulted)
1103 cout << "No configuration file by --default option specified." << endl;
1104 else
1105 {
1106 cout << endl << "Parsed options from '" << fDefaultFile << "':" << endl;
1107 PrintParsed(parsed_defaultfile);
1108 cout << endl;
1109 }
1110 }
1111
1112 po::store(parsed_defaultfile, getfiles);
1113
1114 // ------------------------ (8) --------------------------
1115
1116 // Get priority from commandline(1), defaultfile(2)
1117 if (getfiles.count("config"))
1118 {
1119 fPriorityFile = getfiles["config"].as<string>();
1120 cerr << "Reading config options from '" << fPriorityFile << "'." << endl;
1121 }
1122
1123 ifstream inpri(fPriorityFile.c_str());
1124 // ===> FIXME: Proper handling of missing file or wrong file name
1125 const po::parsed_options parsed_priorityfile =
1126 fPriorityFile.empty() ? po::parsed_options(&opt_configfile) :
1127 po::parse_config_file<char>(inpri, opt_configfile, !checkf);
1128
1129 // ------------------------ (9) --------------------------
1130
1131 if (getfiles.count("print-config") || getfiles.count("print-all"))
1132 {
1133 if (fPriorityFile.empty())
1134 cout << "No configuration file by --config option specified." << endl;
1135 else
1136 {
1137 cout << endl << "Parsed options from '" << fPriorityFile << "':" << endl;
1138 PrintParsed(parsed_priorityfile);
1139 cout << endl;
1140 }
1141 }
1142
1143 // ------------------------ (10) -------------------------
1144
1145 po::variables_map getdatabase;
1146 po::store(parsed_commandline, getdatabase);
1147 po::store(parsed_priorityfile, getdatabase);
1148 po::store(parsed_defaultfile, getdatabase);
1149 po::store(parsed_globalfile, getdatabase);
1150
1151 if (getdatabase.count("database") && !getdatabase.count("no-database"))
1152 {
1153 fDatabase = getdatabase["database"].as<string>();
1154 cerr << "Requesting options from '" << fDatabase << "'." << endl;
1155 }
1156
1157 const bool checkdb = !getdatabase.count("dont-check-database") && !getdatabase.count("dont-check");
1158
1159 const po::parsed_options parsed_database =
1160 fDatabase.empty() ? po::parsed_options(&opt_database) :
1161 parse_database(fDatabase, opt_database, !checkdb);
1162
1163 // ------------------------ (11) -------------------------
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
1179 const po::parsed_options parsed_environment = po::parse_environment(opt_environment, fNameMapper);
1180
1181 // ------------------------ (13) -------------------------
1182
1183 if (getfiles.count("print-environment"))
1184 {
1185 cout << "Parsed options from environment:" << endl;
1186 PrintParsed(parsed_environment);
1187 cout << endl;
1188 }
1189
1190 // ------------------------ (14) -------------------------
1191 po::variables_map result;
1192 po::store(parsed_commandline, result);
1193 po::store(parsed_priorityfile, result);
1194 po::store(parsed_database, result);
1195 po::store(parsed_defaultfile, result);
1196 po::store(parsed_globalfile, result);
1197 po::store(parsed_environment, result);
1198 po::notify(result);
1199
1200 fVariables = result;
1201
1202 // ------------------------ (15) -------------------------
1203
1204 const vector<string> unknown0 = collect_unrecognized(parsed_globalfile.options, po::exclude_positional);
1205 const vector<string> unknown1 = collect_unrecognized(parsed_defaultfile.options, po::exclude_positional);
1206 const vector<string> unknown2 = collect_unrecognized(parsed_priorityfile.options, po::exclude_positional);
1207
1208 fUnknownConfigfile.clear();
1209 fUnknownConfigfile.insert(fUnknownConfigfile.end(), unknown0.begin(), unknown0.end());
1210 fUnknownConfigfile.insert(fUnknownConfigfile.end(), unknown1.begin(), unknown1.end());
1211 fUnknownConfigfile.insert(fUnknownConfigfile.end(), unknown2.begin(), unknown2.end());
1212
1213 fUnknownCommandline = collect_unrecognized(parsed_commandline.options, po::exclude_positional);
1214 fUnknownEnvironment = collect_unrecognized(parsed_environment.options, po::exclude_positional);
1215 fUnknownDatabase = collect_unrecognized(parsed_database.options, po::exclude_positional);
1216
1217 // ------------------------ (16) -------------------------
1218
1219 CreateWildcardOptions();
1220
1221 // ------------------------ (17) -------------------------
1222
1223 if (result.count("print-options"))
1224 PrintOptions();
1225
1226 if (result.count("print-wildcards"))
1227 PrintWildcardOptions();
1228
1229 if (result.count("print-unknown"))
1230 PrintUnknown();
1231
1232 return fVariables;
1233}
1234
1235// --------------------------------------------------------------------------
1236//
1237//! Create a list of all options which were registered using wildcards
1238//!
1239void Configuration::CreateWildcardOptions()
1240{
1241 po::options_description opts;
1242
1243 for (int i=0; i<2; i++)
1244 {
1245 opts.add(fOptionsCommandline[i]);
1246 opts.add(fOptionsConfigfile[i]);
1247 opts.add(fOptionsEnvironment[i]);
1248 opts.add(fOptionsDatabase[i]);
1249 }
1250
1251 fWildcardOptions.clear();
1252
1253 typedef map<string,po::variable_value> Vars;
1254 typedef vector<boost::shared_ptr<po::option_description>> Descs;
1255
1256 const Descs &desc = opts.options();
1257
1258 for (Vars::const_iterator io=fVariables.begin(); io!=fVariables.end(); io++)
1259 {
1260 for (Descs::const_iterator id=desc.begin(); id!=desc.end(); id++)
1261 if ((*id)->match(io->first, false, false, false)==po::option_description::approximate_match)
1262 fWildcardOptions[io->first] = (*id)->long_name();
1263 }
1264}
1265
1266// --------------------------------------------------------------------------
1267//
1268//! Print a list of all options which were registered using wildcards and
1269//! have not be registered subsequently by access.
1270//!
1271void Configuration::PrintWildcardOptions() const
1272{
1273 cout << "Options registered with wildcards and not yet accessed:" << endl;
1274
1275 size_t max = 0;
1276 for (map<string,string>::const_iterator it=fWildcardOptions.begin(); it!=fWildcardOptions.end(); it++)
1277 if (it->second.length()>max)
1278 max = it->second.length();
1279
1280 cout.setf(ios_base::left);
1281 for (map<string,string>::const_iterator it=fWildcardOptions.begin(); it!=fWildcardOptions.end(); it++)
1282 cout << setw(max+1) << it->second << " : " << it->first <<endl;
1283}
1284
1285// --------------------------------------------------------------------------
1286//
1287//! Removes /.libs/lt- from a path or just lt- from the filename.
1288//!
1289//! @param src
1290//! input path with filename
1291//! @returns
1292//! path cleaned from libtool extensions
1293//!
1294string Configuration::UnLibToolize(const string &src) const
1295{
1296 const boost::filesystem::path path(src);
1297
1298 string pname = path.parent_path().string();
1299 string fname = path.filename();
1300
1301 // If the filename starts with "lt-" remove it from the name
1302 if (fname.substr(0, 3)=="lt-")
1303 fname = fname.substr(3);
1304
1305 // If no directory is contained determine the current directory
1306 if (pname.empty())
1307 pname = boost::filesystem::current_path().string();
1308
1309 // If the directory is relative and just ".libs" forget about it
1310 if (pname==".libs")
1311 return fname;
1312
1313 // Check if the directory is long enough to contain "/.libs"
1314 if (pname.length()>=6)
1315 {
1316 // If the directory ends with "/.libs", remove it
1317 const size_t pos = pname.length()-6;
1318 if (pname.substr(pos)=="/.libs")
1319 pname = pname.substr(0, pos);
1320 }
1321
1322 return pname+'/'+fname;
1323}
1324
1325// --------------------------------------------------------------------------
1326//
1327//! Print version information about the program and package.
1328//!
1329//! The program name is taken from fName. If a leading "lt-" is found,
1330//! it is removed. This is useful if the program was build and run
1331//! using libtool.
1332//!
1333//! The package name is taken from the define PACKAGE_STRING. If it is
1334//! not defined (like automatically done by autoconf) no package information
1335//! is printed. The same is true for PACKAGE_URL and PACKAGE_BUGREPORT.
1336//!
1337//! From help2man:
1338//!
1339//! The first line of the --version information is assumed to be in one
1340//! of the following formats:
1341//!
1342//! \verbatim
1343//! - <version>
1344//! - <program> <version>
1345//! - {GNU,Free} <program> <version>
1346//! - <program> ({GNU,Free} <package>) <version>
1347//! - <program> - {GNU,Free} <package> <version>
1348//! \endverbatim
1349//!
1350//! and separated from any copyright/author details by a blank line.
1351//!
1352//! Handle multi-line bug reporting sections of the form:
1353//!
1354//! \verbatim
1355//! - Report <program> bugs to <addr>
1356//! - GNU <package> home page: <url>
1357//! - ...
1358//! \endverbatim
1359//!
1360//! @param name
1361//! name of the program (usually argv[0]).
1362//!
1363void Configuration::PrintVersion() const
1364{
1365#ifndef PACKAGE_STRING
1366#define PACKAGE_STRING ""
1367#endif
1368
1369#ifndef PACKAGE_URL
1370#define PACKAGE_URL ""
1371#endif
1372
1373#ifndef PACKAGE_BUGREPORT
1374#define PACKAGE_BUGREPORT ""
1375#endif
1376
1377 if (fPrintVersion)
1378 {
1379 fPrintVersion(fName);
1380 return;
1381 }
1382
1383 const std::string n = boost::filesystem::path(fName).filename();
1384
1385 const string name = PACKAGE_STRING;
1386 const string bugs = PACKAGE_BUGREPORT;
1387 const string url = PACKAGE_URL;
1388
1389 cout << n;
1390 if (!name.empty())
1391 cout << " - " << name;
1392 cout <<
1393 "\n\n"
1394 "Written by Thomas Bretz et al.\n"
1395 "\n";
1396 if (!bugs.empty())
1397 cout << "Report bugs to <" << bugs << ">\n";
1398 if (!url.empty())
1399 cout << "Home page: " << url << "\n";
1400 cout <<
1401 "\n"
1402 "Copyright (C) 2011 by the FACT Collaboration.\n"
1403 "This is free software; see the source for copying conditions.\n"
1404 << std::endl;
1405}
Note: See TracBrowser for help on using the repository browser.