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

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