1 /* $Id: settings.hpp,v 1.50.4.1 2006/01/20 11:23:31 chfreund Exp $ */
2 
3 #ifndef  SETTINGS_HPP
4 #define  SETTINGS_HPP
5 
6 #include  <cstdio>
7 #include  <iostream>
8 #include "global.hpp"
9 
10 #ifdef USE_SDL
11 #include "serialize.hpp"
12 #endif
13 
14 /**********************************************************
15  *
16  * Settings.hpp
17  * Uwe Fabricius
18  * Version 3.00.01                              09.04.2005
19  *
20  **********************************************************
21  *
22  * Application example:
23  *
24  * --8<---<snip>----8<---<snip>----8<---<snip>---
25  * SettingDef Defs[] =
26  * {
27  *     SettingDef("family",      NULL, Setting::STRING, 1,  4, Setting::COMMA_SEPARATED, "help for \"family\"" ),
28  *     SettingDef("age",         NULL, Setting::INT,    0, -1, Setting::COMMA_SEPARATED, NULL ),
29  *     SettingDef("intelligent",  "i", Setting::BOOL,   1,  4, 0, NULL ),
30  *     SettingDef("athome",      NULL, Setting::STRING, 0, -1, 0, NULL ),
31  *     SettingDef()
32  * };
33  *
34  * int main( int nargs, const char **pargs )
35  * {
36  *     SettingDataBase settings;
37  *
38  *     settings.readSettings( Defs, "config.test", true );
39  *     settings.readSettings( Defs, pargs, nargs, true, true );
40  *     settings.finalCheck( Defs );
41  *
42  *     for( int i = 0; Defs[i].m_IDString; i++ ) {
43  *         Setting* s = settings.getSetting( Defs[i].m_IDString );
44  *         if( s ) {
45  *             cout << "\033[1m" << s->getIDString() << "\033[0m\n";
46  *             for( int j = 0; j < s->getNumParameters(); j++ ) {
47  *                 switch( s->getType() ) {
48  *                     case Setting::BOOL :
49  *                         cout << "   [" << j << "]: " << s->getBool(j) << endl;
50  *                         break;
51  *                     case Setting::INT :
52  *                         cout << "   [" << j << "]: " << s->getInt(j) << endl;
53  *                         break;
54  *                     case Setting::REAL :
55  *                         cout << "   [" << j << "]: " << s->getReal(j) << endl;
56  *                         break;
57  *                     case Setting::STRING :
58  *                         cout << "   [" << j << "]: " << s->getString(j) << endl;
59  *                         break;
60  *                     case Setting::FLAG :
61  *                     default :
62  *                         break;
63  *                 }
64  *             }
65  *         } else {
66  *             cout << "\033[1m" << Defs[i].m_IDString << "\033[0m : not specified\n";
67  *         }
68  *     }
69  *     return true;
70  * }
71  * --8<---<snip>----8<---<snip>----8<---<snip>---
72  *
73  * sample configuration file:
74  *
75  * --8<---<snip>----8<---<snip>----8<---<snip>---
76  * ## part of the family
77  * family = "homer, with #" lisa,bart # marge, maggy
78  *  age    45  40 12
79  * intelligent= false yes 0
80  * athome LISA MARGE
81  * --8<---<snip>----8<---<snip>----8<---<snip>---
82  *
83  **********************************************************/
84 
85 #ifdef DBG
86 #ifndef _WARNINGS_
87 #define _WARNINGS_
88 #endif
89 #endif
90 
91 /**********************************************************/
92 
93 // definition of directive strings
94 #ifndef  SETTING_DB_DIRECTIVES
95 #define  SETTING_DB_DIRECTIVES
96 #define  SETTING_DB_DIRECTIVE_SECTION_IN   ">>section_enter<<"
97 #define  SETTING_DB_DIRECTIVE_SECTION_OUT  ">>section_leave<<"
98 #define  SETTING_DB_DIRECTIVE_END          ">>end<<"
99 #endif // SETTING_DB_DIRECTIVES
100 
101 /**********************************************************/
102 
103 using namespace std;
104 
105 /**********************************************************/
106 
107 class SettingDef;
108 std::ostream& operator << ( std::ostream&, const SettingDef& );
109 
110 /**********************************************************/
111 
112 class FlagClass {};
113 
114 class Setting;
115 template <typename T>
116 struct TypeToConst {
117 	enum { FLAG  = 0, BOOL, INT, REAL, STRING };
118 	enum { value = FLAG }; };
119 
120 template <>
121 struct TypeToConst<bool> {
122 	enum { value = TypeToConst<FlagClass>::BOOL }; };
123 
124 template <>
125 struct TypeToConst<int> {
126 	enum { value = TypeToConst<FlagClass>::INT }; };
127 
128 template <>
129 struct TypeToConst<real> {
130 	enum { value = TypeToConst<FlagClass>::REAL }; };
131 
132 template <>
133 struct TypeToConst<char*> {
134 	enum { value = TypeToConst<FlagClass>::STRING }; };
135 
136 template <>
137 struct TypeToConst<const char*> {
138 	enum { value = TypeToConst<FlagClass>::STRING }; };
139 
140 
141 /**********************************************************/
142 
143 //! class to store <b>one</b> setting including its parameters
144 
145 /*! This class represents one single setting beloning to class
146  *  SettingDataBase, consisting of an ID-string and the setting's
147  *  parameters. The parameters may be of type int, bool, real,
148  *  string, or simply used as a flag, but only one type per setting.
149  *  A setting is created from an object of type class SettingDef.
150  *
151  *  <br><br>
152  *  <b>PATH EXPANSION</b> <br>
153  *  A setting of type STRING might represent a certain path in the file
154  *  system. Therefore it may contain start with the abreviation '~' for
155  *  the home path of the current user or for example "~homer" the home
156  *  path of user "homer". Using such a path inside your program to open
157  *  a file will not work, since the '~' or "~user" is expanded by the
158  *  shell, if you use it in the terminal, but there is no shell inside
159  *  your programm. You have to expand it manually in your program. class
160  *  Setting provides this expansion with the methods expandPathString
161  *  and expandAllPathStrings. Setting::expandPathString expands a single
162  *  specified string in the setting, Setting::expandAllPathStrings expands
163  *  all strings in the setting.
164  */
165 class Setting
166 {
167 	public:
168 
169 		//! constants for the types of the setting's parameters
170 		enum type { FLAG   = TypeToConst<FlagClass>::value, //!< a simple flag
171 		            BOOL   = TypeToConst<bool>::value,      //!< boolean value, possible parameter strings are:
172 		                                                    //!  "true", "false", "yes", "no", "1", "0"
173 		            INT    = TypeToConst<int>::value,       //!< integer value
174 		            REAL   = TypeToConst<real>::value,      //!< real value
175 		            STRING = TypeToConst<char*>::value,     //!< a string
176 		            NUM_TYPES };
177 
178 		//! some flags for the treatment of the setting
179 
180 		/*! some flags for the treatment of the setting
181 		 *   - COMMA_SEPARATED: The parameters <b>can</b> be separated
182 		 *     with commas, i.e. "homer,lisa,bart" will be recognised
183 		 *     as the three parameters "homer","lisa","bart". The
184 		 *     specification "homer lisa bart" is understood nevertheless.
185 		 *   - EXPLICIT_ASSIGNMENT:  The parameters must be assigned
186 		 *     explicitly with a "=", where by spaces may separate the
187 		 *     "=" from the setting identifier and the parameters.
188 		 *   - MANDATORY: This setting is marked as mandatory. If all
189 		 *     settings have been loaded, the function
190 		 *     SettingDataBase::finalCheck() will print an error message
191 		 *     for each mandatory setting that has not been set, and
192 		 *     return false.
193 		 *   - COMMAND_LINE_ONLY, CONFIG_FILE_ONLY: Allows restrictions
194 		 *     concerning the source of a setting. (For example it does
195 		 *     not make sense to allow the setting of a configuration file
196 		 *     to specify the name or path of exactly that config file).
197 		 */
198 		enum { COMMA_SEPARATED     = 1<<0,
199 		       EXPLICIT_ASSIGNMENT = 1<<1,
200 		       MANDATORY           = 1<<2,
201 		       COMMAND_LINE_ONLY   = 1<<3,
202 		       CONFIG_FILE_ONLY    = 1<<4 };
203 
204 		//! creates a Setting object from a object of type SettingDef
205 		Setting( const SettingDef& settingdef,
206 		               bool        verbose = true );
207 		//! creates an empty setting
208 
209 		/*! This constructor creates an empty setting, in particular
210 		 *  a setting without an ID string. Therefore this constructor
211 		 *  should only be used, if the Setting object is filled
212 		 *  immediately with sensible data, e.g. in a deserialization.
213 		 */
214 		Setting( bool verbose = true );
215 		~Setting();
216 
217 		//! \name data read access
218 		//@{
219 		//! returns the number of present parameters
getNumParameters() const220 		int  getNumParameters() const { return m_nParameters; }
221 		//! returns the type of the setting (see the enumeration types)
getType() const222 		type getType() const { return m_Type; }
223 		//! returns the boolean nr. i in the setting
224 		bool  getBool( const int i = 0 ) const;
225 		//! returns the integer nr. i in the setting
226 		int   getInt( const int i = 0 ) const;
227 		//! returns the string nr. i in the setting
228 		char* getString( const int i = 0 ) const;
229 		//! returns the real nr. i in the setting
230 		real getReal( const int i = 0 ) const;
231 		//! direct access to the array
getIDString() const232 		char*  getIDString()  const { return m_IDString; }
233 		//! direct access to the array
getBoolPtr() const234 		bool*  getBoolPtr()   const { return m_bool; }
235 		//! direct access to the array
getIntPtr() const236 		int*   getIntPtr()    const { return m_int; }
237 		//! direct access to the array
getStringPtr() const238 		char** getStringPtr() const { return (char **const)m_string; }
239 		//! direct access to the array
getRealPtr() const240 		real* getRealPtr()  const { return m_real; }
241 		//@}
242 
243 		//! \name data write access
244 		//@{
245 		//! sets one ore more parameter values in the setting
246 
247 		/*! This method allows to set one ore more parameter values
248 		 *  in this setting. As paramters there are passed: the address
249 		 *  of the new value(s), the zero-based index for the position
250 		 *  of the new value(s) inside the paramter list of this setting,
251 		 *  the number of values at \c values. The parameters \c index
252 		 *  and \c nvalues are optional and both have default value -1.
253 		 *  The following rules are valid for the interpretation of
254 		 *  these both parameters:
255 		 *
256 		 *  - \c nvalues < 0 (default) means that only \b one value is
257 		 *    passed at pointer \c value
258 		 *  - \c nvalues == 0 means that all parameter values of this
259 		 *    setting should be removed.
260 		 *  - \c nvalues > 0 means that \c nvalues items are read from
261 		 *    addresse \c values.
262 		 *  - \c index < 0 (default) means that the values are inserted
263 		 *    starting at index \c 0 \b and will replace all present
264 		 *    values.
265 		 *  - \c index >= 0 means that the values are inserted starting
266 		 *    at index \c index, \b but already present values at
267 		 *    positions, that are not touched by the new values, stay
268 		 *    unchanged.
269 		 *
270 		 *  In any case the resulting number of parameters present after
271 		 *  this operation is checked against the constrains of maximal
272 		 *  and minimal number of parameters in this setting.
273 		 *  Also note that the first parameter must be uniquely one of
274 		 *  the following types: FlagClass*, bool*, int*, real*. To
275 		 *  assure the correct template instantiation use explicit type
276 		 *  casts. If you get an error message concerning a not supported
277 		 *  type "FLAG", this may be a hint to a not properly instantiated
278 		 *  version of this function.
279 		 *  \param values pointer to one ore more values
280 		 *  \param index target index for the new values
281 		 *  \param nvalues number of values at address \c values
282 		 *  \return true, if the values could be set successfully,
283 		 *          otherwise false.
284 		 */
285 		template <typename T>
setValue(const T * const values,const int index=-1,const int nvalues=-1)286 		bool setValue( const T*  const values,
287 		               const int       index   = -1,
288 		               const int       nvalues = -1 ) {
289 			if( !processWriteAttempt((int)(TypeToConst<T>::value),
290 			                         values, index, nvalues) ) {
291 				return false;
292 			}
293 			return true;
294 		}
295 		//@}
296 
297 #ifdef USE_SDL
298 		//! \name (de)serialization (only working with #define USE_SDL)
299 		//@{
300 		Uint32 getSerializeBufferSize() const;
301 		void serialize( Uint8*& bufferPointer ) const;
302 		void deserialize( Uint8*& bufferPointer );
303 		bool deserialize( Uint8*& bufferPointer,
304 		                  const SettingDef* const defs );
305 		//@}
306 #endif
307 
308 		//! checks the consistency using the passed definitions
309 
310 		/*! This method checks the correctness of the setting's content
311 		 *  against its definition in the passed array. This is only
312 		 *  necessary, if the settings content was changed after its
313 		 *  creation from other routines than the ones that read
314 		 *  parameters from command line or configuration file, e.g
315 		 *  after a deserialization. Of course, like in all other
316 		 *  methods using a definition array, this array must be
317 		 *  terminated by an empty definition, too.
318 		 */
319 		bool checkConsistency( const SettingDef* const defs );
320 
321 		//! \name data manipulation
322 		//@{
323 		//! expands home path from '~'
324 
325 		/*! String settings often represent a file or directory path.
326 		 *  This path may be passed relatively to the home directory of
327 		 *  a user, abreviated by '~', more precise by "~/" for the
328 		 *  current user's home directory or "~homer/" for the home
329 		 *  directory of the user "homer". Setting::expandPathString
330 		 *  checks, if a string parameters starts with a '~' and tries
331 		 *  to expand the string using the full path of the according
332 		 *  home directory (so the string is changed irreversibly).
333 		 *  Therefore the method uses the content of the environment
334 		 *  variable "HOME" or the corresponding entry in "/etc/passwd".
335 		 *  The necessary functions are declared in the standard library
336 		 *  header "pwd.h". You can switch of this feature by
337 		 *  <i>#defining</i> NO_HOME_PATH_EXPANSION. Then the method can
338 		 *  still be called, but with no effect, and will return false
339 		 *  whenever the string starts with a '~'.
340 		 *  \param i index of the string in this setting, that should be
341 		 *         expanded
342 		 *  \return true, if the expansion was successfull. This also
343 		 *          includes the case that the string did not start with
344 		 *          a '~'. <br> false, if the string started with a '~'
345 		 *          but could not be expanded, including due to a defined
346 		 *          NO_HOME_PATH_EXPANSION.
347 		 */
348 		bool expandPathString( const int i = 0 );
349 
350 		//! expands home path form '~' in all strings
351 
352 		/*! This function expands all present strings in this setting.
353 		 *  So it is simply a loop of expandPathString(const int) over
354 		 *  all strings.
355 		 *  \return true, if all calls of expandPathString(const int)
356 		 *          returned true, otherwise false
357 		 */
358 		bool expandAllPathStrings();
359 		//@}
360 
361 	 // operators
362 		Setting& operator = ( const Setting& setting );
363 
364 
365 		//! \name reading parameters
366 		//@{
367 		// read parameters from one configuration line
368 		bool readParametersFromRawLine(       char* const line,
369 		                                const char* const FileName     = NULL,
370 		                                const int         LineNumber   =   -1,
371 		                                      bool* const pickySuccess = NULL );
372 		//@}
373 
374 
375 		//! \name administration routines for the linked list of Settings
376 		//@{
377 		Setting* append( const Setting* const pSetting );
378 		Setting* getLast() const;
getNext() const379 		Setting* getNext() const { return m_next; }
380 		//@}
381 
382 	protected:
383 
384 		//! separate the parameters in a line by 0 characters
385 		int separateParametersInRawLine(       char *const line,
386 		                                 const char *const FileName,
387 		                                 const int         LineNumber,
388 		                                       bool *const pickySuccess = NULL );
389 		//! read parameters of a setting in a prepared, i.e zero-separated line
390 		bool readParametersFromPreparedLine(       int         nStrings,
391 		                                     const char *const line,
392 		                                     const char *const FileName,
393 		                                     const int         LineNumber,
394 		                                           bool *const pickySuccess = NULL );
395 		//! expand a home directory path from '~'
396 		bool expandHomePath( char** path );
397 
398 	 // controlling data
399 		int         m_nParameters,  // # parameters
400 		            m_minNumParams, // min # parameters
401 		            m_maxNumParams, // max # parameters
402 					m_Attributes;
403 		type        m_Type;
404 		char       *m_IDString;
405 		bool        m_verbose;
406 	 // parameters of the setting
407 		bool       *m_bool;
408 		int        *m_int;
409 		real      *m_real;
410 		char      **m_string;
411 	 // pointer to the next setting
412 		Setting    *m_next;
413 
414 	private:
415 
416 		bool checkIndexRange( const int i,
417 		                      const char* const FunctionName,
418 		                      const void* const pointer ) const;
419 
420 		//! auxiliary function for write access
421 		bool processWriteAttempt( const int         settingtype,
422 		                          const void *const values,
423 		                                int         index,
424 		                                int         nvalues );
425 
426 		static const char* m_typeString[NUM_TYPES];
427 };
428 
429 /**********************************************************
430  * Class only needed as parameter for Setting::Setting(..)
431  **********************************************************/
432 
433 //! class representing the definition of a setting
434 
435 /*! This class is used in combination with class SettingDataBase.
436  *  In principal an object of this class only exists to get passed to
437  *  to the constructor of class Setting. The user of the settings
438  *  framework only will use this class to create an array of SettingDef
439  *  objects, that will be passed to the two versions of function
440  *  SettingDataBase::readSettings.
441  */
442 class SettingDef
443 {
444 	friend std::ostream& operator << ( std::ostream&, const SettingDef& );
445 
446 	public:
447 
448 		//! constructor
449 
450 		/*! Since all parameters have a default value, it is possible
451 		 *  to create a SettingDef object with the standard constructor.
452 		 *  This should only be used to create an empty object to
453 		 *  terminate the definition array, that is passed to the
454 		 *  functions SettingDataBase::readSettings.
455 		 *  \param IDString first identification string of a setting
456 		 *  \param alternative IDString first identification string of
457 		 *         a setting (optional)
458 		 *  \param Type the type of the setting (see documentation of
459 		 *         enumeration type Setting::type).
460 		 *  \param minNumParams minimal number of parameters, that should
461 		 *         be passed with this setting
462 		 *  \param minNumParams maximal number of parameters, that should
463 		 *         be passed with this setting. Default value -1 means
464 		 *         <i>unlimited</i>
465 		 *  \param attributes for the setting (combination of anonymous
466 		 *         enumeration constants in class Setting)
467 		 *  \param HelpString a string containing the text, that should
468 		 *         be printed out as documentation of the accordingly
469 		 *         generated Setting (also see SettingDataBase::printHelp)
470 		 */
471 		SettingDef( const char *const    IDString     = NULL,
472 		            const char *const    altIDString  = NULL,
473 		            const Setting::type  Type         = Setting::FLAG,
474 		            const int            minNumParams = -1,
475 		            const int            maxNumParams = -1,
476 		            const int            Attributes   =  0,
477 		            const char *const    HelpString   = NULL,
478 		            const int            HelpLevel    =  0 );
479 
480 		//! copy constructor
SettingDef(const SettingDef & def)481 		SettingDef( const SettingDef &def )
482 			: m_IDString    ( def.m_IDString ),
483 			  m_altIDString ( def.m_altIDString ),
484 			  m_Type        ( def.m_Type ),
485 			  m_Attributes  ( def.m_Attributes ),
486 			  m_minNumParams( def.m_minNumParams ),
487 			  m_maxNumParams( def.m_maxNumParams ),
488 			  m_HelpString  ( def.m_HelpString ),
489 			  m_HelpLevel   ( def.m_HelpLevel )
490 		{}
491 
492 		~SettingDef();
493 
494 
495 		const char    *m_IDString,      //! ID-string
496 		              *m_altIDString;   //! alternative ID-String
497 		Setting::type  m_Type;          //! storage type
498 		int            m_Attributes;    //! attributes
499 		int            m_minNumParams,  //! minimal number of parameters
500 		               m_maxNumParams;  //! maximal number of parameters
501 		const char    *m_HelpString;    //! string for help
502 		int            m_HelpLevel;
503 };
504 
505 /**********************************************************
506  * Small class for the mapping of string parameters to other values.
507  **********************************************************/
508 
509 //! class for the mapping of a string to another type
510 template <class T>
511 class SettingStringMap
512 {
513 	public :
514 
SettingStringMap(const char * const string=0,T value=0)515 		SettingStringMap( const char* const string = 0,
516 		                  T                 value  = 0  )
517 		: m_String(string),
518 		  m_Value(value) {}
519 
520 		const char* const  m_String;
521 		T                  m_Value;
522 };
523 
524 /**********************************************************
525  * ProgramSettings
526  **********************************************************/
527 
528 //! class for reading settings from command line and configuration files
529 
530 /*! This is the class, you work with, if you want read the settings from
531  *  the command line or a configuration file. <br>
532  *  Create an instance of SettingDataBase with the constructor (optionally
533  *  specifying the verbosity flag) and call readSettings, either the
534  *  version for data files or the one for the command line options. You
535  *  can use the same array of SettingDef objects for both versions of
536  *  readSettings. This allows to read settings either from the command
537  *  line or from a configuration file or from both. If you read the same
538  *  settings from the command line <b>and</b> you can specify by the
539  *  boolean parameter <code>overwrite</code>, if already set settings
540  *  should be overwritten. <br>
541  *  Both versions of readSettings are quite fault-tolerant. Even if there
542  *  are some not readable settings in the source, the functions print
543  *  out an error message, but continue reading setting as far as possible.
544  *  Only in case of really heavy errors the return value will reflect
545  *  this in a false. So by only checking the return value the calling
546  *  routine cannot decide if really <b>everything</b> was fine. But
547  *  readSettings take an optional parameter bool* pickySuccess. This
548  *  pointer is passed all through the whole process of reading and
549  *  interpreting settings and the variable refered by this pointer is
550  *  initialized with true and set to false, whenever a slight error
551  *  occures, even if this error does not harm the processing of the
552  *  functions and does not cause a false as return value. So by passing
553  *  this optional parameter one can still react on small faults.
554  *  <br><br>
555  *  <b>SYNTAX</b> of the configuration file:
556  *   - one setting per line
557  *   - everything after a '#' in a line is comment (including the '#')
558  *   - ONLY and ALL directive lines start with ">>"
559  *   - spaces are ignored, exept, if the separate parameters
560  *   - doublequotes to define a string containing spaces, commas or
561  *     hashes are recognised (see example)
562  *   - overlapping parameters for a setting are ignored (remove comment
563  *     in "family" line of the sample file
564  *   - a backslash, that is not in included in quoting, causes a line
565  *     break. There is no character inserted to separate the items
566  *     befor and after this backslash. So you can continue a string
567  *     over more that one line. For example (| symbolizes the left
568  *     boundary of the text file):
569  *     <br><br>
570  * \code
571  * |age = 10 20 \ this text is not recognized
572  * |30
573  * |height = 120 190\
574  * |230
575  * \endcode
576  *     <br>
577  *     This configuration file will cause: <br>
578  *       age[0] = 10, age[1] = 20, age[2] = 30
579  *       heigth[0] = 120, height[1] = 190230
580  *
581  *  <br><br>
582  *  <b>DIRECTIVES</b> <br>
583  *  The syntax of the configuration files provides directives, that
584  *  means key words, that influence the reading of the file dynamically.
585  *  All directives lines must start with the first two characters ">>".
586  *  Possible directives
587  *   - >>section_enter<< sectionname        <br>
588  *     >>section_leave<< sectionname        <br>
589  *       Enter and leave a section the reading is restricted to. For
590  *       setting and resetting use functions "restrictToSection" and
591  *       and "removeSection". If the SettingDataBase class is restricted
592  *       to a section "sectionname", only settings braced by these two
593  *       directives are read. \b Note that, if the reading is not
594  *       restricted, \b all settings will be read, including the ones
595  *       placed in sections!
596  *   - >>end<<    <br>
597  *       Reading stops immidiately. This directive might be usefull in
598  *       developing states to hide developing rubbish without the need
599  *       of repeated (un)commenting. Section restriction is stronger
600  *       than ">>end<<", i.e. if the class is restricted to a section
601  *       ">>end<<" is only respected, if placed inside this section.
602  *
603  *  <br><br>
604  *  <b>PARAMETER MAPPING</b> <br>
605  *  Often one wants a string parameter of a setting being mapped to
606  *  another type, e.g. a enum constant. This means alway the same code
607  *  of a strcmp inside a loop ... The functions mapStringParameter and
608  *  mapStringToIndex do exactly that.
609  *
610  *  <br><br>
611  *  <b>PATH EXPANSION</b> <br>
612  *  A setting of type STRING might represent a certain path in the file
613  *  system. Therefore it may contain start with the abreviation '~' for
614  *  the home path of the current user or for example "~homer" the home
615  *  path of user "homer". Using such a path inside your program to open
616  *  a file will not work, since the '~' or "~user" is expanded by the
617  *  shell, if you use it in the terminal, but there is no shell inside
618  *  your programm. You have to expand it manually in your program. class
619  *  SettingDataBase provides this expansion with its method expandPaths.
620  *
621  *  \br\br
622  *  \b FAQs
623  *   - <b>How can I specify a path setting containing spaces?</b>
624  *     OK, simply STRING settings containing spaces are easy to produce
625  *     by embracing them with '"'. But whenever we want to use this
626  *     string as file path, we get a problem. If we want to use the path
627  *     "/usr/Homer Simpson" in the shell, we must embrace it with '"',
628  *     but we already had to embrace it to define a string setting
629  *     containing a space. How can I insert the "-character in string
630  *     setting itself? With "\"". So in the config file this would have
631  *     to look like this:
632  *  \code
633  *  path_version1 = "/usr/Homer\\ Simpson"
634  *  \endcode
635  *     in commandline
636  *  \code
637  *  path_version1 = "/usr/Homer\\\\\ Simpson" # yes, 5 backslashes
638  *  \endcode
639  */
640 class SettingDataBase
641 {
642 	public:
643 
644 		//! constructor
645 		SettingDataBase( const bool verbose = true  );
646 		//! destructor
647 		~SettingDataBase();
648 
649 		void reset();
650 
651 		//! \name member read access
652 		//@{
653 	 	//! returns the first member of the settings list
getSettings() const654 		Setting* getSettings() const { return m_Settings; }
655 		//! returns a pointer to the setting with the passed ID string
656 		Setting* getSetting( const char* const IDString ) const;
657 		//! returns setting \c [i] in the list (who needs that?)
658 		Setting* getSetting( int index ) const;
659 		//! removes a setting
660 		void delSetting( const char* const IDString );
661 		//! returns the number of parameters of setting "IDString"
662 		int      getNumParameters( const char* const IDString ) const;
663 		//! returns a pointer to the name of the current restriction
getSection() const664 		char*    getSection() const { return m_activeSection; }
665 		//! returns true, if a restriction is currently active
isRestricted() const666 		bool     isRestricted() const { return m_activeSection != NULL; }
667 		//@}
668 
669 		//! \name member write access
670 		//@{
671 		//! sets one ore more values in a setting, that is present or not
672 
673 		/*! Mr. Anderson, welcome back, we missed you.
674 		 */
675 		template <class T>
676 		bool setValue( const SettingDef* const settingdef,
677 		               const char*       const idstring,
678 		               const T*          const values,
679 		               const int               index   = -1,
680 		               const int               nvalues = -1 );
681 		//@}
682 
683 		//! expands all paths from '~'
684 
685 		/*! Inspects all settings of type Setting::STRING, whether they
686 		 *  start with a '~'. Such a string is recognized as a path and
687 		 *  is tried to be expanded to a full path, no longer containing
688 		 *  the abreviation '~'. For the expansion of selected strings
689 		 *  use methods Setting::expandPathString and Setting::expandAllPathStrings
690 		 *  after a call of SettingDataBase::getSetting.
691 		 *  \return true, if all paths could be expanded, otherwise false
692 		 */
693 		bool expandPaths();
694 
695 		//! translate a setting string into another parameter
696 
697 		/*! Often one wants a string parameter of a setting being mapped
698 		 *  to another type, e.g. a enum constant. This means alway the
699 		 *  same code of a strcmp inside a loop ... The function
700 		 *  SettingDataBase::mapStringSetting does exactly that. Code
701 		 *  example:
702 		 *
703 		 *  \code
704 		 *  SettingStringMap<int> map[] =
705 		 *  {
706 		 *      SettingStringMap<int>( "homer",  0 ),
707 		 *      SettingStringMap<int>( "lisa",   6 ),
708 		 *      SettingStringMap<int>( "bart",  -6 ),
709 		 *      SettingStringMap<int>( )
710 		 *  };
711 		 *
712 		 *  int familyvalue;
713 		 *  settings.mapStringParameter( "family", map,
714 		 *                               familyvalue, 1, true );
715 		 *  \endcode
716 		 *
717 		 * Such a mapping often could be reduced to simply returning
718 		 * the index of an array, the possible strings are contained
719 		 * in (see example). To prevent the creation of SettingStringMap
720 		 * arrays, that contain in the m_Value entry simply the index
721 		 * in the array, this mapping is supported explicitly by the
722 		 * function SettingDataBase::mapStringToIndex.
723 		 *
724 		 *  \param ID the ID string of the setting you want to map
725 		 *  \param map an array of Type SettingStringMap<T>, that contains
726 		 *         the mapping of type STRING to type T. Like the arrays
727 		 *         of SettingDef this array must end with one element
728 		 *         created with the standard constructor (to get
729 		 *         m_String == NULL).
730 		 *  \param value address of a variable of type T, the mapped value
731 		 *         should be stored in
732 		 *  \param iparameter index of the parameter string of the setting
733 		 *         that should be mapped. In most cases these settings
734 		 *         just take one parameter (e.g. runningmode = server),
735 		 *         where iparameter == 0. Therefore the default value of
736 		 *         iparameter is 0.
737 		 *  \param verbose print some informations, if mapping did not
738 		 *         succeed.
739 		 *  \return true, if "value" constains valid data after the call
740 		 */
741 		template <class T>
742 		bool mapStringParameter( const char*                const ID,
743 		                         const SettingStringMap<T>* const map,
744 		                               T&   value,
745 		                         const int  iparameter = 0,
746 		                         const bool verbose    = false ) const;
747 
748 		//! maps a string to its index in an array of strings
749 
750 		/*! maps a string to its index in an array of strings <br>
751 		 *  Code example:
752 		 *
753 		 *  \code
754 		 *  enum { HOMER = 0, MARGE, MAGGY, BART,
755 		 *         LISA, FAMILY_SIZE );
756 		 *  const char* enumStrings[FAMILY_SIZE + 1] = {
757 		 *      "HOMER", "MARGE", "MAGGY",
758 		 *      "BART",  "LISA",   NULL
759 		 *  };
760 		 *  settings.mapStringToIndex( "athome", enumStrings, 0, true );
761 		 *  \endcode
762 		 *
763 		 *  \param ID the ID string of the setting you want to map
764 		 *  \param SettingStrings array containing the possible strings.
765 		 *         This array <b>must</b> be terminated with a NULL
766 		 *         string.
767 		 *  \param stringIndex index of the parameter string of the
768 		 *         setting that should be mapped. In most cases these
769 		 *         settings just take one parameter (e.g. runningmode
770 		 *         = server), where iparameter == 0.
771 		 *  \param verbose print some informations, if mapping did not
772 		 *         succeed.
773 		 *  \param stringIndex of SettingStrings, where the string could
774 		 *         be found
775 		 */
776 		int  mapStringToIndex( const char*  const ID,
777 		                       const char** const SettingStrings,
778 		                       const int          stringIndex = 0,
779 		                       const bool         verbose     = false ) const;
780 
781 		//! read settings from a file
782 
783 		/*! Read settings from an configuration file
784 		 *  \param settingdef array of SettingDef defining the accepted
785 		 *         settings. The last element in this array <b>must</b> be
786 		 *         one with IDString == NULL, i.e. simply created with
787 		 *         the constructor using only default arguments.
788 		 *  \param FileName name of the configuration file
789 		 *  \param overwrite if true, all in the class already present
790 		 *         settings are overwritten, if one with the same
791 		 *         ID-String is read. This flag allows for example a
792 		 *         hierarchy of configuration sources, that are read
793 		 *         one after the other.
794 		 *  \param pickySuccess: pointer to a boolean, where the success
795 		 *         is indicated in, in a pickier way
796 		 *  \return true, if all settings could be read
797 		 */
798 		bool readSettings( const SettingDef* const settingdef,
799 		                   const char*       const FileName,
800 		                   const bool              overwrite    = true,
801 		                         bool*       const pickySuccess = NULL );
802 
803 		//! read settings from the command line passed as int and char**
804 		/*!
805 		 *  \param settingdef array of SettingDef defining the accepted
806 		 *         settings
807 		 *  \param CommandLine pointer to the command line, just the
808 		 *         first parameter passed to the main function
809 		 *  \param nargs number of arguments in the command line, just
810 		 *         the second parameter passed to the main function
811 		 *  \param overwrite if true, all in the class already present
812 		 *         settings are overwritten, if one with the same
813 		 *         ID-String is read. This flag allows for example a
814 		 *         hierarchy of configuration sources, that are read
815 		 *         one after the other.
816 		 *  \param pickySuccess pointer to a boolean, where the success
817 		 *         is indicated in, in a pickier way
818 		 *  \param expectDashesInCommandLine : if true, additionally,
819 		 *         not in m_IDString present, a "-" is demanded for a
820 		 *         setting in the command line.
821 		 */
822 		bool readSettings( const SettingDef* const settingdef,
823 		                   const char**      const CommandLine,
824 		                   const int               nargs,
825 		                   const bool              overwrite = true,
826 		                   const bool              expectDashesInCommandLine = true,
827 		                         bool*       const pickySuccess = NULL );
828 
829 		//! print out help using the definitions in settingdef
830 
831 		/*! This function generates a standard help output for all
832 		 *  settings in the array at settingdef. This array must be
833 		 *  terminated by an empty SettingDef object (generated with
834 		 *  with the standard constructor). For each setting the ID
835 		 *  strings are printed, the storage type, limits for number of
836 		 *  parameters, and the help string, if specified
837 		 *  (-> SettingDef::SettingDef).
838 		 *  \param settingdef array containing the setting definitions.
839 		 *         The last definition must be an empty object created
840 		 *         using the default constructor of class SettingDef.
841 		 *  \param lowerHelpLevel optional lowest level of the printed
842 		 *         help. Only help strings of SettingDef objects with
843 		 *         member SettingDef::m_HelpLevel >= lowerHelpLevel
844 		 *         are printed.
845 		 *  \param upperHelpLevel optional highes level of the printed
846 		 *         help. If upperHelpLevel >= 0, only help strings of
847 		 *         SettingDef objects with SettingDef::m_HelpLevel <=
848 		 *         helpLevel are printed. If helpLevel < 0 (default),
849 		 *         it is ignored and \b all help strings are printed.
850 		 */
851 		void printHelp( const SettingDef* const settingdef,
852 		                const int               lowerHelpLevel = -1,
853 		                const int               upperHelpLevel = -1 ) const;
854 
855 		//! finally check the results of readSettings
856 		bool finalCheck( const SettingDef* const settingdef ) const;
857 
858 		//! \name reading restrictions
859 		//@{
860 		//! restricst reading of the config file to the passed section
861 
862 		/*! \param section Zero-terminated name of the section the
863 		 *         reading of the configuration file should be restricted
864 		 *         to (see also <b>DIRECTIVES</b> in documentation of
865 		 *         class SettingDataBase). If NULL is passed, the current
866 		 *         restriction will be removed (-> removeSection)
867 		 *  \return true, if the registration of the restriction was
868 		 *          successfull, false, otherwise
869 		 */
870 		bool restrictToSection( const char* const section = NULL );
871 
872 		//! Removes the restriction to a section.
873 
874 		/*! In the implementation this is simply a call of
875 		 *  restrictToSection(NULL).
876 		 */
removeSection()877 		void removeSection() { restrictToSection(NULL); }
878 		//@}
879 
880      // static function
881 		static void printLineError( const char* const FunctionName,
882 		                            const char* const FileName = NULL,
883 		                            const int         LineNumber = -1 );
884 
885 	protected:
886 
887 		// reads one line of a file into the buffer
888 		int readLine(       FILE*       file,
889 		                    char**      line,
890 		              const char* const FileName,
891 		              const int         LineNumber );
892 		// prescan line
893 		int prescanLine( const char* const line,
894 		                 const int         length,
895 		                 const char* const FileName,
896 		                 const int         LineNumber );
897 		// process the raw line read from a file
898 		int findMatchingSetting(       char*             line,
899 		                         const int               linelength,
900 		                         const SettingDef *const settingdefs,
901 		                         const char       *const FileName     = NULL,
902 		                         const int               LineNumber   = -1,
903 		                         const bool              quiet        = false,
904 		                               bool       *const pickySuccess = NULL );
905 		// find the next candidate for a setting in the command line
906 		int identifySettingInCommandLine( const SettingDef* const settingdef,
907 		                                  const char**      const CommandLine,
908 		                                  const int               i,
909 		                                  const bool              expectDashesInCommandLine,
910 		                                  const bool              quiet = true,
911 		                                        bool*       const pickySuccess = NULL );
912 		// build a line like in a config file from the command line
913 		int buildConfigLineFromCommandLine( const char** const CommandLine,
914 		                                    const int          istart,
915 		                                    const int          iend,
916 		                                          char**       line,
917 		                                    const bool         expectDashesInCommandLine );
918 		// integrate the new setting in the list at "m_Settings"
919 		bool integrateSetting(       Setting *const setting,
920 		                       const bool           overwrite,
921 		                             bool    *const pickySuccess = NULL );
922 		// enlarge buffer by one granularity
923 		char* enlargeLineBuffer( char* line );
924 
925 	 /////////////////
926 	 // data members
927 		bool     m_verbose;
928 		Setting *m_Settings;
929 		char    *m_activeSection;
930 		bool     m_insideSection;
931 		// current Length of the line buffer
932 		int      m_currentLineLength;
933 
934 		// constants for direcitves in config file
935 		enum { DIRECTIVE_SECTION_ENTER = 0,
936 		       DIRECTIVE_SECTION_LEAVE,
937 		       DIRECTIVE_END,
938 		       N_DIRECTIVES };
939 		// static member keeping the possible directives
940 		static const char* const m_directives[N_DIRECTIVES];
941 };
942 
943 /**********************************************************
944  * Template implementations
945  **********************************************************/
946 
947 /**********************************************************
948  * Automises the mapping from a string parameter to another type T
949  *
950  * ID      : IDString of the setting
951  * map     : array of type SettingStringMap, of which the last element
952  *           must be created without arguments (compare class Settingdef).
953  * value   : address of variable the mapped value should be written to
954  * verbose : if true, "value" contains a valid value
955  **********************************************************/
956 
957 #ifndef  FUNCTION_ERROR
958 #ifdef  SUPPRESS_COLORED_OUTPUT
959 #define  FUNCTION_ERROR(fns)  fns << "\n >> "
960 #else
961 #define  FUNCTION_ERROR(fns)  "\033[31m" << fns << ":\033[0m\n >> "
962 #endif
963 #define  FUNCTION_ERROR_WAS_NOT_DEFINED
964 #endif
965 
966 #ifndef  CALLED_FROM
967 #ifdef  SUPPRESS_COLORED_OUTPUT
968 #define  CALLED_FROM(fns)     "    called from " << fns << "\n"
969 #else
970 #define  CALLED_FROM(fns)     "    called from \033[36m" << fns << "\033[0m\n"
971 #endif
972 #define  CALLED_FROM_WAS_NOT_DEFINED
973 #endif
974 
975 #ifndef  NEW
976 #define  NEW  new(std::nothrow)
977 #define  NEW_WAS_NOT_DEFINED
978 #endif
979 
980 /**********************************************************/
981 
982 template <class T>
983 bool SettingDataBase::
mapStringParameter(const char * const ID,const SettingStringMap<T> * const map,T & value,const int iparameter,bool verbose) const984 mapStringParameter( const char*                const ID,
985                     const SettingStringMap<T>* const map,
986                     T&                               value,
987                     const int                        iparameter,
988                     bool                             verbose ) const
989 {
990 	using namespace std;
991 	const char fn[]   = "SettingDataBase::translateStringSetting";
992 	      int  length = 0;
993 
994 	// create an array, that contains only the strings
995 	while( map[length++].m_String != NULL );
996 	char** const stringmap = NEW char* [length];
997 	if( stringmap == NULL ) {
998 		cerr << FUNCTION_ERROR( fn ) << "could not get enough memory\n";
999 		return false;
1000 	}
1001 	for( int i = 0; i < length; i++ )  stringmap[i] = (char*)map[i].m_String;
1002 
1003 	int index = mapStringToIndex( ID, (const char** const)stringmap,
1004 	                              iparameter, verbose );
1005 	if( index < 0 ) {
1006 		if( verbose ) cerr << CALLED_FROM( fn );
1007 	} else {
1008 		value = map[index].m_Value;
1009 	}
1010 
1011 	delete [] stringmap;
1012 	return index >= 0;
1013 }
1014 
1015 /**********************************************************/
1016 
1017 template <class T>
1018 bool SettingDataBase::
setValue(const SettingDef * const settingdef,const char * const idstring,const T * const values,const int index,const int nvalues)1019 setValue( const SettingDef* const settingdef,
1020           const char*       const idstring,
1021           const T*          const values,
1022           const int               index,
1023           const int               nvalues )
1024 {
1025 	using namespace std;
1026 	const char fn[] = "SettingDataBase::setValue";
1027 
1028 	Setting *setting = getSetting( idstring );
1029 	const bool settingPresent = setting != NULL;
1030 
1031 	if( !settingPresent ) {
1032 		for( int i = 0; settingdef[i].m_IDString; i++ ) {
1033 			if( !strcmp(idstring, settingdef[i].m_IDString) ) {
1034 				setting = NEW Setting( settingdef[i] );
1035 				break;
1036 			}
1037 		}
1038 		if( !setting ) {
1039 			cerr << FUNCTION_ERROR(fn)<<"could not create Setting object\n";
1040 			return false;
1041 		}
1042 	}
1043 
1044 	if( setting->setValue(values, index, nvalues) ) {
1045 		if( !settingPresent ) {
1046 			if( !integrateSetting(setting, false, NULL) ) {
1047 				cerr << CALLED_FROM(fn);
1048 				if( !settingPresent )  delete setting;
1049 				return false;
1050 			}
1051 		}
1052 	} else {
1053 		cerr << CALLED_FROM(fn);
1054 		return false;
1055 	}
1056 
1057 	return true;
1058 }
1059 
1060 /**********************************************************/
1061 
1062 #ifdef  FUNCTION_ERROR_WAS_NOT_DEFINED
1063 #undef  FUNCTION_ERROR
1064 #endif
1065 
1066 #ifdef  CALLED_FROM_WAS_NOT_DEFINED
1067 #undef  CALLED_FROM
1068 #endif
1069 
1070 #ifdef  NEW_WAS_NOT_DEFINED
1071 #undef  NEW
1072 #endif
1073 
1074 /**********************************************************/
1075 
1076 #endif // SETTINGS_HPP
1077