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

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