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

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