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

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