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

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