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

Last change on this file since 10707 was 10707, checked in by tbretz, 9 years ago
Use the functionality of boost program_options better; implemented po_bool() as a special po_switch() case; added some comments.
File size: 34.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 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        ("help",                "Print available commandline options.")
476        ("help-environment",    "Print available environment variables.")
477        ("help-database",       "Print available options retreived from the database.")
478        ("help-config",         "Print available configuration file options.")
479        ("version,V",           "Print version information.")
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
736// --------------------------------------------------------------------------
737//
738//!
739//! The idea of the Parse() memeber-function is to parse the command-line,
740//! the configuration files, the databse and the environment and return
741//! a proper combined result.
742//!
743//! In details the following actions are performed in the given order:
744//!
745//!  - (0)  Init local variables with the list of options described by the
746//!         data members.
747//!  - (1)  Reset the data members fPriorityFile, fDefaultFile, fDatabase
748//!  - (2)  Parse the command line
749//!  - (3)  Check for \b --help* command-line options and performe
750//!         corresponding action
751//!  - (4)  Check for \b --print and \b --print-all and perform corresponding
752//!         action
753//!  - (5)  Read and parse the default configuration file, which is either
754//!         given by the default name or the \b --default command-line
755//!         option. The default name is compiled from the argument
756//!         given to the constructor and ".rc".  If the file-name is
757//!         identical to the default (no command-line option given)
758//!         a missing configuration file is no error. Depending on
759//!         the \b --dont-check and \b --dont-check-files options,
760//!         unrecognized options in the file throw an exception or not.
761//!  - (6)  Check for \b --print-default and \b --print-all and perform
762//!         corresponding action
763//!  - (7)  Read and parse the priority configuration file, which must be given
764//!         by the \b --config or \b -C command-line option or a
765//!         corresponding entry in the default-configuration file.
766//!         If an option on the command-line and the in the configuration
767//!         file exists, the command-line option has priority.
768//!         If none is given, no priority file is read. 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//!  - (8)  Check for \b --print-config and \b --print-all and perform
772//!         corresponding action
773//!  - (9)  Retrieve options from the database according to the
774//!         options \b --database and \b --no-database. Note that
775//!         options given on the command-line have highest priority.
776//!         The second priority is the priority-configuration file.
777//!         The options from the default configuration-file have
778//!         lowest priority.
779//!  - (10) Check for \b --print-database and \b --print-all and perform
780//!         corresponding action
781//!  - (11)  Parse the environment options.
782//!  - (12) Check for \b --print-environment and \b --print-all and perform
783//!         corresponding action
784//!  - (13) Compile the final result. The priority of the options is (in
785//!         decreasing order): command-line options, options from the
786//!         priority configuration file, options from the database,
787//!         options from the default configuration-file and options
788//!         from the environment.
789//!  - (14) Finally all options which were found and flagged as unrecognized,
790//!         because they are not in the user-defined list of described
791//!         options, are collected and stored in the corresponding
792//!         data-members.
793//!  - (15) Before the function returns it check for \b --print-options
794//!         and \b --print-unknown and performs the corresponding actions.
795//!
796//!
797//! @param argc,argv
798//!    arguments passed to <B>main(int argc, char **argv)</B>
799//!
800//! @returns
801//!    A reference to the list with the resulting options with their
802//!    values.
803//!
804//! @todo
805//!    - describe the exceptions
806//!    - describe what happens in a more general way
807//!    - print a waring when no default coonfig file is read
808//!    - proper handling and error messages if files not available
809//
810const po::variables_map &Configuration::Parse(int argc, const char **argv)
811{
812    const po::positional_options_description &opt_positional = fArgumentPositions;
813
814    // ------------------------ (0) --------------------------
815
816    po::options_description opt_commandline;
817    po::options_description opt_configfile;
818    po::options_description opt_environment;
819    po::options_description opt_database;
820
821    for (int i=0; i<2; i++)
822    {
823        opt_commandline.add(fOptionsCommandline[i]);
824        opt_configfile.add(fOptionsConfigfile[i]);
825        opt_environment.add(fOptionsEnvironment[i]);
826        opt_database.add(fOptionsDatabase[i]);
827    }
828
829    // ------------------------ (1) --------------------------
830
831    fPriorityFile = "";
832    fDefaultFile  = "";
833    fDatabase     = "";
834
835    // ------------------------ (2) --------------------------
836
837    po::command_line_parser parser(argc, const_cast<char**>(argv));
838    parser.options(opt_commandline);
839    parser.positional(opt_positional);
840    parser.style(style::unix_style&~style::allow_guessing);
841    //parser.allow_unregistered();
842
843    const po::parsed_options parsed_commandline = parser.run();
844
845    // ------------------------ (3) --------------------------
846
847    po::variables_map getfiles;
848    po::store(parsed_commandline, getfiles);
849
850    if (getfiles.count("help"))
851    {
852        fPrintUsage();
853        cout <<
854            "Options:\n"
855            "The following describes the available commandline options. "
856            "For further details on how command line option are parsed "
857            "and in which order which configuration sources are accessed "
858            "please refer to the class reference of the Configuration class." << endl;
859        cout << fOptionsCommandline[kVisible] << endl;
860    }
861    if (getfiles.count("help-config"))
862        cout << fOptionsConfigfile[kVisible] << endl;
863    if (getfiles.count("help-env"))
864        cout << fOptionsEnvironment[kVisible] << endl;
865    if (getfiles.count("help-database"))
866        cout << fOptionsDatabase[kVisible] << endl;
867
868    // ------------------------ (4) --------------------------
869
870    if (getfiles.count("print") || getfiles.count("print-all"))
871    {
872        cout << endl << "Parsed commandline options:" << endl;
873        PrintParsed(parsed_commandline);
874        cout << endl;
875    }
876
877    // ------------------------ (5) --------------------------
878
879    // Get default file from command line
880    if (getfiles.count("default"))
881    {
882        fDefaultFile = getfiles["default"].as<string>();
883        cerr << "Reading configuration from '" << fDefaultFile << "'." << endl;
884    }
885
886    const bool checkf    = !getfiles.count("dont-check-files") && !getfiles.count("dont-check");
887    const bool defaulted = getfiles.count("default") && getfiles["default"].defaulted();
888    //const bool exists    = boost::filesystem::exists(fDefaultFile);
889
890    ifstream indef(fDefaultFile.c_str());
891    // ===> FIXME: Proper handling of missing file or wrong file name
892    const po::parsed_options parsed_defaultfile =
893        !indef && defaulted ?
894        po::parsed_options(&opt_configfile) :
895        po::parse_config_file<char>(indef, opt_configfile, !checkf);
896
897    // ------------------------ (6) --------------------------
898
899    if (getfiles.count("print-default") || getfiles.count("print-all"))
900    {
901        if (!indef && defaulted)
902            cout << "No configuration file by --default option specified." << endl;
903        else
904        {
905            cout << endl << "Parsed options from '" << fDefaultFile << "':" << endl;
906            PrintParsed(parsed_defaultfile);
907            cout << endl;
908        }
909    }
910
911    po::store(parsed_defaultfile, getfiles);
912
913    // ------------------------ (7) --------------------------
914
915    // Get priority from commandline(1), defaultfile(2)
916    if (getfiles.count("config"))
917    {
918        fPriorityFile = getfiles["config"].as<string>();
919        cerr << "Retrieved option from '" << fPriorityFile << "'." << endl;
920    }
921
922    ifstream inpri(fPriorityFile.c_str());
923    // ===> FIXME: Proper handling of missing file or wrong file name
924    const po::parsed_options parsed_priorityfile =
925        fPriorityFile.empty() ? po::parsed_options(&opt_configfile) :
926        po::parse_config_file<char>(inpri, opt_configfile, !checkf);
927
928    // ------------------------ (8) --------------------------
929
930    if (getfiles.count("print-config") || getfiles.count("print-all"))
931    {
932        if (fPriorityFile.empty())
933            cout << "No configuration file by --config option specified." << endl;
934        else
935        {
936            cout << endl << "Parsed options from '" << fPriorityFile << "':" << endl;
937            PrintParsed(parsed_priorityfile);
938            cout << endl;
939        }
940    }
941
942    // ------------------------ (9) --------------------------
943
944    po::variables_map getdatabase;
945    po::store(parsed_commandline,  getdatabase);
946    po::store(parsed_priorityfile, getdatabase);
947    po::store(parsed_defaultfile,  getdatabase);
948
949    if (getdatabase.count("database") && !getdatabase.count("no-database"))
950    {
951        fDatabase = getdatabase["database"].as<string>();
952        cerr << "Retrieving configuration from '" << fDatabase << "'." << endl;
953    }
954
955    const bool checkdb = !getdatabase.count("dont-check-database") && !getdatabase.count("dont-check");
956
957    const po::parsed_options parsed_database =
958        fDatabase.empty() ? po::parsed_options(&opt_database) :
959        parse_database(fDatabase, opt_database, !checkdb);
960
961    // ------------------------ (10) -------------------------
962
963    if (getfiles.count("print-database") || getfiles.count("print-all"))
964    {
965        if (fDatabase.empty())
966            cout << "No database access requested." << endl;
967        else
968        {
969            cout << endl << "Options retrieved from '" << fDatabase << "':" << endl;
970            PrintParsed(parsed_database);
971            cout << endl;
972        }
973    }
974
975    // ------------------------ (11) -------------------------
976
977    const po::parsed_options parsed_environment = po::parse_environment(opt_environment, fNameMapper);
978
979    // ------------------------ (12) -------------------------
980
981    if (getfiles.count("print-environment"))
982    {
983        cout << "Parsed options from environment:" << endl;
984        PrintParsed(parsed_environment);
985        cout << endl;
986    }
987
988    // ------------------------ (13) -------------------------
989
990    po::variables_map result;
991    po::store(parsed_commandline,  result);
992    po::store(parsed_priorityfile, result);
993    po::store(parsed_database,     result);
994    po::store(parsed_defaultfile,  result);
995    po::store(parsed_environment,  result);
996    po::notify(result);
997
998    fVariables = result;
999
1000    // ------------------------ (14) -------------------------
1001
1002    const vector<string> unknown1 = collect_unrecognized(parsed_defaultfile.options,  po::exclude_positional);
1003    const vector<string> unknown2 = collect_unrecognized(parsed_priorityfile.options, po::exclude_positional);
1004
1005    fUnknownConfigfile.clear();
1006    fUnknownConfigfile.insert(fUnknownConfigfile.end(), unknown1.begin(), unknown1.end());
1007    fUnknownConfigfile.insert(fUnknownConfigfile.end(), unknown2.begin(), unknown2.end());
1008
1009    fUnknownCommandline = collect_unrecognized(parsed_commandline.options, po::exclude_positional);
1010    fUnknownEnvironment = collect_unrecognized(parsed_environment.options, po::exclude_positional);
1011    fUnknownDatabase    = collect_unrecognized(parsed_database.options, po::exclude_positional);
1012
1013    // ------------------------ (15) -------------------------
1014
1015    if (result.count("print-options"))
1016        PrintOptions();
1017
1018    if (result.count("print-unknown"))
1019        PrintUnknown();
1020
1021    return fVariables;
1022}
Note: See TracBrowser for help on using the repository browser.