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

Last change on this file since 13203 was 13005, checked in by tbretz, 13 years ago
Added some debug options; moved the PrintHelp at a better position so that exceptions wouldn't effect the output.
File size: 49.0 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 NOT ISNULL(fValue) "
439 "AND (fProgram='"+prgname+"' OR fProgram='*')").store();
440 /* throws exceptions
441 if (!res)
442 {
443 cout << "MySQL query failed: " << query.error() << endl;
444 throw;
445 }*/
446
447 set<string> allowed_options;
448
449 const vector<boost::shared_ptr<po::option_description>> &options = desc.options();
450 for (unsigned i=0; i<options.size(); ++i)
451 {
452 const po::option_description &d = *options[i];
453 if (d.long_name().empty())
454 boost::throw_exception(po::error("long name required for database"));
455
456 allowed_options.insert(d.long_name());
457 }
458
459 po::parsed_options result(&desc);
460
461 for (vector<mysqlpp::Row>::const_iterator v=res.begin(); v<res.end(); v++)
462 {
463 const string key = (*v)[0].c_str();
464 if (key.empty()) // key == > Throw exception
465 continue;
466
467 // Check if we are allowed to accept unregistered options,
468 // i.e. options which are not in options_description &desc.
469 const bool unregistered = allowed_options.find(key)==allowed_options.end();
470 if (unregistered && allow_unregistered)
471 boost::throw_exception(po::unknown_option(key));
472
473 // Create a key/value-pair and store whether it is a
474 // registered option of not
475 po::option n;
476 n.string_key = key;
477 // This is now identical to file parsing. What if we want
478 // to concatenate options like on the command line?
479 n.value.clear(); // Fixme: composing?
480 n.value.push_back((*v)[1].c_str());
481 //n.unregistered = unregistered;
482
483 // If any parsing will be done in the future...
484 //n.value().original_tokens.clear();
485 //n.value().original_tokens.push_back(name);
486 //n.value().original_tokens.push_back(value);
487
488 result.options.push_back(n);
489 }
490
491 return result;
492}
493#else
494po::basic_parsed_options<char>
495 Configuration::parse_database(const string &, const string &, const po::options_description &desc, bool)
496{
497 return po::parsed_options(&desc);
498}
499#endif
500
501// --------------------------------------------------------------------------
502//
503//!
504//
505Configuration::Configuration(const string &prgname) : fName(UnLibToolize(prgname)),
506fNameMapper(bind1st(mem_fun(&Configuration::DefaultMapper), this)),
507fPrintUsage(bind(&Configuration::PrintUsage, this))
508{
509 po::options_description generic("Generic options");
510 generic.add_options()
511 ("version,V", "Print version information.")
512 ("help", "Print available commandline options.")
513 ("help-environment", "Print available environment variables.")
514 ("help-database", "Print available options retreived from the database.")
515 ("help-config", "Print available configuration file options.")
516 ("print-all", "Print all options as parsed from all the different sources.")
517 ("print", "Print options as parsed from the commandline.")
518 ("print-default", "Print options as parsed from default configuration file.")
519 ("print-database", "Print options as retrieved from the database.")
520 ("print-config", "Print options as parsed from the high priority configuration file.")
521 ("print-environment", "Print options as parsed from the environment.")
522 ("print-unknown", "Print unrecognized options.")
523 ("print-options", "Print options as passed to program.")
524 ("print-wildcards", "Print all options registered with wildcards.")
525 ("dont-check", "Do not check validity of options from files and database.")
526 ("dont-check-files", "Do not check validity of options from files.")
527 ("dont-check-database", "Do not check validity of options from database.")
528 ;
529
530 po::options_description def_config;
531 def_config.add_options()
532 ("default", var<string>(fName+string(".rc")), "Default configuration file.")
533 ;
534
535 po::options_description config("Configuration options");
536 config.add_options()
537 ("config,C", var<string>(), "Configuration file overwriting options retrieved from the database.")
538 ("database", var<string>(), "Database link as in\n\t[user[:password]@]server.com[:port]/database\nOverwrites options from the default configuration file.")
539 ("no-database", "Suppress any access to the database even if a database URL was set.")
540 ;
541
542 fOptionsCommandline[kVisible].add(generic);
543 fOptionsCommandline[kVisible].add(config);
544 fOptionsCommandline[kVisible].add(def_config);
545 fOptionsConfigfile[kVisible].add(config);
546}
547
548// --------------------------------------------------------------------------
549//
550//!
551//
552void Configuration::PrintParsed(const po::parsed_options &parsed) const
553{
554 const vector< po::basic_option<char> >& options = parsed.options;
555
556 // .description -> Pointer to opt_commandline
557 // const std::vector< shared_ptr<option_description> >& options() const;
558
559 //const std::string& key(const std::string& option) const;
560 //const std::string& long_name() const;
561 //const std::string& description() const;
562 //shared_ptr<const value_semantic> semantic() const;
563
564 int maxlen = 0;
565 for (unsigned i=0; i<options.size(); ++i)
566 {
567 const po::basic_option<char> &opt = options[i];
568
569 if (opt.value.size()>0 && opt.string_key[0]!='-')
570 Max(maxlen, opt.string_key.length());
571 }
572
573 cout.setf(ios_base::left);
574
575 // =============> Implement prining of parsed options
576 for(unsigned i=0; i<options.size(); ++i)
577 {
578 const po::basic_option<char> &opt = options[i];
579
580 if (opt.value.size()==0 && !opt.string_key[0]=='-')
581 cout << "--";
582 cout << setw(maxlen) << opt.string_key;
583 if (opt.value.size()>0)
584 cout << " = " << opt.value[0];
585
586 //for (int j=0; j<options[i].value.size(); j++)
587 // cout << "\t = " << options[i].value[j];
588 //cout << "/" << options[i].original_tokens[0];
589
590 ostringstream com;
591
592 if (opt.position_key>=0)
593 com << " [position=" << opt.position_key << "]";
594 if (opt.unregistered)
595 com << " [unregistered]";
596
597 if (!com.str().empty())
598 cout << " # " << com.str();
599
600 cout << endl;
601 }
602}
603
604template<class T>
605string Configuration::VecAsStr(const po::variable_value &v) const
606{
607 ostringstream str;
608
609 const vector<T> vec = v.as<vector<T>>();
610 for (typename std::vector<T>::const_iterator s=vec.begin(); s<vec.end(); s++)
611 str << " " << *s;
612
613 return str.str().substr(1);
614}
615
616string Configuration::VarAsStr(const po::variable_value &v) const
617{
618 if (v.value().type()==typeid(bool))
619 return v.as<bool>() ? "yes ": "no";
620
621 if (v.value().type()==typeid(string))
622 return v.as<string>();
623
624 if (v.value().type()==typeid(int16_t))
625 return to_string((long long int)v.as<int16_t>());
626
627 if (v.value().type()==typeid(int32_t))
628 return to_string((long long int)v.as<int32_t>());
629
630 if (v.value().type()==typeid(int64_t))
631 return to_string((long long int)v.as<int64_t>());
632
633 if (v.value().type()==typeid(uint16_t))
634 return to_string((long long unsigned int)v.as<uint16_t>());
635
636 if (v.value().type()==typeid(uint32_t))
637 return to_string((long long unsigned int)v.as<uint32_t>());
638
639 if (v.value().type()==typeid(uint64_t))
640 return to_string((long long unsigned int)v.as<uint64_t>());
641
642 if (v.value().type()==typeid(float))
643 return to_string((long double)v.as<float>());
644
645 if (v.value().type()==typeid(double))
646 return to_string((long double)v.as<double>());
647
648 if (v.value().type()==typeid(vector<string>))
649 return VecAsStr<string>(v);
650
651 if (v.value().type()==typeid(vector<int16_t>))
652 return VecAsStr<int16_t>(v);
653
654 if (v.value().type()==typeid(vector<int32_t>))
655 return VecAsStr<int32_t>(v);
656
657 if (v.value().type()==typeid(vector<int64_t>))
658 return VecAsStr<int64_t>(v);
659
660 if (v.value().type()==typeid(vector<uint16_t>))
661 return VecAsStr<uint16_t>(v);
662
663 if (v.value().type()==typeid(vector<uint32_t>))
664 return VecAsStr<uint32_t>(v);
665
666 if (v.value().type()==typeid(vector<uint64_t>))
667 return VecAsStr<uint64_t>(v);
668
669 if (v.value().type()==typeid(vector<float>))
670 return VecAsStr<float>(v);
671
672 if (v.value().type()==typeid(vector<double>))
673 return VecAsStr<double>(v);
674
675 ostringstream str;
676 str << hex << setfill('0') << "0x";
677 if (v.value().type()==typeid(Hex<uint16_t>))
678 str << setw(4) << v.as<Hex<uint16_t>>();
679
680 if (v.value().type()==typeid(Hex<uint32_t>))
681 str << setw(8) << v.as<Hex<uint32_t>>();
682
683 if (v.value().type()==typeid(Hex<uint64_t>))
684 str << setw(16) << v.as<Hex<uint64_t>>();
685
686 return str.str();
687}
688
689// --------------------------------------------------------------------------
690//
691//!
692//
693void Configuration::PrintOptions() const
694{
695 cout << "Options propagated to program:" << endl;
696
697 int maxlen = 0;
698 for (map<string,po::variable_value>::const_iterator m=fVariables.begin();
699 m!=fVariables.end(); m++)
700 Max(maxlen, m->first.length());
701
702 cout.setf(ios_base::left);
703
704 // =============> Implement prining of options in use
705 for (map<string,po::variable_value>::const_iterator m=fVariables.begin();
706 m!=fVariables.end(); m++)
707 {
708 const po::variable_value &v = m->second;
709
710 ostringstream str;
711
712 if (v.value().type()==typeid(bool))
713 str << " bool";
714 if (v.value().type()==typeid(string))
715 str << " string";
716 if (v.value().type()==typeid(int16_t))
717 str << " int16_t";
718 if (v.value().type()==typeid(int32_t))
719 str << " int32_t";
720 if (v.value().type()==typeid(int64_t))
721 str << " int64_t";
722 if (v.value().type()==typeid(uint16_t))
723 str << " uint16_t";
724 if (v.value().type()==typeid(uint32_t))
725 str << " uint32_t";
726 if (v.value().type()==typeid(uint64_t))
727 str << " uint64_t";
728 if (v.value().type()==typeid(float))
729 str << " float";
730 if (v.value().type()==typeid(double))
731 str << " double";
732 if (v.value().type()==typeid(Hex<uint16_t>))
733 str << " Hex<uint16_t>";
734 if (v.value().type()==typeid(Hex<uint32_t>))
735 str << " Hex<uint32_t>";
736 if (v.value().type()==typeid(Hex<uint64_t>))
737 str << " Hex<uint64_t>";
738 if (v.value().type()==typeid(vector<string>))
739 str << " vector<string>";
740 if (v.value().type()==typeid(vector<int16_t>))
741 str << " vector<int16_t>";
742 if (v.value().type()==typeid(vector<int32_t>))
743 str << " vector<int32_t>";
744 if (v.value().type()==typeid(vector<int64_t>))
745 str << " vector<int64_t>";
746 if (v.value().type()==typeid(vector<uint16_t>))
747 str << " vector<uint16_t>";
748 if (v.value().type()==typeid(vector<uint32_t>))
749 str << " vector<uint32_t>";
750 if (v.value().type()==typeid(vector<uint64_t>))
751 str << " vector<uint64_t>";
752 if (v.value().type()==typeid(vector<float>))
753 str << " vector<float>";
754 if (v.value().type()==typeid(vector<double>))
755 str << " vector<double>";
756
757 if (str.str().empty())
758 str << " unknown[" << v.value().type().name() << "]";
759
760 const string var = VarAsStr(v);
761 cout << setw(maxlen) << m->first;
762 if (!var.empty())
763 cout << " = ";
764 cout << var << " #" << str.str();
765
766 if (v.defaulted())
767 cout << " [default]";
768 if (v.empty())
769 cout << " [empty]";
770
771 cout << endl;
772 }
773
774 cout << endl;
775}
776
777// --------------------------------------------------------------------------
778//
779//!
780//
781void Configuration::PrintUnknown(const vector<string> &vec, int steps) const
782{
783 for (vector<string>::const_iterator v=vec.begin(); v<vec.end(); v+=steps)
784 cout << " " << *v << endl;
785 cout << endl;
786}
787
788multimap<string, string> Configuration::GetOptions() const
789{
790 multimap<string,string> rc;
791
792 for (map<string,po::variable_value>::const_iterator m=fVariables.begin();
793 m!=fVariables.end(); m++)
794 rc.insert(make_pair(m->first, VarAsStr(m->second)));
795
796 return rc;
797}
798
799// --------------------------------------------------------------------------
800//
801//!
802//
803void Configuration::PrintUnknown() const
804{
805 if (fUnknownCommandline.size())
806 {
807 cout << "Unknown commandline options:" << endl;
808 PrintUnknown(fUnknownCommandline);
809 }
810
811 if (fUnknownConfigfile.size())
812 {
813 cout << "Unknown options in configfile:" << endl;
814 PrintUnknown(fUnknownConfigfile, 2);
815 }
816
817 if (fUnknownEnvironment.size())
818 {
819 cout << "Unknown environment variables:" << endl;
820 PrintUnknown(fUnknownEnvironment);
821 }
822
823 if (fUnknownDatabase.size())
824 {
825 cout << "Unknown database entry:" << endl;
826 PrintUnknown(fUnknownDatabase);
827 }
828}
829
830// --------------------------------------------------------------------------
831//
832//!
833//
834void Configuration::AddOptionsCommandline(const po::options_description &cl, bool visible)
835{
836 fOptionsCommandline[visible].add(cl);
837}
838
839// --------------------------------------------------------------------------
840//
841//!
842//
843void Configuration::AddOptionsConfigfile(const po::options_description &cf, bool visible)
844{
845 fOptionsConfigfile[visible].add(cf);
846}
847
848// --------------------------------------------------------------------------
849//
850//!
851//
852void Configuration::AddOptionsEnvironment(const po::options_description &env, bool visible)
853{
854 fOptionsEnvironment[visible].add(env);
855}
856
857// --------------------------------------------------------------------------
858//
859//!
860//
861void Configuration::AddOptionsDatabase(const po::options_description &db, bool visible)
862{
863 fOptionsDatabase[visible].add(db);
864}
865
866// --------------------------------------------------------------------------
867//
868//!
869//
870void Configuration::SetArgumentPositions(const po::positional_options_description &desc)
871{
872 fArgumentPositions = desc;
873}
874
875// --------------------------------------------------------------------------
876//
877//!
878//
879void Configuration::SetNameMapper(const function<string(string)> &func)
880{
881 fNameMapper = func;
882}
883
884void Configuration::SetNameMapper()
885{
886 fNameMapper = bind1st(mem_fun(&Configuration::DefaultMapper), this);
887}
888
889void Configuration::SetPrintUsage(const function<void(void)> &func)
890{
891 fPrintUsage = func;
892}
893
894void Configuration::SetPrintUsage()
895{
896 fPrintUsage = bind(&Configuration::PrintUsage, this);
897}
898
899void Configuration::SetPrintVersion(const function<void(const string&)> &func)
900{
901 fPrintVersion = func;
902}
903
904void Configuration::SetPrintVersion()
905{
906 fPrintVersion = function<void(const string&)>();
907}
908
909// --------------------------------------------------------------------------
910//
911//!
912//! The idea of the Parse() memeber-function is to parse the command-line,
913//! the configuration files, the databse and the environment and return
914//! a proper combined result.
915//!
916//! In details the following actions are performed in the given order:
917//!
918//! - (0) Init local variables with the list of options described by the
919//! data members.
920//! - (1) Reset the data members fPriorityFile, fDefaultFile, fDatabase
921//! - (2) Parse the command line
922//! - (3) Check for \b --help* command-line options and performe
923//! corresponding action
924//! - (4) Check for \b --print and \b --print-all and perform corresponding
925//! action
926//! - (5) Read and parse the global configuration file, which is compiled
927//! from the path corresponding to the argument given in the
928//! constructor + "/fact++.rc", unrecognized options are always
929//! allowed. Note that in contradiction to all other options
930//! the options in this file are not checked at all. Hence,
931//! typos might stay unnoticed.
932//! - (6) Read and parse the default configuration file, which is either
933//! given by the default name or the \b --default command-line
934//! option. The default name is compiled from the argument
935//! given to the constructor and ".rc". If the file-name is
936//! identical to the default (no command-line option given)
937//! a missing configuration file is no error. Depending on
938//! the \b --dont-check and \b --dont-check-files options,
939//! unrecognized options in the file throw an exception or not.
940//! - (7) Check for \b --print-default and \b --print-all and perform
941//! corresponding action
942//! - (8) Read and parse the priority configuration file, which must be given
943//! by the \b --config or \b -C command-line option or a
944//! corresponding entry in the default-configuration file.
945//! If an option on the command-line and the in the configuration
946//! file exists, the command-line option has priority.
947//! If none is given, no priority file is read. Depending on
948//! the \b --dont-check and \b --dont-check-files options,
949//! unrecognized options in the file throw an exception or not.
950//! - (9) Check for \b --print-config and \b --print-all and perform
951//! corresponding action
952//! - (10) Retrieve options from the database according to the
953//! options \b --database and \b --no-database. Note that
954//! options given on the command-line have highest priority.
955//! The second priority is the priority-configuration file.
956//! The options from the default configuration-file have
957//! lowest priority.
958//! - (11) Check for \b --print-database and \b --print-all and perform
959//! corresponding action
960//! - (12) Parse the environment options.
961//! - (13) Check for \b --print-environment and \b --print-all and perform
962//! corresponding action
963//! - (14) Compile the final result. The priority of the options is (in
964//! decreasing order): command-line options, options from the
965//! priority configuration file, options from the database,
966//! options from the default configuration-file and options
967//! from the environment.
968//! - (15) Find all options which were found and flagged as unrecognized,
969//! because they are not in the user-defined list of described
970//! options, are collected and stored in the corresponding
971//! data-members.
972//! - (16) Find all options which where registered with wildcards and
973//! store the list in fWildcardOptions.
974//! - (17) Before the function returns it check for \b --print-options
975//! and \b --print-unknown and performs the corresponding actions.
976//!
977//!
978//! @param argc,argv
979//! arguments passed to <B>main(int argc, char **argv)</B>
980//!
981//! @returns
982//! A reference to the list with the resulting options with their
983//! values.
984//!
985//! @todo
986//! - describe the exceptions
987//! - describe what happens in a more general way
988//! - print a waring when no default coonfig file is read
989//! - proper handling and error messages if files not available
990//
991const po::variables_map &Configuration::Parse(int argc, const char **argv, const std::function<void()> &PrintHelp)
992{
993 const po::positional_options_description &opt_positional = fArgumentPositions;
994
995 // ------------------------ (0) --------------------------
996#ifdef DEBUG
997 cout << "--0--" << endl;
998#endif
999
1000 po::options_description opt_commandline;
1001 po::options_description opt_configfile;
1002 po::options_description opt_environment;
1003 po::options_description opt_database;
1004
1005 for (int i=0; i<2; i++)
1006 {
1007 opt_commandline.add(fOptionsCommandline[i]);
1008 opt_configfile.add(fOptionsConfigfile[i]);
1009 opt_environment.add(fOptionsEnvironment[i]);
1010 opt_database.add(fOptionsDatabase[i]);
1011 }
1012
1013 // ------------------------ (1) --------------------------
1014#ifdef DEBUG
1015 cout << "--1--" << endl;
1016#endif
1017
1018 fPriorityFile = "";
1019 fDefaultFile = "";
1020 fDatabase = "";
1021
1022 // ------------------------ (2) --------------------------
1023#ifdef DEBUG
1024 cout << "--2--" << endl;
1025#endif
1026
1027 po::command_line_parser parser(argc, const_cast<char**>(argv));
1028 parser.options(opt_commandline);
1029 parser.positional(opt_positional);
1030 parser.style(style::unix_style&~style::allow_guessing);
1031 //parser.allow_unregistered();
1032
1033 const po::parsed_options parsed_commandline = parser.run();
1034
1035 // ------------------------ (3) --------------------------
1036#ifdef DEBUG
1037 cout << "--3--" << endl;
1038#endif
1039
1040 po::variables_map getfiles;
1041 po::store(parsed_commandline, getfiles);
1042
1043 if (getfiles.count("version"))
1044 PrintVersion();
1045 if (getfiles.count("help"))
1046 {
1047 fPrintUsage();
1048 cout <<
1049 "Options:\n"
1050 "The following describes the available commandline options. "
1051 "For further details on how command line option are parsed "
1052 "and in which order which configuration sources are accessed "
1053 "please refer to the class reference of the Configuration class." << endl;
1054 cout << fOptionsCommandline[kVisible] << endl;
1055 }
1056 if (getfiles.count("help-config"))
1057 cout << fOptionsConfigfile[kVisible] << endl;
1058 if (getfiles.count("help-env"))
1059 cout << fOptionsEnvironment[kVisible] << endl;
1060 if (getfiles.count("help-database"))
1061 cout << fOptionsDatabase[kVisible] << endl;
1062
1063
1064
1065 // ------------------------ (4) --------------------------
1066#ifdef DEBUG
1067 cout << "--4--" << endl;
1068#endif
1069
1070 if (getfiles.count("print") || getfiles.count("print-all"))
1071 {
1072 cout << endl << "Parsed commandline options:" << endl;
1073 PrintParsed(parsed_commandline);
1074 cout << endl;
1075 }
1076
1077 if (getfiles.count("help") || getfiles.count("help-config") ||
1078 getfiles.count("help-env") || getfiles.count("help-database"))
1079 {
1080 if (PrintHelp)
1081 PrintHelp();
1082 }
1083
1084 // ------------------------ (5) --------------------------
1085#ifdef DEBUG
1086 cout << "--5--" << endl;
1087#endif
1088
1089 const boost::filesystem::path path(GetName());
1090 const string globalfile = (path.parent_path()/boost::filesystem::path("fact++.rc")).string();
1091
1092 cerr << "Reading global options from '" << globalfile << "'." << endl;
1093
1094 ifstream gfile(globalfile.c_str());
1095 // ===> FIXME: Proper handling of missing file or wrong file name
1096 const po::parsed_options parsed_globalfile =
1097 !gfile ?
1098 po::parsed_options(&opt_configfile) :
1099 po::parse_config_file<char>(gfile, opt_configfile, true);
1100
1101 // ------------------------ (6) --------------------------
1102#ifdef DEBUG
1103 cout << "--6--" << endl;
1104#endif
1105
1106 // Get default file from command line
1107 if (getfiles.count("default"))
1108 {
1109 fDefaultFile = getfiles["default"].as<string>();
1110 cerr << "Reading default options from '" << fDefaultFile << "'." << endl;
1111 }
1112
1113 const bool checkf = !getfiles.count("dont-check-files") && !getfiles.count("dont-check");
1114 const bool defaulted = getfiles.count("default") && getfiles["default"].defaulted();
1115 //const bool exists = boost::filesystem::exists(fDefaultFile);
1116
1117 ifstream indef(fDefaultFile.c_str());
1118 // ===> FIXME: Proper handling of missing file or wrong file name
1119 const po::parsed_options parsed_defaultfile =
1120 !indef && defaulted ?
1121 po::parsed_options(&opt_configfile) :
1122 po::parse_config_file<char>(indef, opt_configfile, !checkf);
1123
1124 // ------------------------ (7) --------------------------
1125#ifdef DEBUG
1126 cout << "--7--" << endl;
1127#endif
1128
1129 if (getfiles.count("print-default") || getfiles.count("print-all"))
1130 {
1131 if (!indef.is_open() && defaulted)
1132 cout << "No configuration file by --default option specified." << endl;
1133 else
1134 {
1135 cout << endl << "Parsed options from '" << fDefaultFile << "':" << endl;
1136 PrintParsed(parsed_defaultfile);
1137 cout << endl;
1138 }
1139 }
1140
1141 po::store(parsed_defaultfile, getfiles);
1142
1143 // ------------------------ (8) --------------------------
1144#ifdef DEBUG
1145 cout << "--8--" << endl;
1146#endif
1147
1148 // Get priority from commandline(1), defaultfile(2)
1149 if (getfiles.count("config"))
1150 {
1151 fPriorityFile = getfiles["config"].as<string>();
1152 cerr << "Reading config options from '" << fPriorityFile << "'." << endl;
1153 }
1154
1155 ifstream inpri(fPriorityFile.c_str());
1156 // ===> FIXME: Proper handling of missing file or wrong file name
1157 const po::parsed_options parsed_priorityfile =
1158 fPriorityFile.empty() ? po::parsed_options(&opt_configfile) :
1159 po::parse_config_file<char>(inpri, opt_configfile, !checkf);
1160
1161 // ------------------------ (9) --------------------------
1162#ifdef DEBUG
1163 cout << "--9--" << endl;
1164#endif
1165
1166 if (getfiles.count("print-config") || getfiles.count("print-all"))
1167 {
1168 if (fPriorityFile.empty())
1169 cout << "No configuration file by --config option specified." << endl;
1170 else
1171 {
1172 cout << endl << "Parsed options from '" << fPriorityFile << "':" << endl;
1173 PrintParsed(parsed_priorityfile);
1174 cout << endl;
1175 }
1176 }
1177
1178 // ------------------------ (10) -------------------------
1179#ifdef DEBUG
1180 cout << "--10--" << endl;
1181#endif
1182
1183 po::variables_map getdatabase;
1184 po::store(parsed_commandline, getdatabase);
1185 po::store(parsed_priorityfile, getdatabase);
1186 po::store(parsed_defaultfile, getdatabase);
1187 po::store(parsed_globalfile, getdatabase);
1188
1189 if (getdatabase.count("database") && !getdatabase.count("no-database"))
1190 {
1191 fDatabase = getdatabase["database"].as<string>();
1192 cerr << "Requesting options from database for '" << fName << "'" << endl;
1193 }
1194
1195 const bool checkdb = !getdatabase.count("dont-check-database") && !getdatabase.count("dont-check");
1196
1197 const po::parsed_options parsed_database =
1198 fDatabase.empty() ? po::parsed_options(&opt_database) :
1199#if BOOST_VERSION < 104600
1200 parse_database(path.filename(), fDatabase, opt_database, !checkdb);
1201#else
1202 parse_database(path.filename().string(), fDatabase, opt_database, !checkdb);
1203#endif
1204 // ------------------------ (11) -------------------------
1205#ifdef DEBUG
1206 cout << "--11--" << endl;
1207#endif
1208
1209 if (getfiles.count("print-database") || getfiles.count("print-all"))
1210 {
1211 if (fDatabase.empty())
1212 cout << "No database access requested." << endl;
1213 else
1214 {
1215 cout << endl << "Options received from '" << fDatabase << "':" << endl;
1216 PrintParsed(parsed_database);
1217 cout << endl;
1218 }
1219 }
1220
1221 // ------------------------ (12) -------------------------
1222#ifdef DEBUG
1223 cout << "--12--" << endl;
1224#endif
1225
1226 const po::parsed_options parsed_environment = po::parse_environment(opt_environment, fNameMapper);
1227
1228 // ------------------------ (13) -------------------------
1229#ifdef DEBUG
1230 cout << "--13--" << endl;
1231#endif
1232
1233 if (getfiles.count("print-environment"))
1234 {
1235 cout << "Parsed options from environment:" << endl;
1236 PrintParsed(parsed_environment);
1237 cout << endl;
1238 }
1239
1240 // ------------------------ (14) -------------------------
1241#ifdef DEBUG
1242 cout << "--14--" << endl;
1243#endif
1244
1245 po::variables_map result;
1246 po::store(parsed_commandline, result);
1247 po::store(parsed_priorityfile, result);
1248 po::store(parsed_database, result);
1249 po::store(parsed_defaultfile, result);
1250 po::store(parsed_globalfile, result);
1251 po::store(parsed_environment, result);
1252 po::notify(result);
1253
1254 fVariables = result;
1255
1256 // ------------------------ (15) -------------------------
1257#ifdef DEBUG
1258 cout << "--15--" << endl;
1259#endif
1260
1261 const vector<string> unknown0 = collect_unrecognized(parsed_globalfile.options, po::exclude_positional);
1262 const vector<string> unknown1 = collect_unrecognized(parsed_defaultfile.options, po::exclude_positional);
1263 const vector<string> unknown2 = collect_unrecognized(parsed_priorityfile.options, po::exclude_positional);
1264
1265 fUnknownConfigfile.clear();
1266 fUnknownConfigfile.insert(fUnknownConfigfile.end(), unknown0.begin(), unknown0.end());
1267 fUnknownConfigfile.insert(fUnknownConfigfile.end(), unknown1.begin(), unknown1.end());
1268 fUnknownConfigfile.insert(fUnknownConfigfile.end(), unknown2.begin(), unknown2.end());
1269
1270 fUnknownCommandline = collect_unrecognized(parsed_commandline.options, po::exclude_positional);
1271 fUnknownEnvironment = collect_unrecognized(parsed_environment.options, po::exclude_positional);
1272 fUnknownDatabase = collect_unrecognized(parsed_database.options, po::exclude_positional);
1273
1274 // ------------------------ (16) -------------------------
1275#ifdef DEBUG
1276 cout << "--16--" << endl;
1277#endif
1278
1279 CreateWildcardOptions();
1280
1281 // ------------------------ (17) -------------------------
1282#ifdef DEBUG
1283 cout << "--17--" << endl;
1284#endif
1285
1286 if (result.count("print-options"))
1287 PrintOptions();
1288
1289 if (result.count("print-wildcards"))
1290 PrintWildcardOptions();
1291
1292 if (result.count("print-unknown"))
1293 PrintUnknown();
1294
1295#ifdef DEBUG
1296 cout << "------" << endl;
1297#endif
1298
1299 return fVariables;
1300}
1301
1302bool Configuration::DoParse(int argc, const char **argv, const std::function<void()> &PrintHelp)
1303{
1304 try
1305 {
1306 Parse(argc, argv, PrintHelp);
1307 }
1308#if BOOST_VERSION > 104000
1309 catch (po::multiple_occurrences &e)
1310 {
1311 cerr << "Program options invalid due to: " << e.what() << " of '" << e.get_option_name() << "'." << endl;
1312 return false;
1313 }
1314#endif
1315 catch (exception& e)
1316 {
1317 cerr << "Program options invalid due to: " << e.what() << endl;
1318 return false;
1319 }
1320
1321 return !HasVersion() && !HasPrint() && !HasHelp();
1322}
1323
1324// --------------------------------------------------------------------------
1325//
1326//! Create a list of all options which were registered using wildcards
1327//!
1328void Configuration::CreateWildcardOptions()
1329{
1330 po::options_description opts;
1331
1332 for (int i=0; i<2; i++)
1333 {
1334 opts.add(fOptionsCommandline[i]);
1335 opts.add(fOptionsConfigfile[i]);
1336 opts.add(fOptionsEnvironment[i]);
1337 opts.add(fOptionsDatabase[i]);
1338 }
1339
1340 fWildcardOptions.clear();
1341
1342 typedef map<string,po::variable_value> Vars;
1343 typedef vector<boost::shared_ptr<po::option_description>> Descs;
1344
1345 const Descs &desc = opts.options();
1346
1347 for (Vars::const_iterator io=fVariables.begin(); io!=fVariables.end(); io++)
1348 {
1349 for (Descs::const_iterator id=desc.begin(); id!=desc.end(); id++)
1350#if BOOST_VERSION > 104000
1351 if ((*id)->match(io->first, false, false, false)==po::option_description::approximate_match)
1352#else
1353 if ((*id)->match(io->first, false)==po::option_description::approximate_match)
1354#endif
1355 fWildcardOptions[io->first] = (*id)->long_name();
1356 }
1357}
1358
1359// --------------------------------------------------------------------------
1360//
1361//! Print a list of all options which were registered using wildcards and
1362//! have not be registered subsequently by access.
1363//!
1364void Configuration::PrintWildcardOptions() const
1365{
1366 cout << "Options registered with wildcards and not yet accessed:" << endl;
1367
1368 size_t max = 0;
1369 for (map<string,string>::const_iterator it=fWildcardOptions.begin(); it!=fWildcardOptions.end(); it++)
1370 if (it->second.length()>max)
1371 max = it->second.length();
1372
1373 cout.setf(ios_base::left);
1374 for (map<string,string>::const_iterator it=fWildcardOptions.begin(); it!=fWildcardOptions.end(); it++)
1375 cout << setw(max+1) << it->second << " : " << it->first <<endl;
1376}
1377
1378// --------------------------------------------------------------------------
1379//
1380//! Removes /.libs/lt- from a path or just lt- from the filename.
1381//!
1382//! @param src
1383//! input path with filename
1384//! @returns
1385//! path cleaned from libtool extensions
1386//!
1387string Configuration::UnLibToolize(const string &src) const
1388{
1389 const boost::filesystem::path path(src);
1390
1391 string pname = path.parent_path().string();
1392#if BOOST_VERSION < 104600
1393 string fname = path.filename();
1394#else
1395 string fname = path.filename().string();
1396#endif
1397
1398 // If the filename starts with "lt-" remove it from the name
1399 if (fname.substr(0, 3)=="lt-")
1400 fname = fname.substr(3);
1401
1402 string pwd;
1403 // If no directory is contained determine the current directory
1404 if (pname.empty())
1405 pname = boost::filesystem::current_path().string();
1406
1407 // If the directory is relative and just ".libs" forget about it
1408 if (pname==".libs")
1409 return fname;
1410
1411
1412 // Check if the directory is long enough to contain "/.libs"
1413 if (pname.length()>=6)
1414 {
1415 // If the directory ends with "/.libs", remove it
1416 const size_t pos = pname.length()-6;
1417 if (pname.substr(pos)=="/.libs")
1418 pname = pname.substr(0, pos);
1419 }
1420
1421 // If the path is the local path do not return the path-name
1422 if (pname==boost::filesystem::current_path().string())
1423 return fname;
1424
1425 return pname+'/'+fname;
1426}
1427
1428// --------------------------------------------------------------------------
1429//
1430//! Print version information about the program and package.
1431//!
1432//! The program name is taken from fName. If a leading "lt-" is found,
1433//! it is removed. This is useful if the program was build and run
1434//! using libtool.
1435//!
1436//! The package name is taken from the define PACKAGE_STRING. If it is
1437//! not defined (like automatically done by autoconf) no package information
1438//! is printed. The same is true for PACKAGE_URL and PACKAGE_BUGREPORT.
1439//!
1440//! From help2man:
1441//!
1442//! The first line of the --version information is assumed to be in one
1443//! of the following formats:
1444//!
1445//! \verbatim
1446//! - <version>
1447//! - <program> <version>
1448//! - {GNU,Free} <program> <version>
1449//! - <program> ({GNU,Free} <package>) <version>
1450//! - <program> - {GNU,Free} <package> <version>
1451//! \endverbatim
1452//!
1453//! and separated from any copyright/author details by a blank line.
1454//!
1455//! Handle multi-line bug reporting sections of the form:
1456//!
1457//! \verbatim
1458//! - Report <program> bugs to <addr>
1459//! - GNU <package> home page: <url>
1460//! - ...
1461//! \endverbatim
1462//!
1463//! @param name
1464//! name of the program (usually argv[0]).
1465//!
1466void Configuration::PrintVersion() const
1467{
1468#ifndef PACKAGE_STRING
1469#define PACKAGE_STRING ""
1470#endif
1471
1472#ifndef PACKAGE_URL
1473#define PACKAGE_URL ""
1474#endif
1475
1476#ifndef PACKAGE_BUGREPORT
1477#define PACKAGE_BUGREPORT ""
1478#endif
1479
1480 if (fPrintVersion)
1481 {
1482 fPrintVersion(fName);
1483 return;
1484 }
1485
1486#if BOOST_VERSION < 104600
1487 const std::string n = boost::filesystem::path(fName).filename();
1488#else
1489 const std::string n = boost::filesystem::path(fName).filename().string();
1490#endif
1491
1492 const string name = PACKAGE_STRING;
1493 const string bugs = PACKAGE_BUGREPORT;
1494 const string url = PACKAGE_URL;
1495
1496 cout << n;
1497 if (!name.empty())
1498 cout << " - " << name;
1499 cout <<
1500 "\n\n"
1501 "Written by Thomas Bretz et al.\n"
1502 "\n";
1503 if (!bugs.empty())
1504 cout << "Report bugs to <" << bugs << ">\n";
1505 if (!url.empty())
1506 cout << "Home page: " << url << "\n";
1507 cout <<
1508 "\n"
1509 "Copyright (C) 2011 by the FACT Collaboration.\n"
1510 "This is free software; see the source for copying conditions.\n"
1511 << std::endl;
1512}
Note: See TracBrowser for help on using the repository browser.