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

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