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

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