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