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

Last change on this file since 12459 was 12335, checked in by tbretz, 13 years ago
Improved class reference.
File size: 47.6 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) Options from the global configuration-file (constrctor path + \b fact++.rc)
34 - (6) Environment variables
35
36Which options are accepted is defined by the program. To get a list
37of all command-line option use \b --help. This also lists all other
38available options to list for exmaple the options available in the
39configuration files or from the databse. In addition some default options
40are available which allow to debug parsing of the options, by either printing
41the options retrieval or after parsing.
42
43Options in the configuration files must be given in the form
44
45 - key = value
46
47which is equivalent to the command-line option <B>--key=value</B>.
48
49If there are sections in the configuration file like
50
51\code
52
53 [section1]
54 key = value
55
56\endcode
57
58the key is transformed into <B>section1.key</B> (which would be equivalent
59to <B>--section1.key</B>)
60
61@attention
62In principle it is possible that an exception is thrown before options
63like \b --help are properly parsed and evaluated. In this case it is
64necessary to first resolve the problem. Usually, this mean that there
65is a design flaw in the program rather than a mistake of usage.
66
67For more details on the order in which configuration is read,
68check Configuration::Parse. For more details on the parsing itself
69see the documentation of boost::program_options.
70
71
72
73
74@section API For the programmer
75
76The Configuration class heavily uses the
77<A HREF="http://www.boost.org"><B>C++ boost library</B></A>
78and makes heavy use of the
79<A HREF="http://www.boost.org/doc/libs/release/doc/html/program_options.html">
80<B>boost::program_options</B></A>
81
82The databse access is based on the
83<A HREF="http://tangentsoft.net/mysql++/"><B>MySQL++ library</B></A>.
84
85The basic idea is to have an easy to use, but powerfull setup. The setup on
86all options is based on a special syntax of options_description. Here is an
87example:
88
89\code
90
91 int opt = 0;
92
93 po::options_description config("Section");
94 config.add_options()
95 ("option1", var<string>(), "This is option1")
96 ("option2", var<int>(22), "This is option2")
97 ("option3,o", var<double>->required(), "This option is mandatory")
98 ("option4", var<int>(&opt), "This is option 4")
99 ("option5", vars<string>(), "A list of strings")
100 ("option6", vars<string>(), "A list of strings")
101 ("option7", vars<string>, "A list of strings")
102 ("option8", var<string>()->implicit_value("val"), "Just a string")
103 ("option9", var<string>()->default_value("def"), "Just a string")
104 ("optionA", var<string>("def"), "Just a string")
105 ("bool", po_bool(), "A special switch")
106 ;
107
108\endcode
109
110This will setup, e.g., the commandline option '<B>--option1 arg</B>' (which
111is identical to '<B>--option1=arg</B>'. Option 3 can also be expressed
112in a short form as '<B>-o arg</B>' or '<B>-o=arg</B>'. Option 2 defaults
113to 22 if no explicit value is given. Option 3 is mandatory and an exceptionb
114is thrown if not specified. Option 4 will, apart from the usual access to the
115option values, also store its value in the variable opt.
116
117The used functions po_*() are defined in configuration.h and are abbreviations.
118Generally speaking also other variable types are possible.
119
120If the options are displayed, e.g. by \b --help the corresponding section will
121by titled \e Section, also untitled sections are possible.
122
123If an option can be given more than once then a std::vector<type> can be used.
124Abbreviations po_ints(), po_doubles() and po_strings() are available.
125
126There are several ways to define the behaviour of the options. In the
127example above Parse will throw an exception if the "--option3" or "-o"
128option is not given. "option9" will evaluate to "def" if it is not
129given on the command line. The syntax of "optionA" is just an
130abbreviation. "option8" will evaluate to "val" if just "--option5" but
131no argument is given. Note, that these modifiers can be concatenated.
132
133A special type po_bool() is provided which is an abbreviation of
134var<bool>()->implicit_value(true)->default_value(false). In
135contradiction to po_switch() this allows to set a true and
136false value in the setup file.
137
138In addition to options introduced by a minus or double minus, so called
139positional options can be given on the command line. To describe these
140options use
141
142\code
143
144 po::positional_options_description p;
145 p.add("option5", 2); // The first 2 positional options
146 p.add("option6", 3); // The next three positional options
147 // p.add("option7", -1); // All others, if wanted
148
149\endcode
150
151This assigns option-keys to the positional options (arguments) in the
152command-line. Note that the validity of the given commandline is checked.
153Hence, this way of defining the options makes sense.
154
155As needed options_descriptions can be grouped together
156
157\code
158
159 po::options_description config1("Section1");
160 po::options_description config2("Section2");
161
162 po::options_description configall;
163 configall.add(config1);
164 configall.add(config2);
165
166\endcode
167
168The member functions of Configurations allow to define for which option
169source these options are valid. The member functions are:
170
171\code
172
173 Configuration conf;
174
175 conf.AddOptionsCommandline(configall, true);
176 conf.AddOptionsConfigfile(config1, true);
177 conf.AddOptionsDatabase(config2, true);
178
179 // To enable the mapping of the position arguments call this
180 conf.SetArgumentPositions(p);
181
182\endcode
183
184If the second option is false, the options will not be displayed in any
185\b --help directive, but are available to the user. Each of the functions
186can be called more than once. If an option should be available from
187all kind of inputs AddOptions() can be used which will call all
188four other AddOptions() functions.
189
190A special case are the options from environment variables. Since you might
191want to use the same option-key for the command-line and the environment,
192a mapping is needed (e.g. from \b PATH to \b --path). This mapping
193can be implemented by a mapping function or by the build in mapping
194and be initialized like this:
195
196\code
197
198 conf.AddEnv("path", "PATH");
199
200\endcode
201
202or
203
204\code
205
206 const string name_mapper(const string str)
207 {
208 return str=="PATH" ? "path" : "";
209 }
210
211 conf.SetNameMapper(name_mapper);
212
213\endcode
214
215Assuming all the above is done in a function calles SetupConfiguration(),
216a simple program to demonstrate the power of the class could look like this:
217
218\code
219
220 int main(int argc, char **argv)
221 {
222 int opt;
223
224 Configuration conf(argv[0]);
225 SetupConfiguration(conf, opt);
226
227 po::variables_map vm;
228 try
229 {
230 vm = conf.Parse(argc, argv);
231 }
232 catch (std::exception &e)
233 {
234 po::multiple_occurrences *MO = dynamic_cast<po::multiple_occurrences*>(&e);
235 if (MO)
236 cout << "Error: " << e.what() << " of '" << MO->get_option_name() << "' option." << endl;
237 else
238 cout << "Error: " << e.what() << endl;
239 cout << endl;
240
241 return -1;
242 }
243
244 cout << "Opt1: " << conf.GetString("option1") << endl;
245 cout << "Opt2: " << conf.GetInt("option2") << endl;
246 cout << "Opt3: " << conf.GetDouble("option3") << endl;
247 cout << "Opt4: " << opt << endl;
248
249 return 0;
250 }
251
252\endcode
253
254Another possibility to access the result is the direct approach, for example:
255
256\code
257
258 vector<int> i = vm["option2"].as<int>();
259 vector<string> vec = vm["option6"].as<vector<string>>();
260
261\endcode
262
263Note that accessing an option which was not given will throw an exception.
264Therefor its availability should first be checked in one of the following
265ways:
266
267\code
268
269 bool has_option1 = vm.count("option1");
270 bool has_option2 = conf.Has("option2");
271
272\endcode
273
274@section Extensions
275
276The configuration interpreter can be easily extended to new types, for example:
277
278\code
279
280template<class T,class S> // Just for the output
281 std::ostream &operator<<(std::ostream &out, const pair<T,S> &f)
282 {
283 out << f.first << "|" << f.second;
284 return out;
285 }
286
287template<class T, class S> // Needed to convert the option
288 std::istream &operator>>(std::istream &in, pair<T,S> &f)
289 {
290 char c;
291 in >> f.first;
292 in >> c;
293 if (c!=':')
294 return in;
295 in >> f.second;
296 return in;
297 }
298
299typedef pair<int,int> mytype; // Type definition
300
301void main(int argc, char **argv)
302{
303 po::options_description config("Configuration");
304 config.add_options()
305 ("mytype", var<mytype>(), "my new type")
306 ;
307
308 Configuration conf;
309 conf.AddOptionsCommandline(config);
310 conf.Parse(argc, argv);
311
312 cout << conf.Get<mytype>("mytype") << endl;
313}
314
315\endcode
316
317@section Examples
318
319 - An example can be found in \ref argv.cc
320
321@todo
322
323 - Maybe we should remove the necessity to propagate argv[0] in the constructor?
324 - Add an option to the constructor to switch of database/file access
325
326*/
327// **************************************************************************
328#include "Configuration.h"
329
330#include <fstream>
331#include <iostream>
332#include <iomanip>
333
334#include <boost/regex.hpp>
335#include <boost/filesystem.hpp>
336
337#define HAS_SQL
338
339#ifdef HAS_SQL
340#include <mysql++/mysql++.h>
341#endif
342
343using namespace std;
344
345namespace style = boost::program_options::command_line_style;
346
347// --------------------------------------------------------------------------
348//
349//! The purpose of this function is basically to connect to the database,
350//! and retrieve all the options entries from the 'Configuration' table.
351//!
352//! @param database
353//! The URL of the database from which the configuration data is
354//! retrieved. It should be given in the form
355//! \li [user[:password]@]server.com[:port]/database
356//!
357//! with
358//! - user: user name (default is the current user)
359//! - password: necessary if required by the database rights
360//! - server: the URL of the server (can be 'localhost')
361//! - port: the port to which to connect (usually obsolete)
362//! - database: The name of the database containing the table
363//!
364//! @param desc
365//! A reference to the object with the description of the options
366//! which should be retrieved.
367//!
368//! @param allow_unregistered
369//! If this is true also unregistered, i.e. options unknown to desc,
370//! are returned. Otherwise an exception is thrown if such an option
371//! was retrieved.
372//!
373//! @return
374//! Return an object of type basic_parsed_options containing all
375//! the entries retrieved from the database. Options not found in
376//! desc are flagged as unregistered.
377//!
378//! @throws
379//! Two types of exceptions are thrown
380//! - It thows an unnamed exception if the options could not be
381//! retrieved properly from the databse.
382//! - If an option is not registered within the given descriptions
383//! and \b allow_unregistered is \b false, an exception of type
384//! \b po::unknown_option is thrown.
385//!
386//! @todo
387//! - The exceptions handling should be improved.
388//! - The final database layout is missing in the description
389//! - Shell we allow options to be given more than once?
390//
391#ifdef HAS_SQL
392po::basic_parsed_options<char>
393 Configuration::parse_database(const string &prgname, const string &database, const po::options_description& desc, bool allow_unregistered)
394{
395 //static const boost::regex expr("(([[:word:].-]+)(:(.+))?@)?([[:word:].-]+)(:([[:digit:]]+))?(/([[:word:].-]+))?");
396 static const boost::regex expr("(([[:word:].-]+)(:(.+))?@)?([[:word:].-]+)(:([[:digit:]]+))?(/([[:word:].-]+))");
397 // 2: user
398 // 4: pass
399 // 5: server
400 // 7: port
401 // 9: db
402
403 boost::smatch what;
404 if (!boost::regex_match(database, what, expr, boost::match_extra))
405 throw runtime_error("Couldn't parse '"+database+"'.");
406
407 if (what.size()!=10)
408 throw runtime_error("Error parsing '"+database+"'.");
409
410 const string user = what[2];
411 const string passwd = what[4];
412 const string server = what[5];
413 const string db = what[9];
414 const int port = atoi(string(what[7]).c_str());
415
416 cout << "Connecting to '";
417 if (!user.empty())
418 cout << user << "@";
419 cout << server;
420 if (port)
421 cout << ":" << port;
422 if (!db.empty())
423 cout << "/" << db;
424 cout << "' for " << prgname << endl;
425
426 mysqlpp::Connection conn(db.c_str(), server.c_str(), user.c_str(), passwd.c_str(), port);
427 /* throws exceptions
428 if (!conn.connected())
429 {
430 cout << "MySQL connection error: " << conn.error() << endl;
431 throw;
432 }*/
433
434 const mysqlpp::StoreQueryResult res =
435 conn.query("SELECT CONCAT(fKey1,fKey2), fValue "
436 "FROM ProgramOption "
437 "WHERE fCounter=(SELECT MAX(fCounter) FROM History) "
438 "AND (fProgram='"+prgname+"' OR fProgram='*')").store();
439 /* throws exceptions
440 if (!res)
441 {
442 cout << "MySQL query failed: " << query.error() << endl;
443 throw;
444 }*/
445
446 set<string> allowed_options;
447
448 const vector<boost::shared_ptr<po::option_description>> &options = desc.options();
449 for (unsigned i=0; i<options.size(); ++i)
450 {
451 const po::option_description &d = *options[i];
452 if (d.long_name().empty())
453 boost::throw_exception(po::error("long name required for database"));
454
455 allowed_options.insert(d.long_name());
456 }
457
458 po::parsed_options result(&desc);
459
460 for (vector<mysqlpp::Row>::const_iterator v=res.begin(); v<res.end(); v++)
461 {
462 const string key = (*v)[0].c_str();
463 if (key.empty()) // key == > Throw exception
464 continue;
465
466 // Check if we are allowed to accept unregistered options,
467 // i.e. options which are not in options_description &desc.
468 const bool unregistered = allowed_options.find(key)==allowed_options.end();
469 if (unregistered && allow_unregistered)
470 boost::throw_exception(po::unknown_option(key));
471
472 // Create a key/value-pair and store whether it is a
473 // registered option of not
474 po::option n;
475 n.string_key = key;
476 // This is now identical to file parsing. What if we want
477 // to concatenate options like on the command line?
478 n.value.clear(); // Fixme: composing?
479 n.value.push_back((*v)[1].c_str());
480 //n.unregistered = unregistered;
481
482 // If any parsing will be done in the future...
483 //n.value().original_tokens.clear();
484 //n.value().original_tokens.push_back(name);
485 //n.value().original_tokens.push_back(value);
486
487 result.options.push_back(n);
488 }
489
490 return result;
491}
492#else
493po::basic_parsed_options<char>
494 Configuration::parse_database(const string &, const string &, const po::options_description &desc, bool)
495{
496 return po::parsed_options(&desc);
497}
498#endif
499
500// --------------------------------------------------------------------------
501//
502//!
503//
504Configuration::Configuration(const string &prgname) : fName(UnLibToolize(prgname)),
505fNameMapper(bind1st(mem_fun(&Configuration::DefaultMapper), this)),
506fPrintUsage(bind(&Configuration::PrintUsage, this))
507{
508 po::options_description generic("Generic options");
509 generic.add_options()
510 ("version,V", "Print version information.")
511 ("help", "Print available commandline options.")
512 ("help-environment", "Print available environment variables.")
513 ("help-database", "Print available options retreived from the database.")
514 ("help-config", "Print available configuration file options.")
515 ("print-all", "Print all options as parsed from all the different sources.")
516 ("print", "Print options as parsed from the commandline.")
517 ("print-default", "Print options as parsed from default configuration file.")
518 ("print-database", "Print options as retrieved from the database.")
519 ("print-config", "Print options as parsed from the high priority configuration file.")
520 ("print-environment", "Print options as parsed from the environment.")
521 ("print-unknown", "Print unrecognized options.")
522 ("print-options", "Print options as passed to program.")
523 ("print-wildcards", "Print all options registered with wildcards.")
524 ("dont-check", "Do not check validity of options from files and database.")
525 ("dont-check-files", "Do not check validity of options from files.")
526 ("dont-check-database", "Do not check validity of options from database.")
527 ;
528
529 po::options_description def_config;
530 def_config.add_options()
531 ("default", var<string>(fName+string(".rc")), "Default configuration file.")
532 ;
533
534 po::options_description config("Configuration options");
535 config.add_options()
536 ("config,C", var<string>(), "Configuration file overwriting options retrieved from the database.")
537 ("database", var<string>(), "Database link as in\n\t[user[:password]@]server.com[:port]/database\nOverwrites options from the default configuration file.")
538 ("no-database", "Suppress any access to the database even if a database URL was set.")
539 ;
540
541 fOptionsCommandline[kVisible].add(generic);
542 fOptionsCommandline[kVisible].add(config);
543 fOptionsCommandline[kVisible].add(def_config);
544 fOptionsConfigfile[kVisible].add(config);
545}
546
547// --------------------------------------------------------------------------
548//
549//!
550//
551void Configuration::PrintParsed(const po::parsed_options &parsed) const
552{
553 const vector< po::basic_option<char> >& options = parsed.options;
554
555 // .description -> Pointer to opt_commandline
556 // const std::vector< shared_ptr<option_description> >& options() const;
557
558 //const std::string& key(const std::string& option) const;
559 //const std::string& long_name() const;
560 //const std::string& description() const;
561 //shared_ptr<const value_semantic> semantic() const;
562
563 int maxlen = 0;
564 for (unsigned i=0; i<options.size(); ++i)
565 {
566 const po::basic_option<char> &opt = options[i];
567
568 if (opt.value.size()>0 && opt.string_key[0]!='-')
569 Max(maxlen, opt.string_key.length());
570 }
571
572 cout.setf(ios_base::left);
573
574 // =============> Implement prining of parsed options
575 for(unsigned i=0; i<options.size(); ++i)
576 {
577 const po::basic_option<char> &opt = options[i];
578
579 if (opt.value.size()==0 && !opt.string_key[0]=='-')
580 cout << "--";
581 cout << setw(maxlen) << opt.string_key;
582 if (opt.value.size()>0)
583 cout << " = " << opt.value[0];
584
585 //for (int j=0; j<options[i].value.size(); j++)
586 // cout << "\t = " << options[i].value[j];
587 //cout << "/" << options[i].original_tokens[0];
588
589 ostringstream com;
590
591 if (opt.position_key>=0)
592 com << " [position=" << opt.position_key << "]";
593 if (opt.unregistered)
594 com << " [unregistered]";
595
596 if (!com.str().empty())
597 cout << " # " << com.str();
598
599 cout << endl;
600 }
601}
602
603template<class T>
604string Configuration::VecAsStr(const po::variable_value &v) const
605{
606 ostringstream str;
607
608 const vector<T> vec = v.as<vector<T>>();
609 for (typename std::vector<T>::const_iterator s=vec.begin(); s<vec.end(); s++)
610 str << " " << *s;
611
612 return str.str().substr(1);
613}
614
615string Configuration::VarAsStr(const po::variable_value &v) const
616{
617 if (v.value().type()==typeid(bool))
618 return v.as<bool>() ? "yes ": "no";
619
620 if (v.value().type()==typeid(string))
621 return v.as<string>();
622
623 if (v.value().type()==typeid(int16_t))
624 return to_string((long long int)v.as<int16_t>());
625
626 if (v.value().type()==typeid(int32_t))
627 return to_string((long long int)v.as<int32_t>());
628
629 if (v.value().type()==typeid(int64_t))
630 return to_string((long long int)v.as<int64_t>());
631
632 if (v.value().type()==typeid(uint16_t))
633 return to_string((long long unsigned int)v.as<uint16_t>());
634
635 if (v.value().type()==typeid(uint32_t))
636 return to_string((long long unsigned int)v.as<uint32_t>());
637
638 if (v.value().type()==typeid(uint64_t))
639 return to_string((long long unsigned int)v.as<uint64_t>());
640
641 if (v.value().type()==typeid(float))
642 return to_string((long double)v.as<float>());
643
644 if (v.value().type()==typeid(double))
645 return to_string((long double)v.as<double>());
646
647 if (v.value().type()==typeid(vector<string>))
648 return VecAsStr<string>(v);
649
650 if (v.value().type()==typeid(vector<int16_t>))
651 return VecAsStr<int16_t>(v);
652
653 if (v.value().type()==typeid(vector<int32_t>))
654 return VecAsStr<int32_t>(v);
655
656 if (v.value().type()==typeid(vector<int64_t>))
657 return VecAsStr<int64_t>(v);
658
659 if (v.value().type()==typeid(vector<uint16_t>))
660 return VecAsStr<uint16_t>(v);
661
662 if (v.value().type()==typeid(vector<uint32_t>))
663 return VecAsStr<uint32_t>(v);
664
665 if (v.value().type()==typeid(vector<uint64_t>))
666 return VecAsStr<uint64_t>(v);
667
668 if (v.value().type()==typeid(vector<float>))
669 return VecAsStr<float>(v);
670
671 if (v.value().type()==typeid(vector<double>))
672 return VecAsStr<double>(v);
673
674 ostringstream str;
675 str << hex << setfill('0') << "0x";
676 if (v.value().type()==typeid(Hex<uint16_t>))
677 str << setw(4) << v.as<Hex<uint16_t>>();
678
679 if (v.value().type()==typeid(Hex<uint32_t>))
680 str << setw(8) << v.as<Hex<uint32_t>>();
681
682 if (v.value().type()==typeid(Hex<uint64_t>))
683 str << setw(16) << v.as<Hex<uint64_t>>();
684
685 return str.str();
686}
687
688// --------------------------------------------------------------------------
689//
690//!
691//
692void Configuration::PrintOptions() const
693{
694 cout << "Options propagated to program:" << endl;
695
696 int maxlen = 0;
697 for (map<string,po::variable_value>::const_iterator m=fVariables.begin();
698 m!=fVariables.end(); m++)
699 Max(maxlen, m->first.length());
700
701 cout.setf(ios_base::left);
702
703 // =============> Implement prining of options in use
704 for (map<string,po::variable_value>::const_iterator m=fVariables.begin();
705 m!=fVariables.end(); m++)
706 {
707 const po::variable_value &v = m->second;
708
709 ostringstream str;
710
711 if (v.value().type()==typeid(bool))
712 str << " bool";
713 if (v.value().type()==typeid(string))
714 str << " string";
715 if (v.value().type()==typeid(int16_t))
716 str << " int16_t";
717 if (v.value().type()==typeid(int32_t))
718 str << " int32_t";
719 if (v.value().type()==typeid(int64_t))
720 str << " int64_t";
721 if (v.value().type()==typeid(uint16_t))
722 str << " uint16_t";
723 if (v.value().type()==typeid(uint32_t))
724 str << " uint32_t";
725 if (v.value().type()==typeid(uint64_t))
726 str << " uint64_t";
727 if (v.value().type()==typeid(float))
728 str << " float";
729 if (v.value().type()==typeid(double))
730 str << " double";
731 if (v.value().type()==typeid(Hex<uint16_t>))
732 str << " Hex<uint16_t>";
733 if (v.value().type()==typeid(Hex<uint32_t>))
734 str << " Hex<uint32_t>";
735 if (v.value().type()==typeid(Hex<uint64_t>))
736 str << " Hex<uint64_t>";
737 if (v.value().type()==typeid(vector<string>))
738 str << " vector<string>";
739 if (v.value().type()==typeid(vector<int16_t>))
740 str << " vector<int16_t>";
741 if (v.value().type()==typeid(vector<int32_t>))
742 str << " vector<int32_t>";
743 if (v.value().type()==typeid(vector<int64_t>))
744 str << " vector<int64_t>";
745 if (v.value().type()==typeid(vector<uint16_t>))
746 str << " vector<uint16_t>";
747 if (v.value().type()==typeid(vector<uint32_t>))
748 str << " vector<uint32_t>";
749 if (v.value().type()==typeid(vector<uint64_t>))
750 str << " vector<uint64_t>";
751 if (v.value().type()==typeid(vector<float>))
752 str << " vector<float>";
753 if (v.value().type()==typeid(vector<double>))
754 str << " vector<double>";
755
756 if (str.str().empty())
757 str << " unknown[" << v.value().type().name() << "]";
758
759 const string var = VarAsStr(v);
760 cout << setw(maxlen) << m->first;
761 if (!var.empty())
762 cout << " = ";
763 cout << var << " #" << str.str();
764
765 if (v.defaulted())
766 cout << " [default]";
767 if (v.empty())
768 cout << " [empty]";
769
770 cout << endl;
771 }
772
773 cout << endl;
774}
775
776// --------------------------------------------------------------------------
777//
778//!
779//
780void Configuration::PrintUnknown(const vector<string> &vec, int steps) const
781{
782 for (vector<string>::const_iterator v=vec.begin(); v<vec.end(); v+=steps)
783 cout << " " << *v << endl;
784 cout << endl;
785}
786
787multimap<string, string> Configuration::GetOptions() const
788{
789 multimap<string,string> rc;
790
791 for (map<string,po::variable_value>::const_iterator m=fVariables.begin();
792 m!=fVariables.end(); m++)
793 rc.insert(make_pair(m->first, VarAsStr(m->second)));
794
795 return rc;
796}
797
798// --------------------------------------------------------------------------
799//
800//!
801//
802void Configuration::PrintUnknown() const
803{
804 if (fUnknownCommandline.size())
805 {
806 cout << "Unknown commandline options:" << endl;
807 PrintUnknown(fUnknownCommandline);
808 }
809
810 if (fUnknownConfigfile.size())
811 {
812 cout << "Unknown options in configfile:" << endl;
813 PrintUnknown(fUnknownConfigfile, 2);
814 }
815
816 if (fUnknownEnvironment.size())
817 {
818 cout << "Unknown environment variables:" << endl;
819 PrintUnknown(fUnknownEnvironment);
820 }
821
822 if (fUnknownDatabase.size())
823 {
824 cout << "Unknown database entry:" << endl;
825 PrintUnknown(fUnknownDatabase);
826 }
827}
828
829// --------------------------------------------------------------------------
830//
831//!
832//
833void Configuration::AddOptionsCommandline(const po::options_description &cl, bool visible)
834{
835 fOptionsCommandline[visible].add(cl);
836}
837
838// --------------------------------------------------------------------------
839//
840//!
841//
842void Configuration::AddOptionsConfigfile(const po::options_description &cf, bool visible)
843{
844 fOptionsConfigfile[visible].add(cf);
845}
846
847// --------------------------------------------------------------------------
848//
849//!
850//
851void Configuration::AddOptionsEnvironment(const po::options_description &env, bool visible)
852{
853 fOptionsEnvironment[visible].add(env);
854}
855
856// --------------------------------------------------------------------------
857//
858//!
859//
860void Configuration::AddOptionsDatabase(const po::options_description &db, bool visible)
861{
862 fOptionsDatabase[visible].add(db);
863}
864
865// --------------------------------------------------------------------------
866//
867//!
868//
869void Configuration::SetArgumentPositions(const po::positional_options_description &desc)
870{
871 fArgumentPositions = desc;
872}
873
874// --------------------------------------------------------------------------
875//
876//!
877//
878void Configuration::SetNameMapper(const function<string(string)> &func)
879{
880 fNameMapper = func;
881}
882
883void Configuration::SetNameMapper()
884{
885 fNameMapper = bind1st(mem_fun(&Configuration::DefaultMapper), this);
886}
887
888void Configuration::SetPrintUsage(const function<void(void)> &func)
889{
890 fPrintUsage = func;
891}
892
893void Configuration::SetPrintUsage()
894{
895 fPrintUsage = bind(&Configuration::PrintUsage, this);
896}
897
898void Configuration::SetPrintVersion(const function<void(const string&)> &func)
899{
900 fPrintVersion = func;
901}
902
903void Configuration::SetPrintVersion()
904{
905 fPrintVersion = function<void(const string&)>();
906}
907
908// --------------------------------------------------------------------------
909//
910//!
911//! The idea of the Parse() memeber-function is to parse the command-line,
912//! the configuration files, the databse and the environment and return
913//! a proper combined result.
914//!
915//! In details the following actions are performed in the given order:
916//!
917//! - (0) Init local variables with the list of options described by the
918//! data members.
919//! - (1) Reset the data members fPriorityFile, fDefaultFile, fDatabase
920//! - (2) Parse the command line
921//! - (3) Check for \b --help* command-line options and performe
922//! corresponding action
923//! - (4) Check for \b --print and \b --print-all and perform corresponding
924//! action
925//! - (5) Read and parse the global configuration file, which is compiled
926//! from the path corresponding to the argument given in the
927//! constructor + "/fact++.rc", unrecognized options are always
928//! allowed. Note that in contradiction to all other options
929//! the options in this file are not checked at all. Hence,
930//! typos might stay unnoticed.
931//! - (6) Read and parse the default configuration file, which is either
932//! given by the default name or the \b --default command-line
933//! option. The default name is compiled from the argument
934//! given to the constructor and ".rc". If the file-name is
935//! identical to the default (no command-line option given)
936//! a missing configuration file is no error. Depending on
937//! the \b --dont-check and \b --dont-check-files options,
938//! unrecognized options in the file throw an exception or not.
939//! - (7) Check for \b --print-default and \b --print-all and perform
940//! corresponding action
941//! - (8) Read and parse the priority configuration file, which must be given
942//! by the \b --config or \b -C command-line option or a
943//! corresponding entry in the default-configuration file.
944//! If an option on the command-line and the in the configuration
945//! file exists, the command-line option has priority.
946//! If none is given, no priority file is read. Depending on
947//! the \b --dont-check and \b --dont-check-files options,
948//! unrecognized options in the file throw an exception or not.
949//! - (9) Check for \b --print-config and \b --print-all and perform
950//! corresponding action
951//! - (10) Retrieve options from the database according to the
952//! options \b --database and \b --no-database. Note that
953//! options given on the command-line have highest priority.
954//! The second priority is the priority-configuration file.
955//! The options from the default configuration-file have
956//! lowest priority.
957//! - (11) Check for \b --print-database and \b --print-all and perform
958//! corresponding action
959//! - (12) Parse the environment options.
960//! - (13) Check for \b --print-environment and \b --print-all and perform
961//! corresponding action
962//! - (14) Compile the final result. The priority of the options is (in
963//! decreasing order): command-line options, options from the
964//! priority configuration file, options from the database,
965//! options from the default configuration-file and options
966//! from the environment.
967//! - (15) Find all options which were found and flagged as unrecognized,
968//! because they are not in the user-defined list of described
969//! options, are collected and stored in the corresponding
970//! data-members.
971//! - (16) Find all options which where registered with wildcards and
972//! store the list in fWildcardOptions.
973//! - (17) Before the function returns it check for \b --print-options
974//! and \b --print-unknown and performs the corresponding actions.
975//!
976//!
977//! @param argc,argv
978//! arguments passed to <B>main(int argc, char **argv)</B>
979//!
980//! @returns
981//! A reference to the list with the resulting options with their
982//! values.
983//!
984//! @todo
985//! - describe the exceptions
986//! - describe what happens in a more general way
987//! - print a waring when no default coonfig file is read
988//! - proper handling and error messages if files not available
989//
990const po::variables_map &Configuration::Parse(int argc, const char **argv)
991{
992 const po::positional_options_description &opt_positional = fArgumentPositions;
993
994 // ------------------------ (0) --------------------------
995
996 po::options_description opt_commandline;
997 po::options_description opt_configfile;
998 po::options_description opt_environment;
999 po::options_description opt_database;
1000
1001 for (int i=0; i<2; i++)
1002 {
1003 opt_commandline.add(fOptionsCommandline[i]);
1004 opt_configfile.add(fOptionsConfigfile[i]);
1005 opt_environment.add(fOptionsEnvironment[i]);
1006 opt_database.add(fOptionsDatabase[i]);
1007 }
1008
1009 // ------------------------ (1) --------------------------
1010
1011 fPriorityFile = "";
1012 fDefaultFile = "";
1013 fDatabase = "";
1014
1015 // ------------------------ (2) --------------------------
1016
1017 po::command_line_parser parser(argc, const_cast<char**>(argv));
1018 parser.options(opt_commandline);
1019 parser.positional(opt_positional);
1020 parser.style(style::unix_style&~style::allow_guessing);
1021 //parser.allow_unregistered();
1022
1023 const po::parsed_options parsed_commandline = parser.run();
1024
1025 // ------------------------ (3) --------------------------
1026
1027 po::variables_map getfiles;
1028 po::store(parsed_commandline, getfiles);
1029
1030 if (getfiles.count("version"))
1031 PrintVersion();
1032 if (getfiles.count("help"))
1033 {
1034 fPrintUsage();
1035 cout <<
1036 "Options:\n"
1037 "The following describes the available commandline options. "
1038 "For further details on how command line option are parsed "
1039 "and in which order which configuration sources are accessed "
1040 "please refer to the class reference of the Configuration class." << endl;
1041 cout << fOptionsCommandline[kVisible] << endl;
1042 }
1043 if (getfiles.count("help-config"))
1044 cout << fOptionsConfigfile[kVisible] << endl;
1045 if (getfiles.count("help-env"))
1046 cout << fOptionsEnvironment[kVisible] << endl;
1047 if (getfiles.count("help-database"))
1048 cout << fOptionsDatabase[kVisible] << endl;
1049
1050 // ------------------------ (4) --------------------------
1051
1052 if (getfiles.count("print") || getfiles.count("print-all"))
1053 {
1054 cout << endl << "Parsed commandline options:" << endl;
1055 PrintParsed(parsed_commandline);
1056 cout << endl;
1057 }
1058
1059 // ------------------------ (5) --------------------------
1060
1061 const boost::filesystem::path path(GetName());
1062 const string globalfile = (path.parent_path()/boost::filesystem::path("fact++.rc")).string();
1063
1064 cerr << "Reading global options from '" << globalfile << "'." << endl;
1065
1066 ifstream gfile(globalfile.c_str());
1067 // ===> FIXME: Proper handling of missing file or wrong file name
1068 const po::parsed_options parsed_globalfile =
1069 !gfile ?
1070 po::parsed_options(&opt_configfile) :
1071 po::parse_config_file<char>(gfile, opt_configfile, true);
1072
1073 // ------------------------ (6) --------------------------
1074
1075 // Get default file from command line
1076 if (getfiles.count("default"))
1077 {
1078 fDefaultFile = getfiles["default"].as<string>();
1079 cerr << "Reading default options from '" << fDefaultFile << "'." << endl;
1080 }
1081
1082 const bool checkf = !getfiles.count("dont-check-files") && !getfiles.count("dont-check");
1083 const bool defaulted = getfiles.count("default") && getfiles["default"].defaulted();
1084 //const bool exists = boost::filesystem::exists(fDefaultFile);
1085
1086 ifstream indef(fDefaultFile.c_str());
1087 // ===> FIXME: Proper handling of missing file or wrong file name
1088 const po::parsed_options parsed_defaultfile =
1089 !indef && defaulted ?
1090 po::parsed_options(&opt_configfile) :
1091 po::parse_config_file<char>(indef, opt_configfile, !checkf);
1092
1093 // ------------------------ (7) --------------------------
1094
1095 if (getfiles.count("print-default") || getfiles.count("print-all"))
1096 {
1097 if (!indef.is_open() && defaulted)
1098 cout << "No configuration file by --default option specified." << endl;
1099 else
1100 {
1101 cout << endl << "Parsed options from '" << fDefaultFile << "':" << endl;
1102 PrintParsed(parsed_defaultfile);
1103 cout << endl;
1104 }
1105 }
1106
1107 po::store(parsed_defaultfile, getfiles);
1108
1109 // ------------------------ (8) --------------------------
1110
1111 // Get priority from commandline(1), defaultfile(2)
1112 if (getfiles.count("config"))
1113 {
1114 fPriorityFile = getfiles["config"].as<string>();
1115 cerr << "Reading config options from '" << fPriorityFile << "'." << endl;
1116 }
1117
1118 ifstream inpri(fPriorityFile.c_str());
1119 // ===> FIXME: Proper handling of missing file or wrong file name
1120 const po::parsed_options parsed_priorityfile =
1121 fPriorityFile.empty() ? po::parsed_options(&opt_configfile) :
1122 po::parse_config_file<char>(inpri, opt_configfile, !checkf);
1123
1124 // ------------------------ (9) --------------------------
1125
1126 if (getfiles.count("print-config") || getfiles.count("print-all"))
1127 {
1128 if (fPriorityFile.empty())
1129 cout << "No configuration file by --config option specified." << endl;
1130 else
1131 {
1132 cout << endl << "Parsed options from '" << fPriorityFile << "':" << endl;
1133 PrintParsed(parsed_priorityfile);
1134 cout << endl;
1135 }
1136 }
1137
1138 // ------------------------ (10) -------------------------
1139
1140 po::variables_map getdatabase;
1141 po::store(parsed_commandline, getdatabase);
1142 po::store(parsed_priorityfile, getdatabase);
1143 po::store(parsed_defaultfile, getdatabase);
1144 po::store(parsed_globalfile, getdatabase);
1145
1146 if (getdatabase.count("database") && !getdatabase.count("no-database"))
1147 {
1148 fDatabase = getdatabase["database"].as<string>();
1149 cerr << "Requesting options from database for '" << fName << "'" << endl;
1150 }
1151
1152 const bool checkdb = !getdatabase.count("dont-check-database") && !getdatabase.count("dont-check");
1153
1154 const po::parsed_options parsed_database =
1155 fDatabase.empty() ? po::parsed_options(&opt_database) :
1156 parse_database(path.filename(), fDatabase, opt_database, !checkdb);
1157
1158 // ------------------------ (11) -------------------------
1159
1160 if (getfiles.count("print-database") || getfiles.count("print-all"))
1161 {
1162 if (fDatabase.empty())
1163 cout << "No database access requested." << endl;
1164 else
1165 {
1166 cout << endl << "Options received from '" << fDatabase << "':" << endl;
1167 PrintParsed(parsed_database);
1168 cout << endl;
1169 }
1170 }
1171
1172 // ------------------------ (12) -------------------------
1173
1174 const po::parsed_options parsed_environment = po::parse_environment(opt_environment, fNameMapper);
1175
1176 // ------------------------ (13) -------------------------
1177
1178 if (getfiles.count("print-environment"))
1179 {
1180 cout << "Parsed options from environment:" << endl;
1181 PrintParsed(parsed_environment);
1182 cout << endl;
1183 }
1184
1185 // ------------------------ (14) -------------------------
1186 po::variables_map result;
1187 po::store(parsed_commandline, result);
1188 po::store(parsed_priorityfile, result);
1189 po::store(parsed_database, result);
1190 po::store(parsed_defaultfile, result);
1191 po::store(parsed_globalfile, result);
1192 po::store(parsed_environment, result);
1193 po::notify(result);
1194
1195 fVariables = result;
1196
1197 // ------------------------ (15) -------------------------
1198
1199 const vector<string> unknown0 = collect_unrecognized(parsed_globalfile.options, po::exclude_positional);
1200 const vector<string> unknown1 = collect_unrecognized(parsed_defaultfile.options, po::exclude_positional);
1201 const vector<string> unknown2 = collect_unrecognized(parsed_priorityfile.options, po::exclude_positional);
1202
1203 fUnknownConfigfile.clear();
1204 fUnknownConfigfile.insert(fUnknownConfigfile.end(), unknown0.begin(), unknown0.end());
1205 fUnknownConfigfile.insert(fUnknownConfigfile.end(), unknown1.begin(), unknown1.end());
1206 fUnknownConfigfile.insert(fUnknownConfigfile.end(), unknown2.begin(), unknown2.end());
1207
1208 fUnknownCommandline = collect_unrecognized(parsed_commandline.options, po::exclude_positional);
1209 fUnknownEnvironment = collect_unrecognized(parsed_environment.options, po::exclude_positional);
1210 fUnknownDatabase = collect_unrecognized(parsed_database.options, po::exclude_positional);
1211
1212 // ------------------------ (16) -------------------------
1213
1214 CreateWildcardOptions();
1215
1216 // ------------------------ (17) -------------------------
1217
1218 if (result.count("print-options"))
1219 PrintOptions();
1220
1221 if (result.count("print-wildcards"))
1222 PrintWildcardOptions();
1223
1224 if (result.count("print-unknown"))
1225 PrintUnknown();
1226
1227 return fVariables;
1228}
1229
1230bool Configuration::DoParse(int argc, const char **argv, const std::function<void()> &PrintHelp)
1231{
1232 try
1233 {
1234 Parse(argc, argv);
1235 }
1236#if BOOST_VERSION > 104000
1237 catch (po::multiple_occurrences &e)
1238 {
1239 cerr << "Program options invalid due to: " << e.what() << " of '" << e.get_option_name() << "'." << endl;
1240 return false;
1241 }
1242#endif
1243 catch (exception& e)
1244 {
1245 cerr << "Program options invalid due to: " << e.what() << endl;
1246 return false;
1247 }
1248
1249 if (HasVersion() || HasPrint())
1250 return false;
1251
1252 if (HasHelp())
1253 {
1254 if (PrintHelp)
1255 PrintHelp();
1256 return false;
1257 }
1258
1259 return true;
1260}
1261
1262// --------------------------------------------------------------------------
1263//
1264//! Create a list of all options which were registered using wildcards
1265//!
1266void Configuration::CreateWildcardOptions()
1267{
1268 po::options_description opts;
1269
1270 for (int i=0; i<2; i++)
1271 {
1272 opts.add(fOptionsCommandline[i]);
1273 opts.add(fOptionsConfigfile[i]);
1274 opts.add(fOptionsEnvironment[i]);
1275 opts.add(fOptionsDatabase[i]);
1276 }
1277
1278 fWildcardOptions.clear();
1279
1280 typedef map<string,po::variable_value> Vars;
1281 typedef vector<boost::shared_ptr<po::option_description>> Descs;
1282
1283 const Descs &desc = opts.options();
1284
1285 for (Vars::const_iterator io=fVariables.begin(); io!=fVariables.end(); io++)
1286 {
1287 for (Descs::const_iterator id=desc.begin(); id!=desc.end(); id++)
1288#if BOOST_VERSION > 104000
1289 if ((*id)->match(io->first, false, false, false)==po::option_description::approximate_match)
1290#else
1291 if ((*id)->match(io->first, false)==po::option_description::approximate_match)
1292#endif
1293 fWildcardOptions[io->first] = (*id)->long_name();
1294 }
1295}
1296
1297// --------------------------------------------------------------------------
1298//
1299//! Print a list of all options which were registered using wildcards and
1300//! have not be registered subsequently by access.
1301//!
1302void Configuration::PrintWildcardOptions() const
1303{
1304 cout << "Options registered with wildcards and not yet accessed:" << endl;
1305
1306 size_t max = 0;
1307 for (map<string,string>::const_iterator it=fWildcardOptions.begin(); it!=fWildcardOptions.end(); it++)
1308 if (it->second.length()>max)
1309 max = it->second.length();
1310
1311 cout.setf(ios_base::left);
1312 for (map<string,string>::const_iterator it=fWildcardOptions.begin(); it!=fWildcardOptions.end(); it++)
1313 cout << setw(max+1) << it->second << " : " << it->first <<endl;
1314}
1315
1316// --------------------------------------------------------------------------
1317//
1318//! Removes /.libs/lt- from a path or just lt- from the filename.
1319//!
1320//! @param src
1321//! input path with filename
1322//! @returns
1323//! path cleaned from libtool extensions
1324//!
1325string Configuration::UnLibToolize(const string &src) const
1326{
1327 const boost::filesystem::path path(src);
1328
1329 string pname = path.parent_path().string();
1330 string fname = path.filename();
1331
1332 // If the filename starts with "lt-" remove it from the name
1333 if (fname.substr(0, 3)=="lt-")
1334 fname = fname.substr(3);
1335
1336 string pwd;
1337 // If no directory is contained determine the current directory
1338 if (pname.empty())
1339 pname = boost::filesystem::current_path().string();
1340
1341 // If the directory is relative and just ".libs" forget about it
1342 if (pname==".libs")
1343 return fname;
1344
1345
1346 // Check if the directory is long enough to contain "/.libs"
1347 if (pname.length()>=6)
1348 {
1349 // If the directory ends with "/.libs", remove it
1350 const size_t pos = pname.length()-6;
1351 if (pname.substr(pos)=="/.libs")
1352 pname = pname.substr(0, pos);
1353 }
1354
1355 // If the path is the local path do not return the path-name
1356 if (pname==boost::filesystem::current_path().string())
1357 return fname;
1358
1359 return pname+'/'+fname;
1360}
1361
1362// --------------------------------------------------------------------------
1363//
1364//! Print version information about the program and package.
1365//!
1366//! The program name is taken from fName. If a leading "lt-" is found,
1367//! it is removed. This is useful if the program was build and run
1368//! using libtool.
1369//!
1370//! The package name is taken from the define PACKAGE_STRING. If it is
1371//! not defined (like automatically done by autoconf) no package information
1372//! is printed. The same is true for PACKAGE_URL and PACKAGE_BUGREPORT.
1373//!
1374//! From help2man:
1375//!
1376//! The first line of the --version information is assumed to be in one
1377//! of the following formats:
1378//!
1379//! \verbatim
1380//! - <version>
1381//! - <program> <version>
1382//! - {GNU,Free} <program> <version>
1383//! - <program> ({GNU,Free} <package>) <version>
1384//! - <program> - {GNU,Free} <package> <version>
1385//! \endverbatim
1386//!
1387//! and separated from any copyright/author details by a blank line.
1388//!
1389//! Handle multi-line bug reporting sections of the form:
1390//!
1391//! \verbatim
1392//! - Report <program> bugs to <addr>
1393//! - GNU <package> home page: <url>
1394//! - ...
1395//! \endverbatim
1396//!
1397//! @param name
1398//! name of the program (usually argv[0]).
1399//!
1400void Configuration::PrintVersion() const
1401{
1402#ifndef PACKAGE_STRING
1403#define PACKAGE_STRING ""
1404#endif
1405
1406#ifndef PACKAGE_URL
1407#define PACKAGE_URL ""
1408#endif
1409
1410#ifndef PACKAGE_BUGREPORT
1411#define PACKAGE_BUGREPORT ""
1412#endif
1413
1414 if (fPrintVersion)
1415 {
1416 fPrintVersion(fName);
1417 return;
1418 }
1419
1420 const std::string n = boost::filesystem::path(fName).filename();
1421
1422 const string name = PACKAGE_STRING;
1423 const string bugs = PACKAGE_BUGREPORT;
1424 const string url = PACKAGE_URL;
1425
1426 cout << n;
1427 if (!name.empty())
1428 cout << " - " << name;
1429 cout <<
1430 "\n\n"
1431 "Written by Thomas Bretz et al.\n"
1432 "\n";
1433 if (!bugs.empty())
1434 cout << "Report bugs to <" << bugs << ">\n";
1435 if (!url.empty())
1436 cout << "Home page: " << url << "\n";
1437 cout <<
1438 "\n"
1439 "Copyright (C) 2011 by the FACT Collaboration.\n"
1440 "This is free software; see the source for copying conditions.\n"
1441 << std::endl;
1442}
Note: See TracBrowser for help on using the repository browser.