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

Last change on this file since 10634 was 10634, checked in by tbretz, 9 years ago
Added unified text before printing Options.
File size: 33.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",    po_string(),              "This is option1")
95        ("option2",    po_int(22),               "This is option2")
96        ("option3,o",  po_double()->required(),  "This option is mandatory")
97        ("option4",    po_int(&opt),             "This is option 4")
98        ("option5",    po_strings(),             "A list of strings")
99        ("option6",    po_strings(),             "A list of strings")
100        ("option7",    po_strings(),             "A list of strings")
101        ;
102
103\endcode
104
105This will setup, e.g.,  the commandline option '<B>--option1 arg</B>' (which
106is identical to '<B>--option1=arg</B>'. Option 3 can also be expressed
107in a short form as '<B>-o arg</B>' or '<B>-o=arg</B>'. Option 2 defaults
108to 22 if no explicit value is given. Option 3 is mandatory and an exceptionb
109is thrown if not specified. Option 4 will, apart from the usual access to the
110option values, also store its value in the variable opt.
111
112The used functions po_*() are defined in configuration.h and are abbreviations.
113Generally speaking also other variable types are possible.
114
115If the options are displayed, e.g. by \b --help the corresponding section will
116by titled \e Section, also untitled sections are possible.
117
118If an option can be given more than once then a std::vector<type> can be used.
119Abbreviations po_ints(), po_doubles() and po_strings() are available.
120
121In addition to options introduced by a minus, so calles positional options
122can be given on the command line. To describe these options use
123
124\code
125
126    po::positional_options_description p;
127    p.add("option5", 2); // The first 2 positional options
128    p.add("option6", 3); // The next three positional options
129    // p.add("option7", -1); // All others, if wanted
130
131\endcode
132
133This assigns option-keys to the positional options (arguments) in the
134command-line. Note that the validity of the given commandline is checked.
135Hence, this way of defining the options makes sense.
136
137As needed options_descriptions can be grouped together
138
139\code
140
141    po::options_description config1("Section1");
142    po::options_description config2("Section2");
143
144    po::options_description configall;
145    configall.add(config1);
146    configall.add(config2);
147
148\endcode
149
150The member functions of Configurations allow to define for which option
151source these options are valid. The member functions are:
152
153\code
154
155    Configuration conf;
156
157    conf.AddOptionsCommandline(configall, true);
158    conf.AddOptionsConfigfile(config1, true);
159    conf.AddOptionsDatabase(config2, true);
160
161    // To enable the mapping of the position arguments call this
162    conf.SetArgumentPositions(p);
163
164\endcode
165
166If the second option is false, the options will not be displayed in any
167\b --help directive, but are available to the user. Each of the functions
168can be called more than once. If an option should be available from
169all kind of inputs AddOptions() can be used which will call all
170four other AddOptions() functions.
171
172A special case are the options from environment variables. Since you might
173want to use the same option-key for the command-line and the environment,
174a mapping is needed (e.g. from \b PATH to \b --path). This mapping
175can be implemented by a mapping function or by the build in mapping
176and be initialized like this:
177
178\code
179
180   conf.AddEnv("path", "PATH");
181
182\endcode
183
184or
185
186\code
187
188   const string name_mapper(const string str)
189   {
190      return str=="PATH" ? "path" : "";
191   }
192
193   conf.SetNameMapper(name_mapper);
194
195\endcode
196
197Assuming all the above is done in a function calles SetupConfiguration(),
198a simple program to demonstrate the power of the class could look like this:
199
200\code
201
202   int main(int argc, char **argv)
203   {
204       int opt;
205
206       Configuration conf(argv[0]);
207       SetupConfiguration(conf, opt);
208
209       po::variables_map vm;
210       try
211       {
212          vm = conf.Parse(argc, argv);
213       }
214       catch (std::exception &e)
215       {
216           po::multiple_occurrences *MO = dynamic_cast<po::multiple_occurrences*>(&e);
217           if (MO)
218               cout << "Error: " << e.what() << " of '" << MO->get_option_name() << "' option." << endl;
219           else
220               cout << "Error: " << e.what() << endl;
221           cout << endl;
222
223           return -1;
224       }
225
226       cout << "Opt1: " << conf.GetString("option1") << endl;
227       cout << "Opt2: " << conf.GetInt("option2") << endl;
228       cout << "Opt3: " << conf.GetDouble("option3") << endl;
229       cout << "Opt4: " << opt << endl;
230
231       return 0;
232   }
233
234\endcode
235
236Another possibility to access the result is the direct approach, for example:
237
238\code
239
240   vector<int>    i   = vm["option2"].as<int>();
241   vector<string> vec = vm["option6"].as<vector<string>>();
242
243\endcode
244
245Note that accessing an option which was not given will throw an exception.
246Therefor its availability should first be checked in one of the following
247ways:
248
249\code
250
251   bool has_option1 = vm.count("option1");
252   bool has_option2 = conf.Has("option2");
253
254\endcode
255
256@section Examples
257
258 - An example can be found in \ref argv.cc
259
260@todo
261
262 - Maybe we should remove the necessity to propagate argv[0] in the constructor?
263 - Add an option to the constructor to switch of database/file access
264
265*/
266// **************************************************************************
267#include "Configuration.h"
268
269#include <fstream>
270#include <iostream>
271#include <iomanip>
272
273#include <boost/bind.hpp>
274#include <boost/regex.hpp>
275//#include <boost/filesystem.hpp>
276#include <boost/program_options.hpp>
277
278#define HAS_SQL
279
280#ifdef HAS_SQL
281#include <mysql++/mysql++.h>
282#endif
283
284using namespace std;
285
286namespace style = boost::program_options::command_line_style;
287
288// --------------------------------------------------------------------------
289//
290//!  The purpose of this function is basically to connect to the database,
291//!  and retrieve all the options entries from the 'Configuration' table.
292//!
293//!  @param database
294//!      The URL of the database from which the configuration data is
295//!      retrieved. It should be given in the form
296//!          \li [user[:password]@]server.com[:port][/database]
297//!
298//!      with
299//!          - user:     user name (default is the current user)
300//!          - password: necessary if required by the database rights
301//!          - server:   the URL of the server (can be 'localhost')
302//!          - port:     the port to which to connect (usually obsolete)
303//!          - database: The name of the database containing the table
304//!
305//!  @param desc
306//!     A reference to the object with the description of the options
307//!     which should be retrieved.
308//!
309//!  @param allow_unregistered
310//!     If this is true also unregistered, i.e. options unknown to desc,
311//!     are returned. Otherwise an exception is thrown if such an option
312//!     was retrieved.
313//!
314//!  @return
315//!     Return an object of type basic_parsed_options containing all
316//!     the entries retrieved from the database. Options not found in
317//!     desc are flagged as unregistered.
318//!
319//!  @throws
320//!     Two types of exceptions are thrown
321//!        - It thows an unnamed exception if the options could not be
322//!          retrieved properly from the databse.
323//!        - If an option is not registered within the given descriptions
324//!          and \b allow_unregistered is \b false, an exception of type
325//!          \b  po::unknown_option is thrown.
326//!
327//!  @todo
328//!     - The exceptions handling should be improved.
329//!     - The final database layout is missing in the description
330//!     - Shell we allow options to be given more than once?
331//
332#ifdef HAS_SQL
333po::basic_parsed_options<char>
334    Configuration::parse_database(const string &database, const po::options_description& desc, bool allow_unregistered)
335{
336    //static const boost::regex expr("(([[:word:].-]+)(:(.+))?@)?([[:word:].-]+)(:([[:digit:]]+))?(/([[:word:].-]+))?");
337    static const boost::regex expr("(([[:word:].-]+)(:(.+))?@)?([[:word:].-]+)(:([[:digit:]]+))?(/([[:word:].-]+))");
338    // 2: user
339    // 4: pass
340    // 5: server
341    // 7: port
342    // 9: db
343
344    boost::smatch what;
345    if (!boost::regex_match(database, what, expr, boost::match_extra))
346    {
347        cout << "Couldn't parse '" << database << "'." << endl;
348        throw;
349    }
350
351    if (what.size()!=10)
352    {
353        cout << "Error parsing '" << database << "'." << endl;
354        throw;
355    }
356
357    const string user   = what[2];
358    const string passwd = what[4];
359    const string server = what[5];
360    const string db     = what[9];
361    const int port      = atoi(string(what[7]).c_str());
362
363    cout << "Connecting to '";
364    if (!user.empty())
365        cout << user << "@";
366    cout << server;
367    if (port)
368        cout << ":" << port;
369    if (!db.empty())
370        cout << "/" << db;
371    cout << "'" << endl;
372
373    mysqlpp::Connection conn(db.c_str(), server.c_str(), user.c_str(), passwd.c_str(), port);
374    if (!conn.connected())
375    {
376        cout << "MySQL connection error: " << conn.error() << endl;
377        throw;
378    }
379
380    // Retrieve a subset of the sample stock table set up by resetdb
381    // and display it.
382    // FIXME: What about a prefix?
383    mysqlpp::Query query = conn.query("select `Key`, Value from Configuration");
384
385    mysqlpp::StoreQueryResult res = query.store();
386    if (!res)
387    {
388        cout << "MySQL query failed: " << query.error() << endl;
389        throw;
390    }
391
392    set<string> allowed_options;
393
394    const vector<boost::shared_ptr<po::option_description>> &options = desc.options();
395    for (unsigned i=0; i<options.size(); ++i)
396    {
397        const po::option_description &d = *options[i];
398        if (d.long_name().empty())
399            boost::throw_exception(po::error("long name required for database"));
400
401        allowed_options.insert(d.long_name());
402    }
403
404    po::parsed_options result(&desc);
405
406    for (vector<mysqlpp::Row>::iterator v=res.begin(); v<res.end(); v++)
407    {
408        const string key = (*v)[0].c_str();
409        if (key.empty())  // key  == > Throw exception
410            continue;
411
412        // Check if we are allowed to accept unregistered options,
413        // i.e. options which are not in options_description &desc.
414        const bool unregistered = allowed_options.find(key)==allowed_options.end();
415        if (unregistered && allow_unregistered)
416            boost::throw_exception(po::unknown_option(key));
417
418        // Create a key/value-pair and store whether it is a
419        // registered option of not
420        po::option n;
421        n.string_key = key;
422        // This is now identical to file parsing. What if we want
423        // to concatenate options like on the command line?
424        n.value.clear();          // Fixme: composing?
425        n.value.push_back((*v)[1].c_str());
426        n.unregistered = unregistered;
427
428        // If any parsing will be done in the future...
429        //n.value().original_tokens.clear();
430        //n.value().original_tokens.push_back(name);
431        //n.value().original_tokens.push_back(value);
432
433        result.options.push_back(n);
434    }
435
436    cout << endl;
437
438    return result;
439}
440#else
441po::basic_parsed_options<char>
442    Configuration::parse_database(const string &, const po::options_description &desc, bool)
443{
444    return po::parsed_options(&desc);
445}
446#endif
447
448// --------------------------------------------------------------------------
449//
450//!
451//
452Configuration::Configuration(const string &prgname) : fName(prgname),
453fNameMapper(bind1st(mem_fun(&Configuration::DefaultMapper), this)),
454fPrintUsage(boost::bind(&Configuration::PrintUsage, this))
455{
456    po::options_description generic("Generic options");
457    generic.add_options()
458        ("help",                "Print available commandline options.")
459        ("help-environment",    "Print available environment variables.")
460        ("help-database",       "Print available options retreived from the database.")
461        ("help-config",         "Print available configuration file options.")
462        ("version,V",           "Print version information.")
463        ("print-all",           "Print all options as parsed from all the different sources.")
464        ("print",               "Print options as parsed from the commandline.")
465        ("print-default",       "Print options as parsed from default configuration file.")
466        ("print-database",      "Print options as retrieved from the database.")
467        ("print-config",        "Print options as parsed from the high priority configuration file.")
468        ("print-environment",   "Print options as parsed from the environment.")
469        ("print-unknown",       "Print unrecognized options.")
470        ("print-options",       "Print options as passed to program.")
471        ("dont-check",          "Do not check validity of options from files and database.")
472        ("dont-check-files",    "Do not check validity of options from files.")
473        ("dont-check-database", "Do not check validity of options from database.")
474        ;
475
476    po::options_description def_config;
477    def_config.add_options()
478        ("default",  var<string>(prgname+string(".rc")), "Default configuration file.")
479        ;
480
481    po::options_description config("Configuration options");
482    config.add_options()
483        ("config,C",    var<string>(), "Configuration file overwriting options retrieved from the database.")
484        ("database",    var<string>(), "Database link as in\n\t[user:[password]@][server][:port][/database]\nOverwrites options from the default configuration file.")
485        ("no-database",                "Suppress any access to the database even if a database URL was set.")
486        ;
487
488    fOptionsCommandline[kVisible].add(generic);
489    fOptionsCommandline[kVisible].add(config);
490    fOptionsCommandline[kVisible].add(def_config);
491    fOptionsConfigfile[kVisible].add(config);
492}
493
494// --------------------------------------------------------------------------
495//
496//!
497//
498void Configuration::PrintParsed(const po::parsed_options &parsed) const
499{
500    const vector< po::basic_option<char> >& options = parsed.options;
501
502    // .description -> Pointer to opt_commandline
503    // const std::vector< shared_ptr<option_description> >& options() const;
504
505    //const std::string& key(const std::string& option) const;
506    //const std::string& long_name() const;
507    //const std::string& description() const;
508    //shared_ptr<const value_semantic> semantic() const;
509
510    int maxlen = 0;
511    for (unsigned i=0; i<options.size(); ++i)
512    {
513        const po::basic_option<char> &opt = options[i];
514
515        if (opt.value.size()>0 && opt.string_key[0]!='-')
516            Max(maxlen, opt.string_key.length());
517    }
518
519    cout.setf(ios_base::left);
520
521    // =============> Implement prining of parsed options
522    for(unsigned i=0; i<options.size(); ++i)
523    {
524        const po::basic_option<char> &opt = options[i];
525
526        if (opt.value.size()==0 && !opt.string_key[0]=='-')
527            cout << "--";
528        cout << setw(maxlen) << opt.string_key;
529        if (opt.value.size()>0)
530            cout << " = " << opt.value[0];
531
532        //for (int j=0; j<options[i].value.size(); j++)
533        //    cout << "\t = " << options[i].value[j];
534
535        //cout << "/" << options[i].position_key;
536        //cout << "/" << options[i].original_tokens[0];
537        //cout << "/" << options[i].unregistered << endl;
538        if (opt.unregistered)
539            cout << "   # option unknown";
540        cout << endl;
541    }
542}
543
544// --------------------------------------------------------------------------
545//
546//!
547//
548void Configuration::PrintOptions()
549{
550    cout << "Options propagated to program:" << endl;
551
552    int maxlen = 0;
553    for (map<string,po::variable_value>::iterator m=fVariables.begin();
554         m!=fVariables.end(); m++)
555        Max(maxlen, m->first.length());
556
557    cout.setf(ios_base::left);
558
559    // =============> Implement prining of options in use
560    for (map<string,po::variable_value>::iterator m=fVariables.begin();
561         m!=fVariables.end(); m++)
562    {
563        cout << setw(maxlen) << m->first << " = ";
564
565        const po::variable_value &v = m->second;
566
567        if (v.value().type()==typeid(bool))
568            cout << (v.as<bool>()?"true":"false") << "   # bool";
569
570        if (v.value().type()==typeid(string))
571            cout << v.as<string>() << "   # string";
572
573        if (v.value().type()==typeid(int))
574            cout << v.as<int>() << "   # int";
575
576        if (v.value().type()==typeid(double))
577            cout << v.as<double>() << "   # double";
578
579        if (v.value().type()==typeid(float))
580            cout << v.as<float>() << "   # float";
581
582        if (v.value().type()==typeid(vector<string>))
583        {
584            vector<string> vec = v.as<vector<string>>();
585            for (vector<string>::iterator s=vec.begin(); s<vec.end(); s++)
586                cout << *s << " ";
587            cout << "   # strings";
588        }
589        if (v.value().type()==typeid(vector<double>))
590        {
591            vector<double> vec = v.as<vector<double>>();
592            for (vector<double>::iterator s=vec.begin(); s<vec.end(); s++)
593                cout << *s << " ";
594            cout << "   # doubles";
595        }
596
597        if (v.defaulted())
598            cout << "   # default value";
599        if (v.empty())
600            cout << "   # empty value";
601
602        cout << endl;
603    }
604
605    cout << endl;
606}
607
608// --------------------------------------------------------------------------
609//
610//!
611//
612void Configuration::PrintUnknown(vector<string> &vec, int steps)
613{
614    for (vector<string>::iterator v=vec.begin(); v<vec.end(); v+=steps)
615        cout << " " << *v << endl;
616    cout << endl;
617}
618
619// --------------------------------------------------------------------------
620//
621//!
622//
623void Configuration::PrintUnknown()
624{
625    if (fUnknownCommandline.size())
626    {
627        cout << "Unknown commandline options:" << endl;
628        PrintUnknown(fUnknownCommandline);
629    }
630
631    if (fUnknownConfigfile.size())
632    {
633        cout << "Unknown options in configfile:" << endl;
634        PrintUnknown(fUnknownConfigfile, 2);
635    }
636
637    if (fUnknownEnvironment.size())
638    {
639        cout << "Unknown environment variables:" << endl;
640        PrintUnknown(fUnknownEnvironment);
641    }
642
643    if (fUnknownDatabase.size())
644    {
645        cout << "Unknown database entry:" << endl;
646        PrintUnknown(fUnknownDatabase);
647    }
648}
649
650// --------------------------------------------------------------------------
651//
652//!
653//
654void Configuration::AddOptionsCommandline(const po::options_description &cl, bool visible)
655{
656    fOptionsCommandline[visible].add(cl);
657}
658
659// --------------------------------------------------------------------------
660//
661//!
662//
663void Configuration::AddOptionsConfigfile(const po::options_description &cf, bool visible)
664{
665    fOptionsConfigfile[visible].add(cf);
666}
667
668// --------------------------------------------------------------------------
669//
670//!
671//
672void Configuration::AddOptionsEnvironment(const po::options_description &env, bool visible)
673{
674    fOptionsEnvironment[visible].add(env);
675}
676
677// --------------------------------------------------------------------------
678//
679//!
680//
681void Configuration::AddOptionsDatabase(const po::options_description &db, bool visible)
682{
683    fOptionsDatabase[visible].add(db);
684}
685
686// --------------------------------------------------------------------------
687//
688//!
689//
690void Configuration::SetArgumentPositions(const po::positional_options_description &desc)
691{
692    fArgumentPositions = desc;
693}
694
695// --------------------------------------------------------------------------
696//
697//!
698//
699void Configuration::SetNameMapper(const boost::function<string(string)> &func)
700{
701    fNameMapper = func;
702}
703
704void Configuration::SetNameMapper()
705{
706    fNameMapper = bind1st(mem_fun(&Configuration::DefaultMapper), this);
707}
708
709void Configuration::SetPrintUsage(const boost::function<void(void)> &func)
710{
711    fPrintUsage = func;
712}
713
714void Configuration::SetPrintUsage()
715{
716    fPrintUsage = boost::bind(&Configuration::PrintUsage, this);
717}
718
719// --------------------------------------------------------------------------
720//
721//!
722//! The idea of the Parse() memeber-function is to parse the command-line,
723//! the configuration files, the databse and the environment and return
724//! a proper combined result.
725//!
726//! In details the following actions are performed in the given order:
727//!
728//!  - (0)  Init local variables with the list of options described by the
729//!         data members.
730//!  - (1)  Reset the data members fPriorityFile, fDefaultFile, fDatabase
731//!  - (2)  Parse the command line
732//!  - (3)  Check for \b --help* command-line options and performe
733//!         corresponding action
734//!  - (4)  Check for \b --print and \b --print-all and perform corresponding
735//!         action
736//!  - (5)  Read and parse the default configuration file, which is either
737//!         given by the default name or the \b --default command-line
738//!         option. The default name is compiled from the argument
739//!         given to the constructor and ".rc".  If the file-name is
740//!         identical to the default (no command-line option given)
741//!         a missing configuration file is no error. Depending on
742//!         the \b --dont-check and \b --dont-check-files options,
743//!         unrecognized options in the file throw an exception or not.
744//!  - (6)  Check for \b --print-default and \b --print-all and perform
745//!         corresponding action
746//!  - (7)  Read and parse the priority configuration file, which must be given
747//!         by the \b --config or \b -C command-line option or a
748//!         corresponding entry in the default-configuration file.
749//!         If an option on the command-line and the in the configuration
750//!         file exists, the command-line option has priority.
751//!         If none is given, no priority file is read. Depending on
752//!         the \b --dont-check and \b --dont-check-files options,
753//!         unrecognized options in the file throw an exception or not.
754//!  - (8)  Check for \b --print-config and \b --print-all and perform
755//!         corresponding action
756//!  - (9)  Retrieve options from the database according to the
757//!         options \b --database and \b --no-database. Note that
758//!         options given on the command-line have highest priority.
759//!         The second priority is the priority-configuration file.
760//!         The options from the default configuration-file have
761//!         lowest priority.
762//!  - (10) Check for \b --print-database and \b --print-all and perform
763//!         corresponding action
764//!  - (11)  Parse the environment options.
765//!  - (12) Check for \b --print-environment and \b --print-all and perform
766//!         corresponding action
767//!  - (13) Compile the final result. The priority of the options is (in
768//!         decreasing order): command-line options, options from the
769//!         priority configuration file, options from the database,
770//!         options from the default configuration-file and options
771//!         from the environment.
772//!  - (14) Finally all options which were found and flagged as unrecognized,
773//!         because they are not in the user-defined list of described
774//!         options, are collected and stored in the corresponding
775//!         data-members.
776//!  - (15) Before the function returns it check for \b --print-options
777//!         and \b --print-unknown and performs the corresponding actions.
778//!
779//!
780//! @param argc,argv
781//!    arguments passed to <B>main(int argc, char **argv)</B>
782//!
783//! @returns
784//!    A reference to the list with the resulting options with their
785//!    values.
786//!
787//! @todo
788//!    - describe the exceptions
789//!    - describe what happens in a more general way
790//!    - print a waring when no default coonfig file is read
791//!    - proper handling and error messages if files not available
792//
793const po::variables_map &Configuration::Parse(int argc, const char **argv)
794{
795    const po::positional_options_description &opt_positional = fArgumentPositions;
796
797    // ------------------------ (0) --------------------------
798
799    po::options_description opt_commandline;
800    po::options_description opt_configfile;
801    po::options_description opt_environment;
802    po::options_description opt_database;
803
804    for (int i=0; i<2; i++)
805    {
806        opt_commandline.add(fOptionsCommandline[i]);
807        opt_configfile.add(fOptionsConfigfile[i]);
808        opt_environment.add(fOptionsEnvironment[i]);
809        opt_database.add(fOptionsDatabase[i]);
810    }
811
812    // ------------------------ (1) --------------------------
813
814    fPriorityFile = "";
815    fDefaultFile  = "";
816    fDatabase     = "";
817
818    // ------------------------ (2) --------------------------
819
820    po::command_line_parser parser(argc, const_cast<char**>(argv));
821    parser.options(opt_commandline);
822    parser.positional(opt_positional);
823    parser.style(style::unix_style&~style::allow_guessing);
824    //parser.allow_unregistered();
825
826    const po::parsed_options parsed_commandline = parser.run();
827
828    // ------------------------ (3) --------------------------
829
830    po::variables_map getfiles;
831    po::store(parsed_commandline, getfiles);
832
833    if (getfiles.count("help"))
834    {
835        fPrintUsage();
836        cout <<
837            "Options:\n"
838            "The following describes the available commandline options. "
839            "For further details on how command line option are parsed "
840            "and in which order which configuration sources are accessed "
841            "please refer to the class reference of the Configuration class." << endl;
842        cout << fOptionsCommandline[kVisible] << endl;
843    }
844    if (getfiles.count("help-config"))
845        cout << fOptionsConfigfile[kVisible] << endl;
846    if (getfiles.count("help-env"))
847        cout << fOptionsEnvironment[kVisible] << endl;
848    if (getfiles.count("help-database"))
849        cout << fOptionsDatabase[kVisible] << endl;
850
851    // ------------------------ (4) --------------------------
852
853    if (getfiles.count("print") || getfiles.count("print-all"))
854    {
855        cout << endl << "Parsed commandline options:" << endl;
856        PrintParsed(parsed_commandline);
857        cout << endl;
858    }
859
860    // ------------------------ (5) --------------------------
861
862    // Get default file from command line
863    if (getfiles.count("default"))
864    {
865        fDefaultFile = getfiles["default"].as<string>();
866        cerr << "Reading configuration from '" << fDefaultFile << "'." << endl;
867    }
868
869    const bool checkf    = !getfiles.count("dont-check-files") && !getfiles.count("dont-check");
870    const bool defaulted = getfiles.count("default") && getfiles["default"].defaulted();
871    //const bool exists    = boost::filesystem::exists(fDefaultFile);
872
873    ifstream indef(fDefaultFile.c_str());
874    // ===> FIXME: Proper handling of missing file or wrong file name
875    const po::parsed_options parsed_defaultfile =
876        !indef && defaulted ?
877        po::parsed_options(&opt_configfile) :
878        po::parse_config_file<char>(indef, opt_configfile, !checkf);
879
880    // ------------------------ (6) --------------------------
881
882    if (getfiles.count("print-default") || getfiles.count("print-all"))
883    {
884        if (!indef && defaulted)
885            cout << "No configuration file by --default option specified." << endl;
886        else
887        {
888            cout << endl << "Parsed options from '" << fDefaultFile << "':" << endl;
889            PrintParsed(parsed_defaultfile);
890            cout << endl;
891        }
892    }
893
894    po::store(parsed_defaultfile, getfiles);
895
896    // ------------------------ (7) --------------------------
897
898    // Get priority from commandline(1), defaultfile(2)
899    if (getfiles.count("config"))
900    {
901        fPriorityFile = getfiles["config"].as<string>();
902        cerr << "Retrieved option from '" << fPriorityFile << "'." << endl;
903    }
904
905    ifstream inpri(fPriorityFile.c_str());
906    // ===> FIXME: Proper handling of missing file or wrong file name
907    const po::parsed_options parsed_priorityfile =
908        fPriorityFile.empty() ? po::parsed_options(&opt_configfile) :
909        po::parse_config_file<char>(inpri, opt_configfile, !checkf);
910
911    // ------------------------ (8) --------------------------
912
913    if (getfiles.count("print-config") || getfiles.count("print-all"))
914    {
915        if (fPriorityFile.empty())
916            cout << "No configuration file by --config option specified." << endl;
917        else
918        {
919            cout << endl << "Parsed options from '" << fPriorityFile << "':" << endl;
920            PrintParsed(parsed_priorityfile);
921            cout << endl;
922        }
923    }
924
925    // ------------------------ (9) --------------------------
926
927    po::variables_map getdatabase;
928    po::store(parsed_commandline,  getdatabase);
929    po::store(parsed_priorityfile, getdatabase);
930    po::store(parsed_defaultfile,  getdatabase);
931
932    if (getdatabase.count("database") && !getdatabase.count("no-database"))
933    {
934        fDatabase = getdatabase["database"].as<string>();
935        cerr << "Retrieving configuration from '" << fDatabase << "'." << endl;
936    }
937
938    const bool checkdb = !getdatabase.count("dont-check-database") && !getdatabase.count("dont-check");
939
940    const po::parsed_options parsed_database =
941        fDatabase.empty() ? po::parsed_options(&opt_database) :
942        parse_database(fDatabase, opt_database, !checkdb);
943
944    // ------------------------ (10) -------------------------
945
946    if (getfiles.count("print-database") || getfiles.count("print-all"))
947    {
948        if (fDatabase.empty())
949            cout << "No database access requested." << endl;
950        else
951        {
952            cout << endl << "Options retrieved from '" << fDatabase << "':" << endl;
953            PrintParsed(parsed_database);
954            cout << endl;
955        }
956    }
957
958    // ------------------------ (11) -------------------------
959
960    const po::parsed_options parsed_environment = po::parse_environment(opt_environment, fNameMapper);
961
962    // ------------------------ (12) -------------------------
963
964    if (getfiles.count("print-environment"))
965    {
966        cout << "Parsed options from environment:" << endl;
967        PrintParsed(parsed_environment);
968        cout << endl;
969    }
970
971    // ------------------------ (13) -------------------------
972
973    po::variables_map result;
974    po::store(parsed_commandline,  result);
975    po::store(parsed_priorityfile, result);
976    po::store(parsed_database,     result);
977    po::store(parsed_defaultfile,  result);
978    po::store(parsed_environment,  result);
979    po::notify(result);
980
981    fVariables = result;
982
983    // ------------------------ (14) -------------------------
984
985    const vector<string> unknown1 = collect_unrecognized(parsed_defaultfile.options,  po::exclude_positional);
986    const vector<string> unknown2 = collect_unrecognized(parsed_priorityfile.options, po::exclude_positional);
987
988    fUnknownConfigfile.clear();
989    fUnknownConfigfile.insert(fUnknownConfigfile.end(), unknown1.begin(), unknown1.end());
990    fUnknownConfigfile.insert(fUnknownConfigfile.end(), unknown2.begin(), unknown2.end());
991
992    fUnknownCommandline = collect_unrecognized(parsed_commandline.options, po::exclude_positional);
993    fUnknownEnvironment = collect_unrecognized(parsed_environment.options, po::exclude_positional);
994    fUnknownDatabase    = collect_unrecognized(parsed_database.options, po::exclude_positional);
995
996    // ------------------------ (15) -------------------------
997
998    if (result.count("print-options"))
999        PrintOptions();
1000
1001    if (result.count("print-unknown"))
1002        PrintUnknown();
1003
1004    return fVariables;
1005}
Note: See TracBrowser for help on using the repository browser.