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

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