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

Last change on this file since 11064 was 11064, checked in by tbretz, 13 years ago
Fixed a bug in UnLibToolize with short filenames.
File size: 38.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", 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 /* throws exceptions
392 if (!conn.connected())
393 {
394 cout << "MySQL connection error: " << conn.error() << endl;
395 throw;
396 }*/
397
398 // Retrieve a subset of the sample stock table set up by resetdb
399 // and display it.
400 // FIXME: What about a prefix?
401 const mysqlpp::StoreQueryResult res = conn.query("select `Key`, Value from Configuration").store();
402 /* throws exceptions
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>::const_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(UnLibToolize(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 ("version,V", "Print version information.")
476 ("help", "Print available commandline options.")
477 ("help-environment", "Print available environment variables.")
478 ("help-database", "Print available options retreived from the database.")
479 ("help-config", "Print available configuration file options.")
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>(fName+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
736void Configuration::SetPrintVersion(const boost::function<void(const string&)> &func)
737{
738 fPrintVersion = func;
739}
740
741void Configuration::SetPrintVersion()
742{
743 fPrintVersion = boost::function<void(const string&)>();
744}
745
746// --------------------------------------------------------------------------
747//
748//!
749//! The idea of the Parse() memeber-function is to parse the command-line,
750//! the configuration files, the databse and the environment and return
751//! a proper combined result.
752//!
753//! In details the following actions are performed in the given order:
754//!
755//! - (0) Init local variables with the list of options described by the
756//! data members.
757//! - (1) Reset the data members fPriorityFile, fDefaultFile, fDatabase
758//! - (2) Parse the command line
759//! - (3) Check for \b --help* command-line options and performe
760//! corresponding action
761//! - (4) Check for \b --print and \b --print-all and perform corresponding
762//! action
763//! - (5) Read and parse the default configuration file, which is either
764//! given by the default name or the \b --default command-line
765//! option. The default name is compiled from the argument
766//! given to the constructor and ".rc". If the file-name is
767//! identical to the default (no command-line option given)
768//! a missing configuration file is no error. 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//! - (6) Check for \b --print-default and \b --print-all and perform
772//! corresponding action
773//! - (7) Read and parse the priority configuration file, which must be given
774//! by the \b --config or \b -C command-line option or a
775//! corresponding entry in the default-configuration file.
776//! If an option on the command-line and the in the configuration
777//! file exists, the command-line option has priority.
778//! If none is given, no priority file is read. Depending on
779//! the \b --dont-check and \b --dont-check-files options,
780//! unrecognized options in the file throw an exception or not.
781//! - (8) Check for \b --print-config and \b --print-all and perform
782//! corresponding action
783//! - (9) Retrieve options from the database according to the
784//! options \b --database and \b --no-database. Note that
785//! options given on the command-line have highest priority.
786//! The second priority is the priority-configuration file.
787//! The options from the default configuration-file have
788//! lowest priority.
789//! - (10) Check for \b --print-database and \b --print-all and perform
790//! corresponding action
791//! - (11) Parse the environment options.
792//! - (12) Check for \b --print-environment and \b --print-all and perform
793//! corresponding action
794//! - (13) Compile the final result. The priority of the options is (in
795//! decreasing order): command-line options, options from the
796//! priority configuration file, options from the database,
797//! options from the default configuration-file and options
798//! from the environment.
799//! - (14) Finally all options which were found and flagged as unrecognized,
800//! because they are not in the user-defined list of described
801//! options, are collected and stored in the corresponding
802//! data-members.
803//! - (15) Before the function returns it check for \b --print-options
804//! and \b --print-unknown and performs the corresponding actions.
805//!
806//!
807//! @param argc,argv
808//! arguments passed to <B>main(int argc, char **argv)</B>
809//!
810//! @returns
811//! A reference to the list with the resulting options with their
812//! values.
813//!
814//! @todo
815//! - describe the exceptions
816//! - describe what happens in a more general way
817//! - print a waring when no default coonfig file is read
818//! - proper handling and error messages if files not available
819//
820const po::variables_map &Configuration::Parse(int argc, const char **argv)
821{
822 const po::positional_options_description &opt_positional = fArgumentPositions;
823
824 // ------------------------ (0) --------------------------
825
826 po::options_description opt_commandline;
827 po::options_description opt_configfile;
828 po::options_description opt_environment;
829 po::options_description opt_database;
830
831 for (int i=0; i<2; i++)
832 {
833 opt_commandline.add(fOptionsCommandline[i]);
834 opt_configfile.add(fOptionsConfigfile[i]);
835 opt_environment.add(fOptionsEnvironment[i]);
836 opt_database.add(fOptionsDatabase[i]);
837 }
838
839 // ------------------------ (1) --------------------------
840
841 fPriorityFile = "";
842 fDefaultFile = "";
843 fDatabase = "";
844
845 // ------------------------ (2) --------------------------
846
847 po::command_line_parser parser(argc, const_cast<char**>(argv));
848 parser.options(opt_commandline);
849 parser.positional(opt_positional);
850 parser.style(style::unix_style&~style::allow_guessing);
851 //parser.allow_unregistered();
852
853 const po::parsed_options parsed_commandline = parser.run();
854
855 // ------------------------ (3) --------------------------
856
857 po::variables_map getfiles;
858 po::store(parsed_commandline, getfiles);
859
860 if (getfiles.count("version"))
861 PrintVersion();
862 if (getfiles.count("help"))
863 {
864 fPrintUsage();
865 cout <<
866 "Options:\n"
867 "The following describes the available commandline options. "
868 "For further details on how command line option are parsed "
869 "and in which order which configuration sources are accessed "
870 "please refer to the class reference of the Configuration class." << endl;
871 cout << fOptionsCommandline[kVisible] << endl;
872 }
873 if (getfiles.count("help-config"))
874 cout << fOptionsConfigfile[kVisible] << endl;
875 if (getfiles.count("help-env"))
876 cout << fOptionsEnvironment[kVisible] << endl;
877 if (getfiles.count("help-database"))
878 cout << fOptionsDatabase[kVisible] << endl;
879
880 // ------------------------ (4) --------------------------
881
882 if (getfiles.count("print") || getfiles.count("print-all"))
883 {
884 cout << endl << "Parsed commandline options:" << endl;
885 PrintParsed(parsed_commandline);
886 cout << endl;
887 }
888
889 // ------------------------ (5) --------------------------
890
891 // Get default file from command line
892 if (getfiles.count("default"))
893 {
894 fDefaultFile = getfiles["default"].as<string>();
895 cerr << "Reading configuration from '" << fDefaultFile << "'." << endl;
896 }
897
898 const bool checkf = !getfiles.count("dont-check-files") && !getfiles.count("dont-check");
899 const bool defaulted = getfiles.count("default") && getfiles["default"].defaulted();
900 //const bool exists = boost::filesystem::exists(fDefaultFile);
901
902 ifstream indef(fDefaultFile.c_str());
903 // ===> FIXME: Proper handling of missing file or wrong file name
904 const po::parsed_options parsed_defaultfile =
905 !indef && defaulted ?
906 po::parsed_options(&opt_configfile) :
907 po::parse_config_file<char>(indef, opt_configfile, !checkf);
908
909 // ------------------------ (6) --------------------------
910
911 if (getfiles.count("print-default") || getfiles.count("print-all"))
912 {
913 if (!indef && defaulted)
914 cout << "No configuration file by --default option specified." << endl;
915 else
916 {
917 cout << endl << "Parsed options from '" << fDefaultFile << "':" << endl;
918 PrintParsed(parsed_defaultfile);
919 cout << endl;
920 }
921 }
922
923 po::store(parsed_defaultfile, getfiles);
924
925 // ------------------------ (7) --------------------------
926
927 // Get priority from commandline(1), defaultfile(2)
928 if (getfiles.count("config"))
929 {
930 fPriorityFile = getfiles["config"].as<string>();
931 cerr << "Retrieved option from '" << fPriorityFile << "'." << endl;
932 }
933
934 ifstream inpri(fPriorityFile.c_str());
935 // ===> FIXME: Proper handling of missing file or wrong file name
936 const po::parsed_options parsed_priorityfile =
937 fPriorityFile.empty() ? po::parsed_options(&opt_configfile) :
938 po::parse_config_file<char>(inpri, opt_configfile, !checkf);
939
940 // ------------------------ (8) --------------------------
941
942 if (getfiles.count("print-config") || getfiles.count("print-all"))
943 {
944 if (fPriorityFile.empty())
945 cout << "No configuration file by --config option specified." << endl;
946 else
947 {
948 cout << endl << "Parsed options from '" << fPriorityFile << "':" << endl;
949 PrintParsed(parsed_priorityfile);
950 cout << endl;
951 }
952 }
953
954 // ------------------------ (9) --------------------------
955
956 po::variables_map getdatabase;
957 po::store(parsed_commandline, getdatabase);
958 po::store(parsed_priorityfile, getdatabase);
959 po::store(parsed_defaultfile, getdatabase);
960
961 if (getdatabase.count("database") && !getdatabase.count("no-database"))
962 {
963 fDatabase = getdatabase["database"].as<string>();
964 cerr << "Retrieving configuration from '" << fDatabase << "'." << endl;
965 }
966
967 const bool checkdb = !getdatabase.count("dont-check-database") && !getdatabase.count("dont-check");
968
969 const po::parsed_options parsed_database =
970 fDatabase.empty() ? po::parsed_options(&opt_database) :
971 parse_database(fDatabase, opt_database, !checkdb);
972
973 // ------------------------ (10) -------------------------
974
975 if (getfiles.count("print-database") || getfiles.count("print-all"))
976 {
977 if (fDatabase.empty())
978 cout << "No database access requested." << endl;
979 else
980 {
981 cout << endl << "Options retrieved from '" << fDatabase << "':" << endl;
982 PrintParsed(parsed_database);
983 cout << endl;
984 }
985 }
986
987 // ------------------------ (11) -------------------------
988
989 const po::parsed_options parsed_environment = po::parse_environment(opt_environment, fNameMapper);
990
991 // ------------------------ (12) -------------------------
992
993 if (getfiles.count("print-environment"))
994 {
995 cout << "Parsed options from environment:" << endl;
996 PrintParsed(parsed_environment);
997 cout << endl;
998 }
999
1000 // ------------------------ (13) -------------------------
1001 po::variables_map result;
1002 po::store(parsed_commandline, result);
1003 po::store(parsed_priorityfile, result);
1004 po::store(parsed_database, result);
1005 po::store(parsed_defaultfile, result);
1006 po::store(parsed_environment, result);
1007 po::notify(result);
1008
1009 fVariables = result;
1010
1011 // ------------------------ (14) -------------------------
1012
1013 const vector<string> unknown1 = collect_unrecognized(parsed_defaultfile.options, po::exclude_positional);
1014 const vector<string> unknown2 = collect_unrecognized(parsed_priorityfile.options, po::exclude_positional);
1015
1016 fUnknownConfigfile.clear();
1017 fUnknownConfigfile.insert(fUnknownConfigfile.end(), unknown1.begin(), unknown1.end());
1018 fUnknownConfigfile.insert(fUnknownConfigfile.end(), unknown2.begin(), unknown2.end());
1019
1020 fUnknownCommandline = collect_unrecognized(parsed_commandline.options, po::exclude_positional);
1021 fUnknownEnvironment = collect_unrecognized(parsed_environment.options, po::exclude_positional);
1022 fUnknownDatabase = collect_unrecognized(parsed_database.options, po::exclude_positional);
1023
1024 // ------------------------ (15) -------------------------
1025
1026 if (result.count("print-options"))
1027 PrintOptions();
1028
1029 if (result.count("print-unknown"))
1030 PrintUnknown();
1031
1032 return fVariables;
1033}
1034
1035// --------------------------------------------------------------------------
1036//
1037//! Removes /.libs/lt- from a path or just lt- from the filename.
1038//!
1039//! @param src
1040//! input path with filename
1041//! @returns
1042//! path cleaned from libtool extensions
1043//!
1044string Configuration::UnLibToolize(const string &src) const
1045{
1046 const boost::filesystem::path path(src);
1047
1048 string pname = path.parent_path().string();
1049 string fname = path.filename();
1050
1051 if (fname.substr(0, 3)=="lt-")
1052 fname = fname.substr(3);
1053
1054 if (pname.empty() || pname==".libs")
1055 return fname;
1056
1057 if (pname.length()>=6)
1058 {
1059 const size_t pos = pname.length()-6;
1060 if (pname.substr(pos)=="/.libs")
1061 pname = pname.substr(0, pos);
1062 }
1063
1064 return pname+'/'+fname;
1065}
1066
1067// --------------------------------------------------------------------------
1068//
1069//! Print version information about the program and package.
1070//!
1071//! The program name is taken from fName. If a leading "lt-" is found,
1072//! it is removed. This is useful if the program was build and run
1073//! using libtool.
1074//!
1075//! The package name is taken from the define PACKAGE_STRING. If it is
1076//! not defined (like automatically done by autoconf) no package information
1077//! is printed. The same is true for PACKAGE_URL and PACKAGE_BUGREPORT.
1078//!
1079//! From help2man:
1080//!
1081//! The first line of the --version information is assumed to be in one
1082//! of the following formats:
1083//!
1084//! \verbatim
1085//! - <version>
1086//! - <program> <version>
1087//! - {GNU,Free} <program> <version>
1088//! - <program> ({GNU,Free} <package>) <version>
1089//! - <program> - {GNU,Free} <package> <version>
1090//! \endverbatim
1091//!
1092//! and separated from any copyright/author details by a blank line.
1093//!
1094//! Handle multi-line bug reporting sections of the form:
1095//!
1096//! \verbatim
1097//! - Report <program> bugs to <addr>
1098//! - GNU <package> home page: <url>
1099//! - ...
1100//! \endverbatim
1101//!
1102//! @param name
1103//! name of the program (usually argv[0]). A possible leading "lt-"
1104//! is removed.
1105//!
1106void Configuration::PrintVersion() const
1107{
1108#ifndef PACKAGE_STRING
1109#define PACKAGE_STRING ""
1110#endif
1111
1112#ifndef PACKAGE_URL
1113#define PACKAGE_URL ""
1114#endif
1115
1116#ifndef PACKAGE_BUGREPORT
1117#define PACKAGE_BUGREPORT ""
1118#endif
1119
1120 if (!fPrintVersion.empty())
1121 {
1122 fPrintVersion(fName);
1123 return;
1124 }
1125
1126 const std::string n = boost::filesystem::path(fName).filename();
1127
1128 const string name = PACKAGE_STRING;
1129 const string bugs = PACKAGE_BUGREPORT;
1130 const string url = PACKAGE_URL;
1131
1132 cout << n;
1133 if (!name.empty())
1134 cout << " - " << name;
1135 cout <<
1136 "\n\n"
1137 "Written by Thomas Bretz et al.\n"
1138 "\n";
1139 if (!bugs.empty())
1140 cout << "Report bugs to <" << bugs << ">\n";
1141 if (!url.empty())
1142 cout << "Home page: " << url << "\n";
1143 cout <<
1144 "\n"
1145 "Copyright (C) 2011 by the FACT Collaboration.\n"
1146 "This is free software; see the source for copying conditions.\n"
1147 << std::endl;
1148}
Note: See TracBrowser for help on using the repository browser.