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

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