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

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