/** @file
$ Id $
$ Date $
$ Author $
@ copy & copy ; Marc W & auml ; ckerlin
@ license LGPL , see file < a href = " license.html " > COPYING < / a >
$ Log $
Revision 1.3 2005 / 11 / 29 12 : 39 : 42 marc
make it compilable with gcc 4.0 .2 and newer doxygen
Revision 1.2 2005 / 01 / 28 07 : 49 : 32 marc
Save configuration using file . hpp
Revision 1.1 2005 / 01 / 07 00 : 31 : 38 marc
initial version
*/
# ifndef __MRW_CONFIGFILE_HPP__
# define __MRW_CONFIGFILE_HPP__
# include <string>
# include <map>
namespace mrw {
/** @defgroup config Configuration File Handler
@ pre \ # include < mrw / configfile . hpp >
Read configuration parameters from a file .
@ section configSyntax Configuration File Syntax
@ subsection configSyntaxSection Sections : [ Section ]
Syntax ( regular expression ) :
< code > \ \ [ ( [ ^ [ = # ] * ) \ \ ] < / code >
A configuration file consists of none or more sections . The
sections start with tag < code > [ Section & nbsp ; Name ] < / code > , where
< code > Section & nbsp ; Name < / code > is the name of the section .
The default section has an empty name .
@ subsection configSyntaxVariables Variables : name = value
Syntax ( regular expression ) :
< code > ^ [ ^ [ \ \ n \ \ t & nbsp ; ] * ( [ ^ [ = # \ \ n ] ] + ) [ \ \ n \ \ t & nbsp ; ] * = [ \ \ n \ \ t & nbsp ; ] * ( [ ^ [ = # ] ] * ) [ \ \ n \ \ t & nbsp ; ] * < / code >
or
< code > ^ [ ^ [ \ \ n \ \ t & nbsp ; ] * ( [ ^ [ = # \ \ n ] ] + ) [ \ \ n \ \ t & nbsp ; ] * = [ \ \ n \ \ t & nbsp ; ] * ( " [^ " ] ] * " )[ \\ n \\ t ]*</code>
or
< code > ^ [ ^ [ \ \ n \ \ t & nbsp ; ] * ( [ ^ [ = # \ \ n ] ] + ) [ \ \ n \ \ t & nbsp ; ] * = [ \ \ n \ \ t & nbsp ; ] * ( ' [ ^ ' ] ] * ' ) [ \ \ n \ \ t & nbsp ; ] * < / code >
In the section , there are variable definitions in the form of
< code > name = value < / code > , where < code > name < / code > is the name of
the variable and < code > value < / code > is it ' s contents . White
spaces at the begin or end of the name and the value are
stripped . Whitespaces inside a name or value remain
unchanged . Please note that a variable name starts at the begoin
of line and ends before the equal sign , while the contents
starts after the equal sign and ends before the next token
starts . If you need a token inside a value , or a variable must
start or end with white spaces , you can enclose it in either
< code > " </code> or <code>'</code> quotes. If a value is quoted
and the quoting is not terminated , then the contents is taken up
to the end of file . Please note that the same quote must not
occur inside the value . All possible tokens are : < code > [ < / code > ,
< code > # < / code > and < code > = < / code > . All other characters are not
treated in a special way .
@ subsection configSyntaxComments Comments : #
Comments start with < code > # < / code > and end at the end of the
line .
@ subsection configSyntaxSpecial Special Characters \ \ n , [ , ] , # , = , " , '
I have chosen a syntax that is downwards compatible with the
common Unix configuration file syntax , but with a grammar as
loose as possible and with as few restrictions as
possible . Special characters have only special meanings in
certain contexts . In other contexts , they can be used with no
restrictions .
The following characters are treated in a special way :
- < code > [ < / code > and < code > ] < / code > enclose a section name . A
section name may contain any arbitrary character , including new
line and white spaces , except a < code > ] < / code > .
- < code > \ \ n < / code > ( begin of line or file ) and < code > = < / code >
enclose a variable name . In other circumstances ( except in
comments ) , a new line has no special meaning . A < code > = < / code >
inside a value is allowed only if the value is quoted . A
variable name may contain any arbitrary character , including
white spaces , except a new line or < code > = < / code > .
- < code > = < / code > and one out of < code > [ < / code > , < code > # < / code >
and the begin of a line that contains < code > = < / code > enclose a
variable contents if the contents is not quoted . A variable ' s
contents may contain any arbitrary character , including new line
and white spaces , except a < code > ] < / code > , < code > # < / code > or the
begin of a line that contains < code > = < / code > .
- < code > " </code> or <code>'</code> may enclose a variables
contents . These characters have only a special meaning , if a
variable ' s contents starts with one of them . Then the variable ' s
contents may contain any arbitrary character , except the same
quote itself , which marks the end of the contents .
- < code > # < / code > and < code > \ \ n < / code > ( end of line or file )
enclose a comment . A variable name may contain any arbitrary
character , including white spaces , except a new line .
@ subsection configSyntaxRestrictions Restrictions
@ attention Due to the actual syntax , it is impossible that a
variable ' s contents contains the following combination :
< code > " </code> @b and <code>'</code> @b and one out of
< code > [ < / code > , < code > = < / code > and < code > # < / code >
@ attention If a variable ' s contents contains heading or trailing
white spaces or one out of < code > [ < / code > , < code > = < / code > and
< code > # < / code > , then it must be quoted either with
< code > " </code> or with <code>'</code>.
@ section configFile Example File
@ subsection configFileSamp1 Nice Example
@ verbatim
[ Section 1 ]
name1 = value1
name2 = value2
[ Section 2 ]
name1 = value1
name2 = value2
@ endverbatim
@ subsection configFileSamp2 Enhanced Example
@ verbatim
# this is in the global section, named ""
this is a variable name = this is a variable contents
[ Global ] # start of section " Global "
multi line text = this is a text
that is longer than one
line !
1234 = " var=17 " # if you need an equal inside a text , enclose it
# either in " or in '
@ endverbatim
@ section configFileCode Code Sample
@ code
try {
const char * home ( getenv ( " HOME " ) ) ;
mrw : : ConfigFileReader config ( home ? home + " /.myprogram " : " .myprogram " ) ;
std : : string userName ( config ( " User Info " , " Name " ) ) ;
} catch ( . . . ) { // file read or evaluation error
}
@ endcode
*/
//@{
//============================================================================
/** @brief Parse configuration file and access the contents.
Parse a configuration file that consists of sections containing
variables with values . This class does not store anything in the
file , even if variables have changed .
@ see If you want % to save changes % to the file , use @ ref ConfigFileWriter .
@ see For details , see @ ref configSyntax .
*/
class ConfigFileReader {
//............................................................... typedefs
public :
//------------------------------------------------------------------ Value
/// A configuration file value.
class Value {
//............................................................ methods
public :
/** @brief Get the value string.
Get the value string . Trailing and leading white spaces have
been truncated . If the variable was quoted , quotes have been
removed .
@ return the variable ' s value as string
*/
operator const std : : string & ( ) const throw ( ) {
return value ;
}
/** @brief Get the value string.
Alternative access to the variable ' s contents . Trailing and
leading white spaces have been truncated . If the variable
was quoted , quotes have been removed .
@ return the variable ' s value as string
*/
const std : : string & operator ( ) ( ) const throw ( ) {
return value ;
}
/** @brief Assign a new value.
Assign a new value . The new value is lost if you use the
ConfigFileReader , but it is stored in the destructor , if you
use the ConfigFileWriter instead .
*/
Value & operator = ( const std : : string & newValue ) throw ( ) {
changed = true ;
value = newValue ;
return * this ;
}
/// Compare the value to a string.
bool operator = = ( const std : : string & o ) const throw ( ) {
return o = = value ;
}
/// Compare a string to the value.
friend bool operator = = ( const std : : string & o , const Value & v ) throw ( ) {
return v = = o ;
}
//............................................................ methods
protected :
friend class ConfigFileReader ;
friend class ConfigFileWriter ;
Value ( const std : : string & v , std : : string : : size_type s ,
std : : string : : size_type e ) throw ( ) :
value ( v ) , changed ( false ) , start ( s ) , end ( e ) { }
Value ( const std : : string & v ) throw ( ) :
value ( v ) , changed ( true ) , start ( 0 ) , end ( 0 ) {
}
//.......................................................... variables
protected :
std : : string value ;
bool changed ;
std : : string : : size_type start ;
std : : string : : size_type end ;
//............................................................ methods
private :
Value ( ) ; // not implemented, must be initialized
Value & operator = ( const Value & ) ; // not implemented, no assignment
} ;
//------------------------------------------------------------------------
//................................................................ methods
public :
/** @brief Uninitialized construction.
Uninitialized construction . Use this constructor , if you
want to call @ ref load ( ) later .
*/
ConfigFileReader ( ) throw ( ) { }
/** @brief Parse a file at construction.
This should be the normal case : load and parse the file at
construction .
@ param filename the name of the file to read
@ throw mrw : : invalid_argument
- if the file does not exist
- if reading the file fails
- if there is an unterminated section name in the file
- if there is an empty variable name in the file
*/
ConfigFileReader ( const std : : string & filename ) throw ( std : : exception ) :
_filename ( filename ) {
reload ( ) ;
}
/// Copy construct from other ConfigFileReader.
ConfigFileReader ( const ConfigFileReader & o ) throw ( ) :
_filename ( o . _filename ) , _values ( o . _values ) {
}
virtual ~ ConfigFileReader ( ) throw ( ) { }
/// Copy from other ConfigFileReader.
ConfigFileReader & operator = ( const ConfigFileReader & o ) {
_filename = o . _filename ;
_values = o . _values ;
return * this ;
}
/** @brief Access a value read from the file.
Access a value read from the file .
@ param section the section in the configuration file
@ param name the variable name
@ param default_ the default value that is set , if the variable is
not configured
@ return the Value you are looking for
*/
Value & operator ( ) ( const std : : string & section , const std : : string & name ,
const std : : string & default_ )
throw ( std : : bad_exception ) ;
/** @brief Load and parse a new file.
Load and parse a new file .
@ param filename the name of the file to load
@ throw mrw : : invalid_argument
- if the file does not exist
- if reading the file fails
- if there is an unterminated section name in the file
- if there is an empty variable name in the file
*/
ConfigFileReader & load ( const std : : string & filename ) throw ( std : : exception ) ;
/** @brief Reload the last file again.
Reload the last file again .
@ throw mrw : : invalid_argument
- if the file does not exist
- if reading the file fails
- if there is an unterminated section name in the file
- if there is an empty variable name in the file
*/
virtual ConfigFileReader & reload ( ) throw ( std : : exception ) ;
//............................................................... typedefs
protected :
typedef std : : map < std : : string , std : : map < std : : string , Value > > Values ;
typedef std : : map < std : : string , std : : string : : size_type > Sections ;
//.............................................................. variables
protected :
std : : string _filename ;
Values _values ;
Sections _sections ;
//................................................................ methods
protected :
ConfigFileReader & parse ( const std : : string & file ) throw ( std : : exception ) ;
std : : string parseSection ( const std : : string & file ,
std : : string : : size_type & pos )
const throw ( std : : exception ) ;
Values : : mapped_type : : value_type parseValue ( const std : : string & file ,
std : : string : : size_type & pos )
const throw ( std : : exception ) ;
std : : string parseComment ( const std : : string & file ,
std : : string : : size_type & pos )
const throw ( std : : bad_exception ) ;
} ;
//============================================================================
/** @brief Parse configuration file and offer read / write
access to the contents .
Parse a configuration file that consists of sections containing
variables with values . This class stores the file in the destructor
if variables have changed .
@ see If you want % to leave the file untouched , use @ ref ConfigFileReader .
@ see For details , see @ ref configSyntax .
*/
class ConfigFileWriter : virtual public ConfigFileReader {
//................................................................ methods
public :
/** @brief Uninitialized construction.
@ copydoc ConfigFileReader ( )
*/
ConfigFileWriter ( ) throw ( ) { }
/** @brief Parse a file at construction.
@ copydoc ConfigFileReader ( const std : : string & )
*/
ConfigFileWriter ( const std : : string & filename ) throw ( std : : exception ) {
load ( filename ) ;
}
/// Copy construct from other ConfigFileWriter.
ConfigFileWriter ( const ConfigFileWriter & o ) throw ( ) :
ConfigFileReader ( o ) {
}
/// The file is stored at destruction.
virtual ~ ConfigFileWriter ( ) throw ( ) {
save ( ) ;
}
/// Copy from other ConfigFileWriter.
ConfigFileWriter & operator = ( const ConfigFileWriter & o ) {
ConfigFileReader : : operator = ( o ) ;
return * this ;
}
// GNU g++ prior to 3.4 does not implement covariant returns
# if __GNUC__ == 3 && __GNUC_MINOR__ < 4
/** @brief Reload the last file again.
@ copydoc mrw : : ConfigFileReader : : reload ( )
@ bug Bug in GNU g + + compiler :
return type should be < code > ConfigFileWriter & < / code > ,
but the compiler sais : < br >
< code > . . / mrw / configfile . hpp : 295 :
sorry , unimplemented : adjusting pointers for
covariant returns < / code > < br >
The problem is fixed since GNU g + + 3.4 , so the signature
will be changed as soon as you upgrade to the new compiler
version .
*/
virtual ConfigFileReader & reload ( ) throw ( std : : exception ) ;
# else
/** @brief Reload the last file again.
@ copydoc mrw : : ConfigFileReader : : reload ( )
*/
virtual ConfigFileWriter & reload ( ) throw ( std : : exception ) ;
# endif
/** @brief Saves changes back to the file.
All changed parameters are stored in the configuration
file . The rest of the file is left untouched .
*/
ConfigFileWriter & save ( ) throw ( std : : exception ) ;
//.............................................................. variables
protected :
std : : string _file ;
} ;
//@}
}
# endif