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

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