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

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